Ansible Dynamic Inventory for Cloud Infrastructure That Won't Stop Changing
Static inventory files rot the moment your cloud autoscales. Here's how to wire up dynamic inventory so Ansible always sees the truth — across AWS, GCP, and Azure.
- #iac
- #ansible
- #dynamic-inventory
- #aws
- #cloud
- #automation
There’s a moment every Ansible user hits: you maintain a tidy hosts.ini file, life is good, and then your infrastructure starts autoscaling. Instances come and go. IPs change. Your static inventory is wrong within minutes of writing it, and you find yourself copy-pasting IPs from the cloud console into a text file like it’s 2009.
Dynamic inventory is the fix. Instead of listing hosts, you ask the cloud provider “what exists right now?” at runtime. Here’s how to set it up so Ansible always targets reality.
How dynamic inventory works
A dynamic inventory source is a plugin (modern) or a script (legacy) that, when invoked, returns the current set of hosts as JSON. Ansible runs it on every playbook execution, so the inventory is always fresh.
Modern Ansible uses inventory plugins configured via YAML. You almost never want the old executable scripts anymore — the plugins are faster, cached, and far easier to configure.
AWS EC2 dynamic inventory
The amazon.aws.aws_ec2 plugin is the workhorse. Install the collection and create an inventory file ending in .aws_ec2.yml:
# inventory.aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
- us-west-2
filters:
instance-state-name: running
tag:Environment: production
keyed_groups:
- key: tags.Role
prefix: role
- key: placement.availability_zone
prefix: az
hostnames:
- private-ip-address
compose:
ansible_host: private_ip_address
What each piece buys you:
filtersscope the query so you’re not pulling every instance in the account — only running production hosts here.keyed_groupsauto-build groups from tags. An instance taggedRole: weblands in a group calledrole_web. Now your playbooks targethosts: role_weband it just works, no matter how many web servers exist.composesets variables per host — here, connecting over the private IP.
Verify it resolves:
ansible-inventory -i inventory.aws_ec2.yml --graph
You’ll see groups populated live from your account. Tag-driven grouping is the whole game: tag your instances well, and your inventory organizes itself.
GCP and Azure follow the same shape
The pattern is identical across clouds — only the plugin and field names change.
# inventory.gcp.yml
plugin: google.cloud.gcp_compute
projects:
- my-project-id
zones:
- us-central1-a
auth_kind: serviceaccount
service_account_file: /etc/gcp/sa.json
keyed_groups:
- key: labels.role
prefix: role
# inventory.azure_rm.yml
plugin: azure.azure_collection.azure_rm
include_vm_resource_groups:
- production-rg
keyed_groups:
- prefix: role
key: tags.role
Once you internalize “filter, then group by tags, then compose connection vars,” every cloud’s plugin reads the same.
Authentication: do it the boring, secure way
Dynamic inventory needs cloud credentials, and this is where people get sloppy. The rules:
- Use the provider’s standard credential chain. For AWS that’s the same chain the CLI uses — environment variables, shared config, or (best) an IAM role on the machine running Ansible. Don’t hardcode keys in the inventory file.
- Scope the IAM permissions to read-only describe calls. The inventory plugin only needs
ec2:DescribeInstancesand friends. It never needs write access. - On a bastion or CI runner, attach an instance role so there are no long-lived keys at all.
A least-privilege policy for EC2 inventory is tiny:
{
"Effect": "Allow",
"Action": ["ec2:DescribeInstances", "ec2:DescribeTags"],
"Resource": "*"
}
Caching so you’re not hammering the API
Every playbook run hitting the cloud API gets slow and rate-limited at scale. Enable inventory caching:
# ansible.cfg
[inventory]
cache = true
cache_plugin = jsonfile
cache_connection = /tmp/ansible_inventory_cache
cache_timeout = 3600
Now the inventory is fetched once an hour and cached. When you genuinely need fresh data (right after a scale-up), pass --flush-cache. The trade-off is staleness versus API load; an hour is a sane default for most teams.
Combining static and dynamic inventory
You rarely go fully dynamic overnight. Point Ansible at a directory and it merges every source inside:
ansible-playbook -i inventory/ site.yml
inventory/
├── static_hosts.ini # the on-prem boxes that never change
├── inventory.aws_ec2.yml # dynamic cloud hosts
└── group_vars/
└── role_web.yml # vars applied to the dynamic group
This is the realistic migration path: legacy hosts stay static, cloud fleets go dynamic, and group_vars/role_web.yml applies to dynamically-discovered web servers exactly as if you’d listed them by hand.
Where AI helps
The friction with dynamic inventory isn’t the concept — it’s remembering the exact plugin field names and keyed_groups syntax for each provider. That’s squarely in AI’s wheelhouse.
- Translating a static inventory to dynamic. Paste your
hosts.iniand the tags you use, and ask for anaws_ec2.ymlthat reproduces the same groups viakeyed_groups. It’s a clean, well-specified transformation. - Debugging an empty inventory. “My
ansible-inventory --graphreturns no hosts, here’s my config and a sample instance’s tags” — the model is good at spotting a filter typo or a region mismatch. - Writing the least-privilege IAM policy for the describe calls your plugin makes.
What AI can’t do is know your tagging conventions or your credential setup, so treat its output as a draft you verify against ansible-inventory --graph. We keep a library of IaC prompts for inventory and provisioning tasks like these.
The payoff
Once dynamic inventory is in place, autoscaling stops being your problem. A new instance comes up, gets its tags, and your next playbook run sees it automatically. You delete the ritual of copying IPs into a file, and your inventory can never again be wrong about what actually exists. For infrastructure that won’t stop changing, that’s the only inventory that survives contact with production.
AI-generated inventory configs are assistive, not authoritative. Always verify with ansible-inventory --graph before running playbooks against discovered hosts.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.