skip to content

Your Ansible runs target EC2 instances that scale up and down all day, and the checked-in static inventory is permanently out of date. How do you move to a dynamic inventory, and why prefer an inventory plugin over an inventory script?

level: seniorimportance: should knowfreq 52%

answer

  1. stop maintaining the host list by hand
  2. the cloud API answers at run time
  3. config file name is not arbitrary
  4. tags become groups declaratively
  5. the old alternative was an opaque program

basics

~20 s

Replace the static file with a dynamic inventory plugin config — for EC2, a YAML file whose name ends in aws_ec2.yml declaring the amazon.aws.aws_ec2 plugin. Plugins are preferred over executable scripts because they are configured declaratively, support caching, and build groups from tags.

solid answer

~40 s

I stop maintaining the host list by hand and let the cloud be the source of truth. For AWS that is the `amazon.aws.aws_ec2` inventory plugin: a YAML config file whose filename ends in `aws_ec2.yml`, enabled through `enable_plugins` in `ansible.cfg`, and passed with `-i`. Inside it I filter to the instances I want, and use `keyed_groups` to turn instance tags into Ansible groups so `hosts: env_prod` keeps working, plus `compose` to set `ansible_host` from the right address. Plugins beat the old executable scripts because a script is an opaque program that must implement the `--list` and `--host` protocol and reinvent everything else — a plugin is declarative YAML, reads `ansible.cfg`, can use encrypted credentials, and supports caching so a large account is not re-queried for every play. I verify with `ansible-inventory -i inventory.aws_ec2.yml --graph`.

code

yaml · 15 lines
yaml
plugin: amazon.aws.aws_ec2
regions:
  - eu-west-1
filters:
  instance-state-name: running
keyed_groups:
  - key: tags.Role
    prefix: role
  - key: tags.Environment
    prefix: env
compose:
  ansible_host: private_ip_address
cache: true
cache_plugin: jsonfile
cache_timeout: 3600

go deeper

for a junior

Know that an inventory does not have to be a static file — Ansible can ask the cloud provider who exists at run time, via a plugin you pass to -i like any other inventory.

for a middle

Explain the plugin config: the plugin key, filters to narrow the query, keyed_groups to build groups from tags, and compose to set ansible_host.

for a senior

Argue plugin over script on configuration, caching, credentials and grouping, and show the operational habits — verify with ansible-inventory --graph, and reason about cache staleness for the run at hand.

for a principal

Own the tagging contract the inventory depends on: if groups are derived from tags, untagged instances are unmanaged, so tagging becomes an enforced platform requirement rather than a convention.

## The problem with a static file An autoscaling fleet invalidates a checked-in host list continuously. Every scale event makes the file wrong in one of two directions: a new host that no play configures, or a terminated host that every play now fails to reach. Patching it by hand is toil, and the file's mistakes are discovered during a run rather than in review. Dynamic inventory removes the file: the cloud API is asked at run time who exists. ## What a plugin config looks like An inventory plugin is configured by a YAML file that you pass to `-i` like any other inventory source. For EC2 the plugin ships in the `amazon.aws` collection, and the file name must end with `aws_ec2.yml` or `aws_ec2.yaml` for the plugin to claim it: ```yaml # inventory.aws_ec2.yml plugin: amazon.aws.aws_ec2 regions: - eu-west-1 filters: instance-state-name: running tag:Environment: prod keyed_groups: - key: tags.Role prefix: role - key: tags.Environment prefix: env compose: ansible_host: private_ip_address cache: true cache_plugin: jsonfile cache_timeout: 3600 ``` The plugin must be enabled — `ansible.cfg` carries an `enable_plugins` list under `[inventory]`, and non-default plugins have to appear in it. Then `ansible-playbook -i inventory.aws_ec2.yml site.yml` targets whatever exists right now. The important options are the ones that make the result *usable*, not merely current: - **`filters`** narrows the query at the API, so you do not pull an entire account. - **`keyed_groups`** manufactures groups from instance attributes, most usefully tags. This is the piece that lets existing plays keep saying `hosts: role_web` after the static groups are gone. - **`compose`** sets host variables from instance attributes — typically `ansible_host` from a private or public address, since the default host name may not be reachable from where the run happens. - **`hostnames`** controls what each host is called in the inventory, which matters because `host_vars` files and `--limit` arguments key off that name. - **`cache`** with a cache plugin and timeout stops every play in a run from re-querying the API. ## Why plugins, not scripts Before plugins, dynamic inventory meant an executable that Ansible ran, implementing a protocol: `--list` prints all hosts, groups and (via `_meta.hostvars`) host variables as JSON, and `--host <name>` prints one host's variables. That still works and you will meet it in older repositories, but everything around it is homemade: - **Configuration.** A script takes environment variables or its own config file; a plugin is a YAML file that lives beside the playbooks and is reviewed like code. - **Caching.** A script has to build its own; plugins use Ansible's cache plugins with a shared timeout. - **Credentials.** A plugin runs in the Ansible process, so it uses the collection's SDK and can read variables from an encrypted file; a script tends to accumulate its own credential handling. - **Grouping.** `keyed_groups` and `compose` are declarative and diffable. In a script, group construction is imperative code nobody wants to own. - **Maintenance.** The plugin ships with the collection and is updated with it. The script is a bespoke program with its own bugs. For an interview, the short version is: a script is an opaque program that must reimplement configuration, caching and grouping; a plugin is declarative YAML that inherits all three from Ansible. ## Operating it Three habits matter in production. First, **verify before you run**: `ansible-inventory -i inventory.aws_ec2.yml --graph` shows the groups the plugin invented, and `--list --yaml` shows the host variables it composed. Second, **be deliberate about caching**: a cache makes big accounts fast but means a freshly launched instance may be invisible until the timeout expires — acceptable for a nightly patch run, dangerous for a run intended to configure the instance that just booted. Third, **combine sources when you need to**: pointing `-i` at a *directory* containing both a static file and a plugin config merges them, which is how a fleet of cloud instances and two hand-managed appliances live in one inventory. Authentication should come from the ambient role — an instance profile or a pipeline's federated credentials — rather than long-lived keys pasted into the config. The plugin config is a reviewed file in the repository; it is exactly the wrong place for a secret.

  • Existing plays say `hosts: role_web`, but the cloud has no such group. How do you keep them working?
    Use `keyed_groups` in the plugin config to build groups from instance attributes — `key: tags.Role` with `prefix: role` yields `role_web` for instances tagged Role=web. The plays never change; the grouping moves from a hand-edited file into a declarative rule applied to whatever exists.
  • What is the downside of enabling caching on a dynamic inventory?
    Staleness. Within the cache timeout, instances launched after the last query are invisible and terminated ones still appear. That is a good trade for a nightly run over a large account and a bad one for a play meant to configure an instance that just booted — those runs should bypass or shorten the cache.
  • Can a static inventory and a dynamic plugin be used together?
    Yes. Pass `-i` more than once, or point it at a directory containing both files: Ansible parses every source and merges the hosts and groups. That is the normal pattern when most of the fleet is cloud-managed but a few appliances or bastions are maintained by hand.

saying these in an interview costs you the question

  • Thinking dynamic inventory means committing a generated host file
  • Believing any .yml filename works for a plugin config
  • Assuming groups appear automatically without keyed_groups
  • Putting long-lived cloud keys in the inventory config
  • Ignoring cache staleness right after an instance launches

context