Ansible Static Inventory and Host Patterns
An inventory is a list of hosts arranged into groups, and the groups only earn their keep once you start selecting with them. This builds one in INI format with two groups, a parent group and group variables, works through the host patterns the exam expects, shows the failure mode of a pattern that matches nothing, and then writes the same inventory in YAML.
Control Node and Managed Nodes Guide 3 of 45 Beginner
- OSUbuntu 26.04 LTS
- ansible-core2.20.1
- Python3.14.4
- TimeAbout 15 min
- Reviewed23 August 2026
Written against the versions above. Both INI and YAML inventories are fully supported and neither is deprecated. INI is faster to type under exam pressure; YAML is easier to keep correct once there is real nesting. `ansible-inventory --graph` renders either one the same way, which makes it the fastest way to check you wrote what you meant.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| ANS-CTL01 | 192.168.0.36 | Ubuntu 26.04 LTS | Ansible Control Node | 2 Core | 3 GB | 50 GB |
| ANS-A01 | 192.168.0.37 | Ubuntu 26.04 LTS | Managed Node (group: web) | 2 Core | 3 GB | 50 GB |
| ANS-B01 | 192.168.0.38 | Ubuntu 26.04 LTS | Managed Node (group: db) | 2 Core | 3 GB | 50 GB |
Before you start
- A control node with Ansible and SSH access to two hosts.
- The session rewrites
~/inventory.iniand adds~/inventory.yml.
-
An inventory with two groups and a parent
[web] ans-a01 [db] ans-b01 [production:children] web db [web:vars] app_port=8080Four ideas in nine lines: groups, a parent group built from other groups with
:children, group variables with:vars, and[all:vars]for everything.@all: |--@ungrouped: |--@web: | |--ans-a01 |--@db: | |--ans-b01ansible-inventory --graphis the check to run every time you edit an inventory. Two groups exist implicitly and always:all, andungroupedfor hosts you listed outside any group.Add
--varsand the graph shows which variables a group carries, which is how you confirm a:varsblock landed where you intended.bash Example session cat > ~/inventory.ini <<'EOF'[web]ans-a01 [db]ans-b01 [production:children]webdb [web:vars]app_port=8080 [all:vars]ansible_python_interpreter=/usr/bin/python3EOFcat ~/inventory.ini[web]ans-a01 [db]ans-b01 [production:children]webdb [web:vars]app_port=8080 [all:vars]ansible_python_interpreter=/usr/bin/python3ansible-inventory --graph@all: |--@ungrouped: |--@production: | |--@web: | | |--ans-a01 | |--@db: | | |--ans-b01ansible-inventory --graph --vars web@web: |--ans-a01 | |--{ansible_python_interpreter = /usr/bin/python3} | |--{app_port = 8080} |--{app_port = 8080}Expected resultA graph showing web, db, and their parent.
Success conditionYou can write an inventory and verify it in one command.
-
Patterns decide who runs
Every
ansibleandansible-playbookinvocation takes a pattern, and--list-hostsanswers 'who would this hit' without running anything:| Pattern | Meaning | |---|---| |
all| every host | |web| one group | |production| a parent group, expanded | |web:db| the union - a colon is OR, not AND | |all:!db| everything except a group | |ans-*| glob on the host name |The one that catches people is
web:db. It reads like an intersection and it is a union; the intersection operator is:&.--list-hostsbefore anything destructive is a cheap habit. It is the difference between restarting a service on one group and restarting it on the fleet.bash Example session ansible all --list-hosts hosts (2): ans-a01 ans-b01ansible web --list-hosts hosts (1): ans-a01ansible production --list-hosts hosts (2): ans-a01 ans-b01ansible 'web:db' --list-hosts hosts (2): ans-a01 ans-b01ansible 'all:!db' --list-hosts hosts (1): ans-a01ansible 'ans-*' --list-hosts hosts (2): ans-a01 ans-b01Expected resultSix patterns and the hosts each resolves to.
Success conditionYou can target exactly the hosts you mean.
-
A pattern that matches nothing
Ask for a group that does not exist:
[WARNING]: Could not match supplied host pattern, ignoring: cachehosts (0):and running a module against it:
[WARNING]: No hosts matched, nothing to doA warning, not an error, and an exit code of 0. A typo in a group name does not fail your command - it succeeds against nobody. In a pipeline or a script that is a silent no-op reported as success, and it is one of the more expensive small mistakes in Ansible.
Guard against it the same way:
--list-hostsfirst, and read the count.bash Example session ansible 'cache' --list-hosts[WARNING]: Could not match supplied host pattern, ignoring: cache[WARNING]: No hosts matched, nothing to do hosts (0):ansible 'cache' -m ping[WARNING]: Could not match supplied host pattern, ignoring: cache[WARNING]: No hosts matched, nothing to doExpected resultA warning, zero hosts, and a successful exit.
Success conditionYou will not trust a play that quietly matched nothing.
-
The same inventory in YAML
The nesting is explicit, which is the point:
all: children: production: children: web: hosts: ans-a01: vars: app_port: 8080--graphrenders it identically to the INI version, so the two really are interchangeable.--hostdumps everything one host has resolved, merging group variables from every group it belongs to - which is the command to reach for when a variable is not what you expected.And
-ioverrides the inventory fromansible.cfgfor a single run, which is how you test a new inventory without editing your config:ansible -i ~/inventory.yml production -m pingWhich format to use: INI when you are typing against a clock, YAML when the structure has real depth or when the same file carries a lot of variables. Both are examinable; neither is going away.
bash Example session cat > ~/inventory.yml <<'EOF'all: vars: ansible_python_interpreter: /usr/bin/python3 children: production: children: web: hosts: ans-a01: vars: app_port: 8080 db: hosts: ans-b01:EOFansible-inventory -i ~/inventory.yml --graph@all: |--@ungrouped: |--@production: | |--@web: | | |--ans-a01 | |--@db: | | |--ans-b01ansible-inventory -i ~/inventory.yml --host ans-a01{ "ansible_python_interpreter": "/usr/bin/python3", "app_port": 8080}ansible -i ~/inventory.yml production -m pingans-a01 | SUCCESS => { "changed": false, "ping": "pong"}ans-b01 | SUCCESS => { "changed": false, "ping": "pong"}Expected resultAn identical graph from a different file format.
Success conditionYou can write an inventory in either format and check it the same way.
Troubleshooting
Could not match supplied host pattern.Why: The group or host does not exist in the inventory being used.
Fix:
ansible-inventory --graph. Note this is a warning and the command still exits 0.A group variable is not reaching a host.
Why: It is in the wrong
:varsblock, or overridden by a more specific one.Fix:
ansible-inventory --host <name>shows the merged result for that host.web:dbreturned more hosts than expected.Why: A colon is a union. You wanted
web:&dbfor the intersection.Fix:
--list-hostsbefore running anything.Ansible uses a different inventory than the one you edited.
Why:
ansible.cfgpoints elsewhere, or/etc/ansible/hostsis the default.Fix:
ansible --versionnames the config file;ansible-config dump | grep DEFAULT_HOST_LISTnames the inventory.