Introduction
A reusable playbook should consume environment data instead of hardcoding separate development and production answers. Inventory selection, variable scope and target facts determine what each node receives.
You will use two inventories selecting different SSH nodes, store group and host data beside each source, and render settings with one playbook. You will also apply a temporary extra-variable override and restore a stable baseline.
Separate Inventory Data by Environment
In this step, you will give development and production separate inventory sources and group variable values.
Enter the project:
cd /home/labex/project
The lab supplies two small inventories. Read each before running automation:
cat inventories/dev/hosts.ini
cat inventories/prod/hosts.ini
The dev app group contains web1; the prod app group contains web2. Their addresses and SSH settings are prepared. These are genuinely distinct nodes, not two names for localhost.
-i selects an inventory source for this invocation, overriding the project's default inventory. Preview each environment separately:
ansible app -i inventories/dev/hosts.ini --list-hosts
ansible app -i inventories/prod/hosts.ini --list-hosts
Expect web1 for dev and web2 for prod. Do not combine these two sources to simulate isolated environments: sources can merge, and the same inventory host name still identifies one host. Keep each environment's invocation explicit.
A variable is a named value consumed by automation. A YAML mapping uses name: value lines. Ansible loads group_vars/app.yml beside an inventory for its app group. Create different data for the same group name in each environment:
cat > inventories/dev/group_vars/app.yml <<'EOF'
environment_label: development
release_channel: preview
EOF
cat > inventories/prod/group_vars/app.yml <<'EOF'
environment_label: production
release_channel: stable
EOF
Inspect the resolved inventory data. --list returns JSON, including a _meta.hostvars object for host values:
ansible-inventory -i inventories/dev/hosts.ini --list
ansible-inventory -i inventories/prod/hosts.ini --list
The dev host should resolve development/preview; the prod host production/stable. A variable file has value only when the intended inventory and group actually load it.
Consume Host Variables and Real Facts
In this step, you will override one host's group value and render environment data into actual managed settings.
A file under host_vars is named for an inventory host. For the same host and key, its host variable overrides the group variable used here. Give the development canary a host-specific release channel:
cat > inventories/dev/host_vars/web1.yml <<'EOF'
release_channel: canary
EOF
Development still has environment_label development, but web1 now resolves release_channel canary. Production remains stable. Confirm the dev resolution:
ansible-inventory -i inventories/dev/hosts.ini --list
Write one reusable playbook for either inventory:
cat > deploy-info.yml <<'EOF'
- name: Configure environment-specific application settings
hosts: app
gather_facts: true
vars:
banner_prefix: Managed environment
tasks:
- name: Write settings from variables and facts
ansible.builtin.copy:
dest: /home/automation/app-settings.conf
content: |
[application]
environment={{ environment_label }}
channel={{ release_channel }}
host={{ inventory_hostname }}
distribution={{ ansible_facts.distribution }}
label={{ banner_prefix }}
mode: "0640"
EOF
vars defines a play-level value, banner_prefix. Within the copy content, {{ variable_name }} inserts a value at execution time. This is basic value substitution; a later lab will manage standalone Jinja templates.
The YAML | keeps the following indented content as multiline text, including its final newline. inventory_hostname is the current inventory name. With gather_facts true, Ansible collects target data before the tasks; ansible_facts.distribution supplies the real operating-system distribution. It is target data, not a fact about the controller.
Check syntax, then run the same playbook separately for each environment:
ansible-playbook -i inventories/dev/hosts.ini deploy-info.yml --syntax-check
ansible-playbook -i inventories/dev/hosts.ini deploy-info.yml
ansible-playbook -i inventories/prod/hosts.ini deploy-info.yml
Read both actual files through their respective inventories:
ansible app -i inventories/dev/hosts.ini -m ansible.builtin.command -a 'cat /home/automation/app-settings.conf'
ansible app -i inventories/prod/hosts.ini -m ansible.builtin.command -a 'cat /home/automation/app-settings.conf'
Development should contain environment=development, channel=canary and host=web1. Production should contain environment=production, channel=stable and host=web2. Both should contain distribution=Ubuntu and label=Managed environment. The shared playbook produced different target content by consuming different data.
Override One Invocation and Restore the Baseline
In this step, you will use an extra variable without editing the saved inventory data, then return to a converged baseline.
-e supplies an extra variable for this execution. It overrides the group and host values demonstrated here. Temporarily select a hotfix channel on development only:
ansible-playbook -i inventories/dev/hosts.ini deploy-info.yml -e release_channel=hotfix
Read the resulting dev configuration:
ansible app -i inventories/dev/hosts.ini -m ansible.builtin.command -a 'cat /home/automation/app-settings.conf'
Its channel should be hotfix. The source group_vars and host_vars files were not edited by -e, and production was not selected.
Run without the extra variable to restore web1's saved canary channel:
ansible-playbook -i inventories/dev/hosts.ini deploy-info.yml
Now preview both baseline environments:
ansible-playbook -i inventories/dev/hosts.ini deploy-info.yml --check
ansible-playbook -i inventories/prod/hosts.ini deploy-info.yml --check
Each selected host should report zero pending changes and no failures/unreachable results. The limited precedence relationships practiced here are group data, a host override and an extra-variable override. More complex precedence rules are outside this exercise.
Summary
You selected distinct environment inventories, loaded group data, applied a host-specific override, and rendered real target facts with a shared playbook. A temporary extra variable changed one invocation, and subsequent baseline runs returned both environments to stable declared settings.


