Dynamic Inventory¶
What You'll Learn¶
- Why a static inventory file can't keep up with real infrastructure
- Inventory plugins vs. legacy inventory scripts
- A complete, working AWS EC2 dynamic inventory configuration
Why This Exists¶
A hand-maintained inventory file can't keep up with autoscaling infrastructure — instances come and go, IPs change, and a static inventory.ini is stale within hours. Dynamic inventory plugins query a live source of truth (a cloud API, a Kubernetes API) at run time instead, so the inventory literally cannot drift from what's actually running. This is the same problem Inventory introduces and defers here.
Plugins, Not Scripts¶
Modern Ansible sources dynamic inventory through YAML-configured inventory plugins (amazon.aws.aws_ec2, azure.azcollection.azure_rm, kubernetes.core.k8s) — the legacy alternative, inventory scripts (arbitrary executables that print JSON), still works but is largely superseded; plugins are declarative, don't require writing and maintaining a custom script, and integrate with Ansible's caching and compose/keyed_groups machinery natively.
A Complete AWS EC2 Example¶
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
filters:
tag:Environment: production
instance-state-name: running
keyed_groups:
- key: tags.Role
prefix: role
compose:
ansible_host: public_ip_address
cache: true
cache_plugin: jsonfile
cache_connection: /tmp/ansible_inventory_cache
cache_timeout: 300
Reading it field by field:
plugin: amazon.aws.aws_ec2— selects the plugin; the filename must end inaws_ec2.yml/aws_ec2.yamlfor Ansible to recognize it as this plugin's config.filters— scopes which EC2 instances are included at all; here, only running instances taggedEnvironment: production.keyed_groups— auto-creates inventory groups from an EC2 tag. An instance taggedRole: webautomatically lands in a group calledrole_web— no manual group maintenance.compose— derives a variable from an existing attribute; here,ansible_hostis set to the instance's public IP so Ansible knows how to connect without a separate lookup.cache— caches the resolved inventory (distinct from fact caching) so every single run doesn't cost a fresh API round trip.
Inspecting What It Resolved¶
Run this before trusting a dynamic source against a real playbook — the fastest way to confirm the plugin resolved what you expect, including that keyed_groups actually produced the groups you intended.
Least-Privilege Credentials¶
The IAM policy (or Azure/GCP equivalent) behind a dynamic inventory plugin only ever needs to list and describe resources — never write access:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["ec2:DescribeInstances", "ec2:DescribeTags"],
"Resource": "*"
}
]
}
Granting broader access "to be safe" is a common and unnecessary mistake — the plugin never modifies anything.
Common Mistakes¶
- Granting the inventory plugin's cloud credentials broad write access when it only ever needs to list resources.
- Not enabling
cache, adding real latency (an API round trip) to the start of every single run against a large fleet. - Filtering too loosely (or not at all), pulling in every instance in an account instead of scoping to the environment/tag actually being managed.
- Forgetting the filename convention (
*_aws_ec2.yml) — Ansible identifies which plugin to use partly from the filename suffix.
Interview Questions¶
- How would you source inventory automatically from AWS EC2 instead of maintaining a static file?
- How does
keyed_groupsturn a cloud tag into an Ansible inventory group automatically? - What credentials does a dynamic inventory plugin need, and how should they be scoped?
Full worked example¶
See Case Study: Dynamic Inventory for the same plugin used end-to-end in a real playbook run.
Next¶
Continue to Production Engineering.