Affiliate-Exclusive: Get up to 40% off sitewide on CKA, CKAD, CKS, KCNA, KCSA, and the new MCPA exams and bundles!

AWS Dynamic Inventory in Ansible Automation Platform: aws_ec2 Plugin Best Practices

AWS Dynamic Inventory in Ansible Automation Platform: aws_ec2 Plugin Best Practices
Table of Contents

The problem with a static AWS inventory

Autoscaling groups spin instances up and down. Someone terminates a box and forgets to update the inventory file. A new environment gets tagged staging instead of stage, and nobody notices until a playbook runs against the wrong hosts. If you’re managing AWS with Ansible, a static inventory file goes stale the moment your fleet changes size, which for most teams is “constantly.”

The fix is to stop maintaining a host list by hand and let Ansible ask AWS directly, every time a job runs. That’s what the amazon.aws.aws_ec2 dynamic inventory plugin does.

I’ve covered dynamic inventory before with a Git-backed static file as the inventory source, which is great when your hosts don’t live in a cloud API. This post is the cloud-native sibling: instead of committing a host list to Git, Ansible queries the EC2 API on every sync and builds the inventory from tags, regions, and instance state.

Info

This guide focuses on Ansible Automation Platform (AAP) as the place you’ll actually run this in production, but everything in the “anatomy” section works identically from the CLI or ansible-navigator.

Quick CLI demo: the plugin in 30 seconds

Before touching AAP, it’s worth seeing the plugin work locally so you understand what you’re actually configuring later.

ansible-galaxy collection install amazon.aws
pip install boto3 botocore --break-system-packages

Create a file that ends in aws_ec2.yml (the suffix isn’t optional, the plugin loader matches on it):

# demo.aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
  - ap-southeast-2
filters:
  instance-state-name: running
  tag:Project: blog-demo
keyed_groups:
  - key: tags.Environment
    prefix: env
hostnames:
  - tag:Name
compose:
  ansible_host: public_ip_address

Run it:

ansible-inventory -i demo.aws_ec2.yml --graph
@all:
  |--@ungrouped:
  |--@aws_ec2:
  |  |--db-01
  |  |--db-02
  |  |--web-01
  |  |--web-02
  |  |--app-01
  |--@env_dev:
  |  |--db-01
  |--@env_prod:
  |  |--db-02
  |  |--web-01
  |  |--web-02
  |--@env_staging:
  |  |--app-01

Five lines of YAML and you already have hosts grouped by environment, named after their Name tag, and reachable on their public IP. That’s the entire mental model. Everything else is refinement.

Anatomy of the aws_ec2.yml file

Breaking down each key from the sample above, since these are the ones you’ll actually touch:

  • regions - scope this to where your instances actually live. Leaving it empty queries every enabled region, which is slower and burns more API calls than you need.
  • filters - a dictionary of EC2 API filter/value pairs. instance-state-name: running is the most common one, since nobody wants terminated instances cluttering an inventory.
  • keyed_groups - this is where dynamic grouping happens. key: tags.Environment reads the Environment tag off each instance and creates a group named env_<value> (thanks to prefix: env). Do this instead of maintaining group membership by hand.
  • hostnames - a precedence list for what to call each host. tag:Name means “use the value of the Name tag.” If that tag is missing on an instance, the plugin falls through to whatever’s next in the list (or the instance ID if nothing matches).
  • compose - lets you set host variables from Jinja2 expressions. ansible_host: public_ip_address is the one line that actually makes the inventory usable, since it tells Ansible how to connect to each host, separately from what it’s named.

Getting hostnames right

This one trips people up because the same option name can mean two different things depending on how you write it. A plain string like tag:Name just uses that tag’s value. But you can also hand it a dictionary to control prefix and separator behavior:

hostnames:
  - name: 'private-ip-address'
    separator: '_'
    prefix: 'tag:Name'

That produces hostnames like webserver01_10.0.1.23, private IP prefixed with the instance’s Name tag. Useful when you have instances across VPCs that could otherwise share the same private IP and collide in the inventory.

Tip

List multiple entries under hostnames in order of precedence (ip-address, then dns-name, then tag:Name) so instances missing one attribute still get a sane fallback instead of failing the sync.

Configuring AWS Dynamic inventory in Ansible Automation Platform

The CLI version above is the same YAML you’ll paste into AAP’s source_vars field. Here’s the full flow.

1. Create a dedicated IAM service account

Before touching AAP, create a dedicated read-only IAM user for the inventory sync - one that can only describe EC2 resources and nothing else.

# Create the policy
aws iam create-policy \
  --policy-name aap-inventory-readonly \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [{
      "Sid": "AnsibleInventoryReadOnly",
      "Effect": "Allow",
      "Action": [
        "ec2:DescribeInstances",
        "ec2:DescribeTags",
        "ec2:DescribeRegions"
      ],
      "Resource": "*"
    }]
  }'

# Create the user and attach the policy
aws iam create-user --user-name aap-inventory-reader
aws iam attach-user-policy \
  --user-name aap-inventory-reader \
  --policy-arn arn:aws:iam::<ACCOUNT_ID>:policy/aap-inventory-readonly

# Generate access keys - save these, the secret is shown only once
aws iam create-access-key --user-name aap-inventory-reader

Tip

Name the IAM user something that makes the purpose obvious - aap-inventory-reader or ansible-ec2-readonly. When you audit IAM users six months later, you’ll thank yourself.

2. Create the AWS credential in AAP

Go to Resources > Credentials and create a new credential using the Amazon Web Services credential type.

Warning

Long-lived IAM user access keys work here, but they’re the weaker option: a leaked key doesn’t expire on its own. Where your execution nodes run inside AWS, an IAM role attached to the instance (or an assumed role via iam_role_arn in source_vars) avoids storing static credentials in AAP at all.

AAP credential type screen with Amazon Web Services selected
Creating an Amazon Web Services credential in Ansible Automation Platform

3. Create the inventory and add the EC2 source

Create a new inventory, then go to its Sources tab and click Add. Choose Amazon EC2 as the source, select the credential from step 1, and pick an execution environment.

Paste the plugin config into Source variables:

plugin: aws_ec2
regions:
  - ap-southeast-2
filters:
  instance-state-name: running
keyed_groups:
  - key: tags.Environment
    prefix: env
  - key: tags.Criticality
    prefix: crit
hostnames:
  - tag:Name
compose:
  ansible_host: public_ip_address
AAP add inventory source screen, Amazon EC2 selected, source_vars filled
Amazon EC2 inventory source with source_vars populated in AAP

4. Sync and verify

Save, then click Sync. Check the inventory’s Hosts and Groups tabs, you should see hosts grouped by env_* and crit_* based on the tags above.

AAP inventory hosts list after sync, showing env_prod and env_dev groups
Hosts and groups populated after syncing the EC2 inventory source
AAP inventory groups after sync
Hosts and groups populated after syncing the EC2 inventory source

A few things that matter more once this is running in a real environment, not a demo.

  • Scope by region and account. Don’t query every region by default. It slows the sync and burns API calls you don’t need. If you manage multiple AWS accounts, use separate inventory sources per account rather than one giant source trying to span all of them.
  • Standardize your tags before you rely on them. keyed_groups is only as good as your tagging discipline. Agree on tag keys like Environment and Criticality across the team before building groups around them, otherwise you’ll get a env_prod, an env_Prod, and an env_PRODUCTION all as separate groups.
  • Use a read-only IAM role for the sync, not your automation role. The inventory sync only needs to describe instances, never modify them. A minimal policy looks like this:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AnsibleInventoryReadOnly",
      "Effect": "Allow",
      "Action": [
        "ec2:DescribeInstances",
        "ec2:DescribeTags",
        "ec2:DescribeRegions"
      ],
      "Resource": "*"
    }
  ]
}

This is narrower than the AWS-managed AmazonEC2ReadOnlyAccess policy, which also grants read access to snapshots, volumes, and several other EC2 sub-resources the inventory plugin never touches.

  • Know where the sync actually runs. By default, AAP runs inventory sync jobs on the control plane, not on execution nodes. If your AWS account is only reachable from a specific network segment, associate the inventory source with an instance group that has that connectivity, rather than assuming any execution node will do.
  • Split sources by environment. Separate prod-ap-southeast-2 and dev-ap-southeast-2 sources, instead of one source filtered by tag, make RBAC simpler and mean a sync failure in dev doesn’t touch your production inventory.

Troubleshooting

Sync completes but returns zero hosts. Almost always a filter typo, or the credential’s region doesn’t match where your instances actually run. Test the same source_vars content locally with ansible-inventory -i test.aws_ec2.yml --graph first, it’s much faster to debug than re-running a sync in the UI.

Sync job errors out immediately. Check that the execution environment you selected actually has amazon.aws, boto3, and botocore installed. The default AAP execution environment usually does, a custom one might not.

Hosts appear but keep changing names between syncs. Your hostnames precedence list is probably falling through to something unstable, like dns-name on instances that get a new one on every stop/start. Pin it to a tag-based name instead.

Wrapping up

If you’re already comfortable with file-based dynamic inventory sourced from a project, the EC2 plugin is a smaller jump than it looks. Same source-and-sync pattern in AAP, just pointed at AWS’s API instead of a Git repo, with tags doing the grouping work a static file would otherwise need manual upkeep to match.

Happy Engineering!

Gineesh Madapparambath

Gineesh Madapparambath

Gineesh Madapparambath is the founder of techbeatly. He is the co-author of The Kubernetes Bible, Second Edition and the author of Ansible for Real Life Automation. He has worked as a Systems Engineer, Automation Specialist, and content author. His primary focus is on AI, Ansible Automation, Containerization (OpenShift & Kubernetes), and Infrastructure as Code (Terraform). (Read more: gineesh.com)


Note

Disclaimer: The views expressed and the content shared in all published articles on this website are solely those of the respective authors, and they do not necessarily reflect the views of the author’s employer or the platform. We strive to ensure the accuracy and validity of the content published on our website. However, we cannot guarantee the absolute correctness or completeness of the information provided. It is the responsibility of the readers and users of this website to verify the accuracy and appropriateness of any information or opinions expressed within the articles. If you come across any content that you believe to be incorrect or invalid, please contact us immediately so that we can address the issue promptly.

Share :

Related Posts

Installing Ansible AI Skills with Lola and AI Forge

Installing Ansible AI Skills with Lola and AI Forge

In the previous post, we covered what Lola is and why it matters. Now let’s put it to work: installing Ansible community AI skills from the …

How to Become a Red Hat Certified Architect (RHCA)

How to Become a Red Hat Certified Architect (RHCA)

If you spend enough time working with Linux, automation, and platforms, the Red Hat certification path eventually becomes part of the conversation. …

Claude Code + Ansible Automation Platform: How I Manage AAP with MCP Server

Claude Code + Ansible Automation Platform: How I Manage AAP with MCP Server

Introduction I design and deploy Ansible Automation Platform for customers, and the day-to-day routine for most teams hasn’t changed much over …

Book Review: Generative AI at AWS

Book Review: Generative AI at AWS

Authors: Nestor Gandara, Eduardo Ordax, Srikanth Daggumalli, Ashutosh Dubey Publisher: Packt

Installing Ansible AI Skills with Lola and AI Forge

Installing Ansible AI Skills with Lola and AI Forge

In the previous post, we covered what Lola is and why it matters. Now let’s put it to work: installing Ansible community AI skills from the …

Learn Ansible: A Comprehensive Guide to Courses, Certifications, and Exams (2026 Update)

Learn Ansible: A Comprehensive Guide to Courses, Certifications, and Exams (2026 Update)

Introduction Ansible is one of the most widely used open-source automation tools, letting teams automate everything from Linux and Windows servers to …