Build and validate a dynamic inventory source so Ansible discovers target hosts at runtime instead of relying on a static inventory file.
Confirm discovery output with ansible-inventory and run a playbook against the resulting group.
Your target fleet changes frequently (ephemeral VMs, short-lived test hosts, container nodes). Maintaining a static inventory file becomes a drift problem. You need a repeatable way to generate the inventory graph at runtime so playbooks always point at what actually exists.
Treat the inventory graph as production input. Your first sanity check is always
ansible-inventory --list
to confirm groups, hosts, and hostvars before you run a playbook.
ansible-inventory.
all tree.
_meta and hostvars for per-host variables without extra lookups.
ansible-inventory -i <source> --list.
hosts: against a group that must exist in the resulting graph.
nano dyn_inventory.py
# OR
vim dyn_inventory.py
A dynamic inventory script must print valid JSON that matches the inventory
structure Ansible expects. This example defines a web group with
two hosts and includes an _meta stanza for host variables.
#!/usr/bin/env python3
import json
print(json.dumps({
"web": { "hosts": ["web1.local", "web2.local"] },
"_meta": { "hostvars": {} }
}))
In real environments, your inventory source usually returns real hostnames or IPs plus hostvars. This lab focuses on the workflow and validation loop.
chmod +x dyn_inventory.py
Ansible can execute inventory scripts directly when the file is runnable. The shebang plus the executable bit allows Ansible to treat the script as an inventory source.
ansible-inventory -i dyn_inventory.py --list
This is the trust-but-verify step. Confirm the group exists, hosts appear where you expect, and Ansible can build the inventory graph cleanly.
{
"_meta": {
"hostvars": {}
},
"all": {
"children": [
"ungrouped",
"web"
]
},
"web": {
"hosts": [
"web1.local",
"web2.local"
]
}
}
The web group is present, and both hosts appear under it with no parse errors.
web group.
nano dyn_playbook.yml
# OR
vim dyn_playbook.yml
- hosts: web
gather_facts: false
tasks:
- name: Ping web group hosts
ping:
The play targets hosts: web. If the inventory graph does not contain a
web group at runtime, Ansible has nothing to match and the run will not behave as intended.
ansible-playbook -i dyn_inventory.py dyn_playbook.yml
PLAY [web] *********************************************************************
TASK [Ping web group hosts] ****************************************************
ok: [web1.local] => {
"ansible_facts": {},
"changed": false,
"ping": "pong"
}
ok: [web2.local] => {
"ansible_facts": {},
"changed": false,
"ping": "pong"
}
PLAY RECAP *********************************************************************
web1.local : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
web2.local : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
If hosts are unreachable, inventory discovery worked but connectivity did not. In production, validate DNS/SSH and ensure your dynamic source returns routable addresses and required connection variables.
Your script must print valid JSON only. Remove debug prints and confirm the script runs cleanly with
./dyn_inventory.py
before retrying
ansible-inventory.
The playbook targets hosts: web, so the graph must include a web group.
Re-run
ansible-inventory -i dyn_inventory.py --list
and confirm the JSON includes the group key and a valid hosts list.
Discovery succeeded, but the targets are not reachable. Confirm DNS resolution, SSH connectivity, and the
connection method. If needed, return real IPs/hostnames and add required connection variables under
_meta
hostvars.
Ensure the script is executable and the shebang points to a valid Python interpreter. Verify permissions with
chmod +x
and run
./dyn_inventory.py
from the same directory where Ansible is invoked.
This lab creates local files only. Remove the inventory script and playbook if you do not need them for further testing.
rm -f dyn_inventory.py dyn_playbook.yml
ansible-inventory -i <source> --list
: Prints the full inventory graph as Ansible interprets it.
-i <source>
: Inventory source (file, script, or plugin).
--list
: Outputs the full inventory in JSON.
ansible-playbook -i <source> <playbook>
: Runs a playbook using the specified inventory source.
-i <source>
: Points Ansible at an inventory script instead of a static file.
chmod +x <script>
: Makes the inventory script executable so Ansible can run it.
+x
: Adds execute permission.
nano <file>
: Opens a file for editing in the Nano editor.
vim <file>
: Opens a file for editing in the Vim editor.
rm -f <file>...
: Removes files without prompting if they exist.
-f
: Forces removal and suppresses missing-file errors.