Skip to content
CloudOps
Newsletter
All guides
AI for Ansible By James Joyner IV · · 8 min read

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:

  • filters scope the query so you’re not pulling every instance in the account — only running production hosts here.
  • keyed_groups auto-build groups from tags. An instance tagged Role: web lands in a group called role_web. Now your playbooks target hosts: role_web and it just works, no matter how many web servers exist.
  • compose sets 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:DescribeInstances and 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.ini and the tags you use, and ask for an aws_ec2.yml that reproduces the same groups via keyed_groups. It’s a clean, well-specified transformation.
  • Debugging an empty inventory. “My ansible-inventory --graph returns 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.

Free download · 368-page PDF

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.