Introduction
Inventories describe the hosts Ansible can manage and the groups those hosts belong to. Good inventory structure lets you target hosts clearly and attach variables at the right scope.
In this lab, you will build a static inventory with host aliases, groups, child groups, group variables, host variables, and a short playbook that proves variables resolve as expected.
Create an Inventory Project
In this step, you will create the project directories used for inventory practice. This lab uses local host aliases so you can learn inventory behavior safely inside one RHEL 10 VM.
Create and enter the project.
cd /home/labex/project
mkdir -p rhce-ansible/{inventory/group_vars,inventory/host_vars,playbooks,reports}
cd rhce-ansible
Create ansible.cfg so Ansible uses your inventory by default.
cat > ansible.cfg <<'EOF'
[defaults]
inventory = ./inventory/hosts
retry_files_enabled = False
interpreter_python = auto_silent
EOF
This lab places variable files under inventory/group_vars and inventory/host_vars. That layout keeps inventory data together and makes it clear that the variables belong to the hosts defined in inventory/hosts.
Define Hosts, Groups, and Children
In this step, you will create a static inventory. Each host alias uses ansible_connection=local, so the aliases behave like managed hosts while commands still run on this VM. Child groups let one group contain other groups.
Before running commands in this step, return to the Ansible project directory. This keeps the commands stable even when LabEx runs each step separately.
cd /home/labex/project/rhce-ansible
Create inventory/hosts.
cat > inventory/hosts <<'EOF'
[web]
web1 ansible_connection=local
web2 ansible_connection=local
[db]
db1 ansible_connection=local
[prod:children]
web
db
EOF
Inspect the group graph.
ansible-inventory --graph
The web and db sections define ordinary groups. The [prod:children] section defines a parent group, so the graph should show prod containing both groups and all three host aliases.
Add Group and Host Variables
In this step, you will attach variables to groups and individual hosts. A group variable applies to all hosts in that group. A host variable overrides or supplements values for one host.
Before running commands in this step, return to the Ansible project directory. This keeps the commands stable even when LabEx runs each step separately.
cd /home/labex/project/rhce-ansible
Create variables for web hosts.
cat > inventory/group_vars/web.yml <<'EOF'
service_role: web
service_port: 8080
EOF
These values apply to every host in the web group. In this inventory, both web1 and web2 should inherit the web role and port.
Create variables for database hosts.
cat > inventory/group_vars/db.yml <<'EOF'
service_role: database
service_port: 5432
EOF
The database group gets different values because database hosts usually need different service settings. Separating group files avoids putting conditionals into every playbook task.
Create a host-specific variable for web2.
cat > inventory/host_vars/web2.yml <<'EOF'
rack: rack-b
EOF
Host variables are the narrowest scope used in this lab. Only web2 should have the rack value, which makes it easy to see the difference between group-wide and host-specific data.
Inspect Resolved Host Variables
In this step, you will ask Ansible to show the resolved variables for selected hosts. This is a safe way to debug inventory before writing playbooks.
Before running commands in this step, return to the Ansible project directory. This keeps the commands stable even when LabEx runs each step separately.
cd /home/labex/project/rhce-ansible
Inspect web1.
ansible-inventory --host web1
web1 should show the web role and port, but no rack variable. That absence is useful evidence that the host-specific file for web2 did not accidentally apply to the whole group.
Inspect web2 and look for the host-specific rack value.
ansible-inventory --host web2 | grep -E 'service_role|service_port|rack'
Inspect db1.
ansible-inventory --host db1 | grep -E 'service_role|service_port'
Compare all three hosts before writing the playbook. This is the normal inventory debugging flow: confirm inherited group values first, then check whether a host-specific value exists.
Write a Variable Report Playbook
In this step, you will create a short playbook that writes one report file per inventory host. The task uses inventory_hostname so each alias gets its own file even though all aliases run locally.
Before running commands in this step, return to the Ansible project directory. This keeps the commands stable even when LabEx runs each step separately.
cd /home/labex/project/rhce-ansible
Create the playbook.
cat > playbooks/write-inventory-report.yml <<'EOF'
---
- name: Write inventory variable reports
hosts: prod
gather_facts: false
tasks:
- name: Write report for each inventory host
ansible.builtin.copy:
dest: "/home/labex/project/rhce-ansible/reports/{{ inventory_hostname }}.txt"
content: "{{ inventory_hostname }} role={{ service_role }} port={{ service_port }} rack={{ rack | default('none') }}\n"
mode: '0644'
EOF
The expression {{ inventory_hostname }} expands to the current host alias, so one task can create separate report files for web1, web2, and db1. The default('none') filter prevents an error on hosts that do not define rack.
Run the playbook and inspect the reports.
ansible-playbook playbooks/write-inventory-report.yml
The play targets prod, which is the parent group containing both web and database hosts. The recap should include all three aliases and should finish with failed=0.
cat reports/*.txt
The report files are the final proof that inventory variables resolved correctly. web2.txt should include rack-b, while the other hosts should show rack=none.
Summary
You built a static inventory with groups, child groups, group variables, and host variables. You also validated variable resolution with ansible-inventory and a small playbook.


