Build Static Inventories with Groups and Variables

AnsibleBeginner
Practice Now

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.