<aside> 🧭
Module 09 · Dynamic Inventory & Cloud
Every module so far assumed you know which servers exist. In any cloud environment you do not — the list changes while you are reading it. This module makes the inventory a query rather than a file.
🧠 concept → 🧪 exercise → ✅ expected result (hidden) → 🎯 interview questions (answers hidden)
Prerequisite: Modules 01–08. You saw a preview of aws_ec2 in Module 01 Part B1 — this is the full treatment.
</aside>
<aside> 📖
The analogy. Think of a printed telephone directory. It was completely accurate on the day it went to the printer. By the time it lands on your doorstep people have moved, changed numbers and been disconnected — and the book has no way of telling you which of its entries are now wrong. You only find out when you dial and nobody answers, or when the person you needed was never in it at all.
A static inventory file is that directory; a dynamic inventory is looking the number up live at the moment you dial. The contacts app from Module 01 B1 worked fine when you were adding servers by hand. Once an autoscaling group can create and destroy machines while you are reading the file, a printed list is not just inconvenient — it is wrong by design.
</aside>
<aside> 📖
Official docs: Working with dynamic inventory · Inventory plugins · amazon.aws.aws_ec2 inventory plugin
</aside>
flowchart TD
A["STATIC inventory<br>hosts.yml"] --> A1["You edit it by hand"]
A1 --> A2["Autoscaling adds 12 instances"]
A2 --> A3["❌ File is now wrong<br>New hosts unmanaged<br>Terminated hosts still listed"]
B["DYNAMIC inventory<br>aws_ec2.yml"] --> B1["Plugin queries the cloud API<br>at the moment you run"]
B1 --> B2["Autoscaling adds 12 instances"]
B2 --> B3["✅ Next run includes them<br>automatically<br>Terminated hosts disappear"]
style A3 fill:#FEE2E2,stroke:#DC2626
style B3 fill:#D1FAE5,stroke:#059669,stroke-width:2px
<aside> 🔑
A static inventory is a cache of reality that nothing invalidates. The moment autoscaling, spot instances, or anyone using the cloud console exists, the file is wrong and nobody is told.
The two failure modes are asymmetric and both bad: missing hosts silently go unpatched and unmonitored, and stale hosts make every run report unreachable errors until people learn to ignore red output — which is how a genuinely unreachable production host gets missed.
</aside>
| Approach | Detail |
|---|---|
| Inventory plugin | ⭐ The modern way. A YAML config file declaring plugin: and its options. Supports caching, keyed_groups, compose, and runs in-process |
| Inventory script | Legacy. Any executable that prints inventory JSON when called with --list. Still supported, still works, but no caching and no shared features |
<aside> 💡
You will still meet scripts — the old ec2.py is in a lot of legacy repositories. It works, but every plugin feature (caching, keyed_groups, compose, constructed) had to be reimplemented inside each script, which is exactly why plugins replaced them.
If asked to modernise one: the plugin config is usually 15 lines of YAML replacing several hundred lines of Python.
</aside>
<aside> 📁
The analogy. Think of a form that only gets processed if the filename is right.
You fill it in perfectly and email it in. Nothing comes back — no rejection, no error, just silence — because the department's system only picks up files named a particular way, and yours was not.
Silence is the worst possible response, because everything you can see says you did it correctly.
The aws_ec2 plugin behaves exactly like this. Name the file aws.yml instead of prod.aws_ec2.yml and you get an empty inventory, no error, and a run that "succeeds" having done nothing at all.
</aside>
# ansible.cfg
[inventory]
enable_plugins = auto, host_list, yaml, ini, toml, amazon.aws.aws_ec2, constructed
cache = True
cache_plugin = jsonfile
cache_connection = /tmp/ansible_inventory_cache
cache_timeout = 3600
<aside> ⚠️
The filename rule that silently breaks everything. The aws_ec2 plugin requires its config file to end in aws_ec2.yml or aws_ec2.yaml. Name it inventory/aws.yml and you get an empty inventory with no error at all — Ansible simply does not recognise the file as belonging to that plugin.
This is the single most common dynamic-inventory support question, and the reason it is so confusing is that everything looks correct. Other cloud plugins have equivalent conventions: azure_rm.yml, gcp_compute.yml.
</aside>
🧪 Exercise A3.1 — Reproduce the empty-inventory trap
mkdir -p inventory
cat > inventory/wrongname.yml <<'EOF'
plugin: amazon.aws.aws_ec2
regions:
- ap-southeast-1
EOF
cp inventory/wrongname.yml inventory/prod.aws_ec2.yml
ansible-inventory -i inventory/wrongname.yml --graph
ansible-inventory -i inventory/prod.aws_ec2.yml --graph
ansible-inventory -i inventory/wrongname.yml --graph -vvvv 2>&1 | grep -i 'declined\|skipping\|parse'