AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible Dynamic Inventory: Complete Guide to AWS, Azure, GCP, and Custom Plugins

By Luca Berton · Published 2024-01-01 · Category: installation

Master Ansible dynamic inventory. Configure AWS EC2, Azure, GCP, Proxmox, VMware, and custom inventory plugins.

What Is Dynamic Inventory?

Static inventory files work when your infrastructure is fixed — a known list of servers that rarely changes. But in cloud environments where instances scale up and down, containers spawn and die, and infrastructure is ephemeral, you need inventory that discovers hosts automatically.

Dynamic inventory queries external sources (AWS, Azure, VMware, a CMDB, a database) and builds the host list at runtime. No manual updates needed.

See also: AAP 2.6 Cloud Automation: AWS, Azure, and GCP with Ansible

Inventory Plugins vs Scripts

Ansible supports two approaches:

| Feature | Inventory Plugin (recommended) | Inventory Script (legacy) | |---------|-------------------------------|--------------------------| | Format | YAML configuration file | Executable script (Python, Bash) | | Caching | Built-in | Manual | | Composable | Yes (combine with static) | Limited | | Maintained | Actively | Deprecated for most use cases | | Performance | Optimized | Varies |

Always use inventory plugins unless you have a unique data source with no existing plugin.

AWS EC2 Dynamic Inventory

Setup

# Install AWS collection
ansible-galaxy collection install amazon.aws

# Install boto3 pip install boto3 botocore

# Configure AWS credentials export AWS_ACCESS_KEY_ID="your-key" export AWS_SECRET_ACCESS_KEY="your-secret" export AWS_DEFAULT_REGION="us-east-1" # Or use ~/.aws/credentials

Inventory Configuration

# inventory/aws_ec2.yml
plugin: amazon.aws.aws_ec2

# Regions to query regions: - us-east-1 - eu-west-1

# Filter instances filters: instance-state-name: running "tag:Environment": - production - staging

# Group by tags, instance type, region keyed_groups: - key: tags.Environment prefix: env separator: "_" - key: instance_type prefix: type - key: placement.region prefix: region - key: tags.Role prefix: role

# Set connection variables compose: ansible_host: private_ip_address # Use public IP if no VPN: # ansible_host: public_ip_address | default(private_ip_address) ansible_user: "'ubuntu'" ansible_ssh_private_key_file: "'~/.ssh/aws-key.pem'"

# Create groups from tags groups: webservers: tags.Role == 'web' databases: tags.Role == 'database' monitoring: tags.Role == 'monitoring'

# Caching (optional, speeds up repeated runs) cache: true cache_plugin: jsonfile cache_connection: /tmp/aws_inventory_cache cache_timeout: 300 # 5 minutes

Test It

# List all discovered hosts
ansible-inventory -i inventory/aws_ec2.yml --graph

# Output: # @all: # |--@env_production: # | |--web-01 # | |--web-02 # | |--db-01 # |--@env_staging: # | |--staging-web-01 # |--@role_web: # | |--web-01 # | |--web-02 # |--@type_t3_medium: # | |--web-01 # | |--web-02

# Show all variables for a host ansible-inventory -i inventory/aws_ec2.yml --host web-01

# Use in a playbook ansible-playbook -i inventory/aws_ec2.yml site.yml

See also: Ansible for Cloud Automation: AWS, Azure, and GCP Complete Guide

Azure Dynamic Inventory

# Install Azure collection
ansible-galaxy collection install azure.azcollection
pip install azure-identity azure-mgmt-compute azure-mgmt-network azure-mgmt-resource
# inventory/azure_rm.yml
plugin: azure.azcollection.azure_rm

# Authentication (or use env vars / managed identity) auth_source: auto

# Include only running VMs include_vm_resource_groups: - production-rg - staging-rg

# Conditional groups conditional_groups: webservers: "'web' in tags.Role | default('')" databases: "'db' in tags.Role | default('')"

# Keyed groups keyed_groups: - key: tags.Environment | default('untagged') prefix: env - key: location prefix: region - key: os_profile.system | default('linux') prefix: os

# Connection settings compose: ansible_host: private_ipv4_addresses[0] | default(public_ipv4_addresses[0]) ansible_user: "'azureadmin'"

# Exclude deallocated VMs exclude_host_filters: - powerstate != "running"

# Caching cache: true cache_plugin: jsonfile cache_connection: /tmp/azure_inventory_cache cache_timeout: 600

GCP Dynamic Inventory

ansible-galaxy collection install google.cloud
pip install google-auth requests
# inventory/gcp.yml
plugin: google.cloud.gcp_compute

# Authentication auth_kind: serviceaccount service_account_file: /path/to/service-account.json

# Projects to query projects: - my-gcp-project

# Zones zones: - us-central1-a - us-central1-b - europe-west1-b

# Filters filters: - status = RUNNING - labels.environment = production

# Grouping keyed_groups: - key: labels.role prefix: role - key: zone prefix: zone - key: machine_type | basename prefix: type

compose: ansible_host: networkInterfaces[0].networkIP ansible_user: "'ansible'"

groups: webservers: labels.role == "web" databases: labels.role == "database"

cache: true cache_plugin: jsonfile cache_connection: /tmp/gcp_inventory_cache cache_timeout: 300

See also: Ansible Terraform Integration: Orchestrate Infrastructure and Configuration Together

VMware Dynamic Inventory

ansible-galaxy collection install community.vmware
pip install pyvmomi
# inventory/vmware.yml
plugin: community.vmware.vmware_vm_inventory

hostname: vcenter.company.com username: "ansible@vsphere.local" password: "{{ vault_vcenter_password }}" validate_certs: false

# Only powered-on VMs with_nested_properties: true resources: - datacenter: - DC1 resources: - compute_resource: - Cluster1

filters: - runtime.powerState == "poweredOn"

keyed_groups: - key: config.guestId prefix: os - key: guest.net[0].network | default('unknown') prefix: network

compose: ansible_host: guest.ipAddress ansible_user: "'ansible'"

properties: - config.name - config.guestId - guest.ipAddress - runtime.powerState - config.hardware.memoryMB - config.hardware.numCPU

Combining Multiple Inventory Sources

Create an inventory directory with multiple sources:

inventory/
├── 01-static.yml          # Static hosts (network devices, etc.)
├── 02-aws_ec2.yml         # AWS dynamic inventory
├── 03-azure_rm.yml        # Azure dynamic inventory
├── group_vars/
│   ├── all.yml            # Variables for all hosts
│   ├── webservers.yml     # Variables for web group
│   └── databases.yml      # Variables for db group
└── host_vars/
    └── special-host.yml   # Host-specific variables
# Ansible reads ALL files in the directory
ansible-playbook -i inventory/ site.yml

# Or set in ansible.cfg [defaults] inventory = ./inventory/

Custom Dynamic Inventory Script

When no plugin exists for your data source, write a custom script:

#!/usr/bin/env python3
"""Custom dynamic inventory from CMDB API."""
import json
import argparse
import requests

CMDB_URL = "https://cmdb.company.com/api/v1" API_KEY = "your-api-key"

def get_inventory(): """Fetch hosts from CMDB and build Ansible inventory.""" headers = {"Authorization": f"Bearer {API_KEY}"} hosts = requests.get(f"{CMDB_URL}/hosts", headers=headers).json()

inventory = { "_meta": {"hostvars": {}}, "all": {"children": []} }

groups = {} for host in hosts: hostname = host["fqdn"]

# Add to groups for group in host.get("groups", []): if group not in groups: groups[group] = {"hosts": []} inventory["all"]["children"].append(group) groups[group]["hosts"].append(hostname)

# Host variables inventory["_meta"]["hostvars"][hostname] = { "ansible_host": host["ip_address"], "ansible_user": host.get("ssh_user", "ansible"), "os_type": host.get("os", "linux"), "datacenter": host.get("datacenter", "unknown"), "environment": host.get("environment", "unknown"), }

inventory.update(groups) return inventory

def get_host(hostname): """Fetch single host variables.""" headers = {"Authorization": f"Bearer {API_KEY}"} host = requests.get(f"{CMDB_URL}/hosts/{hostname}", headers=headers).json() return { "ansible_host": host["ip_address"], "ansible_user": host.get("ssh_user", "ansible"), }

if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--list", action="store_true") parser.add_argument("--host", type=str) args = parser.parse_args()

if args.list: print(json.dumps(get_inventory(), indent=2)) elif args.host: print(json.dumps(get_host(args.host), indent=2))

# Make executable
chmod +x cmdb_inventory.py

# Test ./cmdb_inventory.py --list | jq

# Use ansible-playbook -i cmdb_inventory.py site.yml

Inventory Plugin Development

For a reusable, cacheable plugin:

# plugins/inventory/cmdb.py
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable

DOCUMENTATION = """ name: cmdb plugin_type: inventory short_description: CMDB dynamic inventory options: api_url: description: CMDB API endpoint required: true api_key: description: API authentication key required: true env: - name: CMDB_API_KEY """

class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): NAME = "cmdb"

def verify_file(self, path): return super().verify_file(path) and path.endswith(("cmdb.yml", "cmdb.yaml"))

def parse(self, inventory, loader, path, cache=True): super().parse(inventory, loader, path, cache) self._read_config_data(path)

api_url = self.get_option("api_url") api_key = self.get_option("api_key")

# Fetch and populate inventory # self.inventory.add_host(hostname) # self.inventory.add_group(group) # self.inventory.add_child(group, hostname) # self.inventory.set_variable(hostname, key, value)

Best Practices

Use Caching

# In your inventory plugin config
cache: true
cache_plugin: jsonfile
cache_connection: /tmp/ansible_inventory_cache
cache_timeout: 300

# Clear cache when needed ansible-inventory -i inventory/ --flush-cache

Tag Everything

Dynamic inventory grouping relies on tags/labels. Establish a tagging standard:

# Required tags for all cloud resources
tags:
  Environment: production    # Groups: env_production
  Role: web                  # Groups: role_web
  Team: platform             # Groups: team_platform
  Application: api           # Groups: app_api
  CostCenter: engineering    # For billing

Use Compose for Connection Variables

compose:
  # Choose IP based on network access
  ansible_host: >-
    private_ip_address if 'vpn' in group_names
    else public_ip_address | default(private_ip_address)

# Set user based on OS ansible_user: >- 'ec2-user' if image_id.startswith('ami-amazon') else 'ubuntu' if image_id.startswith('ami-ubuntu') else 'centos'

FAQ

How does Ansible dynamic inventory work?

Ansible queries an external data source (cloud API, CMDB, database) at runtime to build the list of hosts and their variables. Instead of maintaining a static file, the inventory is generated dynamically every time you run a playbook.

Can I mix static and dynamic inventory?

Yes. Put both static YAML files and dynamic inventory plugin configs in the same directory. Ansible merges them automatically. Static inventory is great for network devices, bastion hosts, and other fixed infrastructure alongside dynamic cloud hosts.

How do I debug dynamic inventory issues?

Use ansible-inventory -i your_inventory.yml --list to see the full generated inventory as JSON. Add --graph for a tree view. Check ansible-inventory -i your_inventory.yml --host hostname for specific host variables.

Which inventory plugin should I use for AWS?

Use amazon.aws.aws_ec2 (the collection-based plugin). The older aws_ec2 plugin from community.aws is deprecated. Always use the FQCN: plugin: amazon.aws.aws_ec2.

How do I filter hosts in dynamic inventory?

Use the filters parameter in your plugin config. For AWS, filters match EC2 API filters. For all plugins, keyed_groups with Jinja2 expressions let you create groups from any host attribute, and groups with conditions include/exclude hosts.

Conclusion

Dynamic inventory is essential for cloud-native Ansible automation. Use inventory plugins (not scripts) for AWS, Azure, GCP, VMware, and Proxmox. Tag your infrastructure consistently, use compose for connection variables, enable caching for performance, and combine multiple inventory sources in a single directory. When no plugin exists for your data source, write a custom script or develop a reusable inventory plugin.

Related Articles

Install Ansible Complete GuideAnsible Documentation Complete GuideAnsible for Proxmox: Complete GuideAnsible VMware Dynamic InventoryAAP 2.6 Cloud Automation: AWS, Azure, GCPAnsible VMware Dynamic InventoryCreate Network Infrastructure on AWSCreating an Azure Virtual NetworkCreating an Azure VM Scale Set

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home