Introduction

The inventory is Ansible's source of truth — it defines which hosts to manage, how to group them, and what variables apply. Whether you use a simple INI file with 5 servers or dynamic inventory pulling thousands of hosts from AWS, understanding inventory is fundamental to every Ansible workflow.

INI Format (Simple)

# inventory.ini
[webservers]
web1.example.com
web2.example.com
web3.example.com

[dbservers]
db1.example.com
db2.example.com

[loadbalancers]
lb1.example.com ansible_host=192.168.1.10

# Group of groups
[production:children]
webservers
dbservers
loadbalancers

# Group variables
[webservers:vars]
http_port=80
ansible_user=deploy
# inventory.yml
all:
  children:
    webservers:
      hosts:
        web1.example.com:
        web2.example.com:
        web3.example.com:
      vars:
        http_port: 80
        ansible_user: deploy

    dbservers:
      hosts:
        db1.example.com:
          postgresql_port: 5432
        db2.example.com:
          postgresql_port: 5433

    loadbalancers:
      hosts:
        lb1.example.com:
          ansible_host: 192.168.1.10

    production:
      children:
        webservers:
        dbservers:
        loadbalancers:

Host and Group Variables

Inline Variables

all:
  hosts:
    web1.example.com:
      ansible_host: 192.168.1.101
      ansible_port: 2222
      ansible_user: admin
      ansible_python_interpreter: /usr/bin/python3

Variable Files (Best Practice)

inventory/
├── hosts.yml
├── group_vars/
│   ├── all.yml           # Applied to all hosts
│   ├── webservers.yml    # Applied to webservers group
│   └── production.yml    # Applied to production group
└── host_vars/
    ├── web1.example.com.yml
    └── db1.example.com.yml
# group_vars/webservers.yml
http_port: 80
ssl_cert_path: /etc/ssl/certs/site.pem
max_connections: 1000
deploy_user: www-data
# host_vars/db1.example.com.yml
postgresql_version: "16"
postgresql_max_connections: 200
backup_schedule: "0 2 * * *"

Variable Precedence

1. command line -e (highest)
2. host_vars/hostname.yml
3. group_vars/child_group.yml
4. group_vars/parent_group.yml
5. group_vars/all.yml (lowest)

Host Patterns

# All hosts
ansible all -m ping

# Specific group
ansible webservers -m ping

# Single host
ansible web1.example.com -m ping

# Multiple groups (union)
ansible 'webservers:dbservers' -m ping

# Intersection
ansible 'webservers:&production' -m ping

# Exclusion
ansible 'all:!loadbalancers' -m ping

# Wildcard
ansible 'web*' -m ping

# Regex
ansible '~web[0-9]+\.example\.com' -m ping

# Numeric range
ansible 'web[1:3].example.com' -m ping
# Matches web1, web2, web3

# Limit at runtime
ansible-playbook site.yml --limit web1.example.com
ansible-playbook site.yml --limit 'webservers:!web3.example.com'

Dynamic Inventory

AWS EC2

# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
  - us-east-1
  - eu-west-1

filters:
  tag:Environment:
    - production
  instance-state-name: running

keyed_groups:
  - key: tags.Role
    prefix: role
  - key: placement.region
    prefix: region
  - key: instance_type
    prefix: type

compose:
  ansible_host: public_ip_address
  instance_name: tags.Name
# Use dynamic inventory
ansible-inventory -i aws_ec2.yml --graph
ansible-playbook -i aws_ec2.yml site.yml

Azure

# azure_rm.yml
plugin: azure.azcollection.azure_rm
auth_source: auto
include_vm_resource_groups:
  - production-rg
  - staging-rg

keyed_groups:
  - prefix: tag
    key: tags

GCP

# gcp_compute.yml
plugin: google.cloud.gcp_compute
projects:
  - my-project-id
zones:
  - us-central1-a
filters:
  - status = RUNNING

keyed_groups:
  - key: labels.env
    prefix: env

ServiceNow CMDB

# snow_inventory.yml
plugin: servicenow.itsm.now
table: cmdb_ci_linux_server
query:
  - operational_status: = operational
compose:
  ansible_host: ip_address

Multiple Inventory Sources

# Use multiple inventory files
ansible-playbook -i inventory/production -i inventory/staging site.yml

# Or use a directory
ansible-playbook -i inventory/ site.yml
# ansible.cfg
[defaults]
inventory = inventory/production,inventory/cloud

Multi-Environment Setup

inventory/
├── production/
│   ├── hosts.yml
│   ├── group_vars/
│   │   └── all.yml
│   └── host_vars/
├── staging/
│   ├── hosts.yml
│   ├── group_vars/
│   │   └── all.yml
│   └── host_vars/
└── development/
    ├── hosts.yml
    └── group_vars/
        └── all.yml
# Deploy to staging
ansible-playbook -i inventory/staging site.yml

# Deploy to production
ansible-playbook -i inventory/production site.yml

Inventory Plugins

# Combine static and dynamic
# inventory/combined.yml
plugin: ansible.builtin.constructed
strict: false
groups:
  webservers: "'web' in inventory_hostname"
  large_instances: "ansible_memtotal_mb > 16000"
compose:
  ansible_connection: "'local' if inventory_hostname == 'localhost' else 'ssh'"

Verify Inventory

# List all hosts
ansible-inventory -i inventory.yml --list

# Show graph
ansible-inventory -i inventory.yml --graph

# Show specific host variables
ansible-inventory -i inventory.yml --host web1.example.com

# Verify connectivity
ansible all -i inventory.yml -m ping

Inventory Best Practices

# ✅ Use YAML format
# ✅ Use group_vars/ and host_vars/ directories
# ✅ Use meaningful group names
# ✅ Encrypt sensitive vars with Ansible Vault
# ✅ Use dynamic inventory for cloud
# ✅ Keep inventory in version control

# ❌ Don't put passwords in inventory files
# ❌ Don't use IP addresses as hostnames (use DNS)
# ❌ Don't create groups with single hosts (unless meaningful)
# ❌ Don't duplicate variables across groups

Complete Example

# inventory/production/hosts.yml
all:
  children:
    webservers:
      hosts:
        web-[01:03].prod.example.com:
    dbservers:
      hosts:
        db-01.prod.example.com:
          role: primary
        db-02.prod.example.com:
          role: replica
    monitoring:
      hosts:
        mon-01.prod.example.com:
    production:
      children:
        webservers:
        dbservers:
        monitoring:
      vars:
        env: production
        ansible_user: deploy
        ansible_become: true
        ntp_server: time.prod.example.com

Conclusion

Ansible inventory ranges from simple INI files to multi-cloud dynamic discovery. Master these patterns: use YAML format for readability, group_vars/ and host_vars/ directories for clean variable management, dynamic inventory plugins for cloud environments, and host patterns for flexible targeting. For large environments, combine static and dynamic sources, separate by environment, and use the constructed plugin for computed groups. The inventory is the foundation of everything Ansible does — invest time getting it right.