Loading...

Lab 102: Managing Dynamic Inventories in Ansible

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.

automation inventory ansible

Scenario

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.

Operator context

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.

Objective

  • Create a dynamic inventory script that prints valid inventory JSON.
  • Make the script executable and query it with ansible-inventory.
  • Create a playbook that targets a dynamically-generated group.
  • Run the playbook using the script inventory source.

Concepts

  • Dynamic inventory as a “source of truth” adapter: file, script, or plugin.
  • Inventory graph fundamentals: groups, hosts, and the all tree.
  • _meta and hostvars for per-host variables without extra lookups.
  • Trust-but-verify workflow using ansible-inventory -i <source> --list.
  • Play targeting with hosts: against a group that must exist in the resulting graph.

Walkthrough

Step 1 : Create a minimal dynamic inventory script.
Command
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.

dyn_inventory.py
#!/usr/bin/env python3
import json

print(json.dumps({
  "web": { "hosts": ["web1.local", "web2.local"] },
  "_meta": { "hostvars": {} }
}))
Note

In real environments, your inventory source usually returns real hostnames or IPs plus hostvars. This lab focuses on the workflow and validation loop.

Step 2 : Make the script executable.
Command
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.

Step 3 : Validate inventory discovery output.
Command
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"
    ]
  }
}
Success signal

The web group is present, and both hosts appear under it with no parse errors.

Step 4 : Create a playbook that targets the web group.
Command
nano dyn_playbook.yml
# OR
vim dyn_playbook.yml
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.

Step 5 : Run the playbook using the dynamic inventory source.
Command
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
Troubleshooting

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.

Common breakpoints

ansible-inventory fails to parse the script output

Your script must print valid JSON only. Remove debug prints and confirm the script runs cleanly with ./dyn_inventory.py before retrying ansible-inventory.

web group is missing from the inventory graph

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.

Playbook runs but hosts are unreachable

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.

Script works manually but fails under Ansible

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.

Cleanup checklist

This lab creates local files only. Remove the inventory script and playbook if you do not need them for further testing.

Commands
rm -f dyn_inventory.py dyn_playbook.yml

Reference

  • 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.