AnsibleIV · Inventory FundamentalsInventory inspection
Proving what your inventory contains
What you'll learn
- Choose the right inventory inspection command for structure, variables, one host, or a pattern
- Answer "which hosts is this group" from the command line rather than from memory
- Recognise that a pattern matching nothing exits successfully, and plan for it
- Build a pre-run verification habit that costs seconds and bounds every change
Prerequisites
Verified against ansible-core 2.21.x · ansible (community package) 14.x · Python (controller) 3.12+ · ansible-lint 26.x · Molecule 26.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-11
This is the lesson the rest of the part exists to make possible.
You now know that groups nest, that membership inherits upward, that
ranges expand into hosts nobody listed, and that two group_vars trees
can both be read. Every one of those is a way for the inventory to
resolve into something other than what the file appears to say.
So stop reading the file. Print the answer.
You do not know what
hosts: allmeans until you have printed it.
Everything here is read-only. Nothing in this lesson connects to a managed node, which means all of it is safe to run at any time, on any machine, including during an incident.
Five commands, four questions
| Question | Command |
|---|---|
| What is the group structure? | ansible-inventory --graph |
| What variables does each host have? | ansible-inventory --graph --vars |
| Everything, as machine-readable data | ansible-inventory --list |
| What does this one host have? | ansible-inventory --host <name> |
| Which hosts does this pattern reach? | ansible <pattern> --list-hosts |
The structure
$ ansible-inventory -i inventory/hosts.yml --graph@all:
|--@ungrouped:
|--@cache:
| |--cache01.example.com
|--@production:
| |--@webservers:
| | |--web01.example.com
| | |--web02.example.com
| |--@databases:
| | |--db01.example.com
| | |--db02.example.comRestrict it to one group when the fleet is large:
$ ansible-inventory -i inventory/hosts.yml --graph legacy@legacy:
|--oldboxThe variables
$ ansible-inventory -i inventory/hosts.yml --graph legacy --vars@legacy:
|--oldbox
| |--{ansible_host = 192.0.2.55}
| |--{ansible_python_interpreter = /usr/bin/python3.9}
| |--{ansible_user = root}For one host, --host gives clean JSON that is easy to diff between two
inventories or two branches:
$ ansible-inventory -i inventory/hosts.ini --host edge1{
"ansible_host": "192.0.2.11"
}--list gives the whole inventory in the same JSON form, and --list -y
gives it as YAML, which is easier to read when you are checking
structure rather than diffing:
$ ansible-inventory -i inventory/hosts.ini --list -yall:
children:
ungrouped:
hosts:
jump01.example.com: {}
webservers:
hosts:
web01.example.com: {}The pattern
ansible-inventory answers questions about the inventory.
--list-hosts answers the question you actually have before a change:
which machines will this touch?
$ ansible -i inventory/hosts.ini 'all:!oddweb' --list-hosts hosts (10):
web01.example.com
web02.example.com
web03.example.com
web04.example.com
db-a.example.com
db-b.example.com
db-c.example.com
db-d.example.com
edge1
edge2The count on the first line is the number to read. It is the blast radius of any play using that pattern, stated as an integer, before anything happens.
The same question can be asked of a playbook, which resolves its own
hosts: line rather than requiring you to retype it:
$ ansible-playbook -i inventory/hosts.ini --list-hosts playbooks/webtier.ymlplaybook: playbooks/webtier.yml
play #1 (webservers): Configure the web tier TAGS: []
pattern: ['webservers']
hosts (2):
web01.example.com
web02.example.comThis is the form to prefer, because it removes the transcription step. The pattern it reports is the one the play will actually use.
Both directions of wrong are silent
Here is the property that makes the habit necessary rather than merely sensible.
$ ansible -i inventory/hosts.yml nosuchgroup --list-hosts[WARNING]: Could not match supplied host pattern, ignoring: nosuchgroup
[WARNING]: No hosts matched, nothing to do
hosts (0):Exit code 0. A mistyped --limit in a scheduled job produces exactly
this: a nightly run that succeeds, every night, having done nothing at
all. Nothing alerts, because from the shell’s point of view the command
worked.
And the opposite direction is worse, because it produces no warning at all. A pattern that matches three hundred hosts when you meant thirty also exits 0, also reports success, and does change three hundred machines. There is no warning for “matched more than you expected”, because Ansible has no idea what you expected.
The habit
Five steps, seconds to run, no connection to anything.
- Confirm which config file and which inventory are in effect: ansible --version | head -2, then ansible-config dump --only-changed.
- Print the group structure: ansible-inventory --graph. Look for hosts in ungrouped, and for nesting you did not expect.
- Resolve the pattern the playbook will use: ansible-playbook --list-hosts <playbook>. Read the count.
- Compare the count with what you believe it should be. A mismatch stops here, before anything runs.
- Spot-check one host you did not expect to see, or one you expected and cannot find: ansible-inventory --host <name>.
Step four is the one that matters, and it is the one people skip. The other four produce output; that one requires you to have an expectation and to check it. A count you read without comparing is a count you did not check.
Knowledge check
Knowledge check · 4 questions
Q1. A nightly scheduled run has reported success for three weeks and changed nothing. The --limit value contains a typo. What did the operator see?
Q2. Which command answers "which hosts will this playbook actually touch" with the least chance of transcription error?
Q3. A variable whose value is a Jinja expression appears unevaluated in `ansible-inventory --list` output.
Q4. `ansible-inventory --graph` shows a host in a group, but the run did not appear to touch it. Which explanations are Ansible behaving as instructed rather than a defect? Select all that apply.
Passing score: 75%. Answers are checked in this browser.