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?
answer
- stop maintaining the host list by hand
- the cloud API answers at run time
- config file name is not arbitrary
- tags become groups declaratively
- the old alternative was an opaque program
basics
~20 sReplace 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 sI 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 linesplugin: 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: 3600go deeper
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.
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.
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.
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