Ansible Inventory
What is Inventory?
The inventory is a list of hosts that Ansible manages. It can be a simple static file or a dynamic script that queries cloud providers or other systems.
Static Inventory - INI Format
# inventory.ini
# Ungrouped hosts
server1.example.com
192.168.1.10
# Grouped hosts
[webservers]
web1.example.com
web2.example.com ansible_host=192.168.1.21
[databases]
db1.example.com ansible_port=2222
db2.example.com
# Group of groups
[production:children]
webservers
databases
# Group variables
[webservers:vars]
ansible_user=deploy
nginx_port=80
[databases:vars]
ansible_user=dbadmin
mysql_port=3306
Static Inventory - YAML Format
# inventory.yml
all:
hosts:
standalone-server:
ansible_host: 192.168.1.5
children:
webservers:
hosts:
web1.example.com:
web2.example.com:
ansible_host: 192.168.1.21
vars:
ansible_user: deploy
nginx_port: 80
databases:
hosts:
db1.example.com:
ansible_port: 2222
db2.example.com:
vars:
ansible_user: dbadmin
mysql_port: 3306
production:
children:
webservers:
databases:
Host and Group Variables
Directory Structure
inventory/
├── hosts
├── group_vars/
│ ├── all.yml
│ ├── webservers.yml
│ └── databases.yml
└── host_vars/
├── web1.example.com.yml
└── db1.example.com.yml
group_vars/all.yml
---
# Variables for all hosts
ntp_server: time.example.com
dns_servers:
- 8.8.8.8
- 8.8.4.4
group_vars/webservers.yml
---
# Variables specific to webservers
nginx_version: 1.20.2
ssl_certificate: /etc/ssl/certs/example.crt
ssl_key: /etc/ssl/private/example.key
host_vars/web1.example.com.yml
---
# Variables specific to web1
server_id: 1
backup_enabled: true
Connection Variables
SSH Connection Variables
[servers]
server1 ansible_host=192.168.1.10
server2 ansible_host=192.168.1.11 ansible_user=admin
server3 ansible_host=192.168.1.12 ansible_user=deploy ansible_port=2222
server4 ansible_host=192.168.1.13 ansible_ssh_private_key_file=~/.ssh/id_rsa_custom
Windows Connection Variables
[windows]
win1 ansible_host=192.168.1.50
win2 ansible_host=192.168.1.51
[windows:vars]
ansible_connection=winrm
ansible_winrm_transport=ntlm
ansible_winrm_server_cert_validation=ignore
ansible_user=Administrator
ansible_password=SecurePass123
ansible_port=5986
Behavioral Inventory Parameters
| Parameter | Description |
|---|---|
ansible_host |
The IP address or hostname to connect to |
ansible_port |
SSH port (default: 22) |
ansible_user |
User to connect as |
ansible_password |
SSH password (use Vault!) |
ansible_ssh_private_key_file |
Private key file path |
ansible_connection |
Connection type (ssh, winrm, local, docker) |
ansible_become |
Equivalent to become: yes |
ansible_become_user |
User to become (default: root) |
ansible_python_interpreter |
Path to Python interpreter |
Inventory Patterns
Targeting Hosts
# All hosts
ansible all -m ping
# Single host
ansible web1.example.com -m ping
# Group
ansible webservers -m ping
# Multiple groups (union)
ansible webservers:databases -m ping
# Intersection (hosts in both groups)
ansible webservers:&databases -m ping
# Exclusion
ansible webservers:!databases -m ping
# Wildcard
ansible web* -m ping
ansible *.example.com -m ping
# Range
ansible web[1:5].example.com -m ping
# Regex
ansible ~web[0-9]+ -m ping
Dynamic Inventory
Dynamic inventory allows you to query external sources for your host list.
AWS EC2 Dynamic Inventory
Install AWS collection:
ansible-galaxy collection install amazon.aws
Create aws_ec2.yml:
---
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
- us-west-2
filters:
tag:Environment: production
keyed_groups:
- key: tags.Name
prefix: tag_name
- key: instance_type
prefix: instance_type
- key: placement.region
prefix: aws_region
hostnames:
- ip-address
- dns-name
- private-ip-address
compose:
ansible_host: public_ip_address
Use it:
ansible-inventory -i aws_ec2.yml --graph
ansible-playbook -i aws_ec2.yml site.yml
Custom Dynamic Inventory Script
#!/usr/bin/env python3
import json
import sys
def get_inventory():
inventory = {
"webservers": {
"hosts": ["web1.example.com", "web2.example.com"],
"vars": {
"ansible_user": "deploy",
"nginx_port": 80
}
},
"databases": {
"hosts": ["db1.example.com"],
"vars": {
"ansible_user": "dbadmin"
}
},
"_meta": {
"hostvars": {
"web1.example.com": {
"server_id": 1
},
"db1.example.com": {
"mysql_port": 3306
}
}
}
}
return inventory
if __name__ == "__main__":
if len(sys.argv) == 2 and sys.argv[1] == '--list':
print(json.dumps(get_inventory()))
elif len(sys.argv) == 3 and sys.argv[1] == '--host':
print(json.dumps({}))
else:
print(json.dumps({}))
Make it executable and use it:
chmod +x dynamic_inventory.py
ansible-playbook -i dynamic_inventory.py site.yml
Inventory Best Practices
- Use groups: Organize hosts logically (by function, environment, location)
- Separate environments: Use different inventory files for dev, staging, production
- Use host_vars and group_vars: Keep inventory file clean
- Secure secrets: Never store passwords in plain text; use Ansible Vault
- Document: Add comments to explain complex inventory structures
- Version control: Keep inventory in git (except secrets)
- Dynamic inventory: Use for cloud environments to auto-discover hosts
- Test patterns: Use
ansible-inventory --listto verify
Verifying Inventory
# List all hosts
ansible-inventory -i inventory.ini --list
# Show inventory graph
ansible-inventory -i inventory.ini --graph
# List hosts in a specific group
ansible-inventory -i inventory.ini --graph webservers
# Show host variables
ansible-inventory -i inventory.ini --host web1.example.com
Advanced Inventory Patterns
Complex Host Patterns
# Multiple patterns with OR
ansible 'webservers:dbservers' -m ping
# Intersection - hosts in BOTH groups
ansible 'webservers:&production' -m ping
# Exclude hosts from pattern
ansible 'all:!development' -m ping
# Combine multiple operators
ansible 'webservers:&production:!maintenance' -m ping
# Range patterns
ansible 'web[01:10].example.com' -m ping
ansible 'db[a:f].example.com' -m ping
# Regex patterns (use ~)
ansible '~web[0-9]+\.prod\..*' -m ping
ansible '~(web|app|api)[0-9]+' -m ping
# Multiple specific hosts
ansible 'web1,web2,db1' -m ping
# Slice pattern (first 5 hosts)
ansible 'webservers[0:5]' -m ping
# Every Nth host
ansible 'webservers[::2]' -m ping # Every other host
# Combination patterns
ansible 'webservers[0:10]:&production:!maintenance' -m ping
Pattern Examples in Playbooks
---
# Run on all hosts except development
- name: Production maintenance
hosts: all:!development
tasks:
- name: Update security patches
yum:
name: '*'
state: latest
security: yes
# Run on intersection of groups
- name: Load balancer backend configuration
hosts: webservers:&production
tasks:
- name: Register with load balancer
uri:
url: http://lb.example.com/api/register
method: POST
# Run on hosts matching pattern
- name: Database backup
hosts: '~db[0-9]+\.prod\..*'
tasks:
- name: Backup databases
mysql_db:
state: dump
name: all
Multi-Cloud Dynamic Inventory
AWS EC2 - Advanced Configuration
---
# aws_ec2.yml
plugin: amazon.aws.aws_ec2
# Multiple regions
regions:
- us-east-1
- us-west-2
- eu-west-1
- ap-southeast-1
# Credentials
aws_profile: production
# OR use environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
# Filter instances
filters:
tag:Environment:
- production
- staging
instance-state-name: running
# Group instances by tags
keyed_groups:
# Group by Environment tag
- key: tags.Environment
prefix: env
separator: '_'
# Group by instance type
- key: instance_type
prefix: type
# Group by availability zone
- key: placement.availability_zone
prefix: az
# Group by VPC
- key: vpc_id
prefix: vpc
# Group by security groups
- key: security_groups
prefix: sg
# Create composite groups
groups:
# Production web servers
prod_web: tags.Environment == 'production' and tags.Role == 'webserver'
# Large instances
large_instances: instance_type.startswith('m5.2x') or instance_type.startswith('m5.4x')
# Multi-AZ setup
multi_az: placement.availability_zone in ['us-east-1a', 'us-east-1b']
# Define hostname to use
hostnames:
- tag:Name
- private-ip-address
- ip-address
# Compose additional variables
compose:
ansible_host: public_ip_address
ansible_user: ec2-user
ec2_instance_id: instance_id
ec2_region: placement.region
ec2_az: placement.availability_zone
ec2_vpc_id: vpc_id
# Include/exclude hosts
include_filters:
- tag:Managed: ansible
exclude_filters:
- tag:DoNotManage: true
# Cache for performance
cache: yes
cache_plugin: jsonfile
cache_timeout: 3600
cache_connection: /tmp/aws_inventory_cache
Azure - Dynamic Inventory
---
# azure_rm.yml
plugin: azure.azcollection.azure_rm
# Authentication
auth_source: auto # Uses Azure CLI, environment vars, or MSI
# Filter by subscription
include_vm_resource_groups:
- production-rg
- staging-rg
# Conditional groups
conditional_groups:
# Group by tags
linux: "'Linux' in image.offer"
windows: "'Windows' in image.offer"
webservers: "'webserver' in tags.role"
databases: "'database' in tags.role"
# Group by properties
keyed_groups:
- key: location
prefix: azure_location
- key: tags.environment
prefix: env
- key: resource_group
prefix: rg
- key: os_profile.computer_name
prefix: host
# Hostname construction
hostvar_expressions:
ansible_host: public_ipv4_addresses[0] if public_ipv4_addresses else private_ipv4_addresses[0]
ansible_user: "'azureuser' if 'Linux' in image.offer else 'Administrator'"
azure_vm_id: id
azure_resource_group: resource_group
azure_location: location
azure_tags: tags
# Performance
use_contrib_script_compatible_sanitization: yes
plain_host_names: yes
Google Cloud Platform (GCP)
---
# gcp_compute.yml
plugin: google.cloud.gcp_compute
# Project and zone
projects:
- my-project-id
- another-project
regions:
- us-central1
- us-east1
- europe-west1
# Filter instances
filters:
- status = RUNNING
- labels.environment = production
# Service account authentication
service_account_file: /path/to/service-account.json
# Grouping
keyed_groups:
# Group by zone
- key: zone
prefix: gcp_zone
# Group by machine type
- key: machineType
prefix: gcp_type
# Group by labels
- key: labels
prefix: label
# Compose variables
compose:
ansible_host: networkInterfaces[0].accessConfigs[0].natIP
ansible_user: labels.ssh_user | default('ubuntu')
gcp_instance_name: name
gcp_zone: zone
gcp_machine_type: machineType
gcp_labels: labels
# Group expressions
groups:
webservers: "'webserver' in labels.role"
production: "labels.environment == 'production'"
VMware vSphere
---
# vmware.yml
plugin: community.vmware.vmware_vm_inventory
hostname: vcenter.example.com
username: "{{ lookup('env', 'VMWARE_USER') }}"
password: "{{ lookup('env', 'VMWARE_PASSWORD') }}"
validate_certs: no
# Filters
with_tags: yes
filters:
- summary.runtime.powerState == "poweredOn"
- "'production' in tag"
# Properties to retrieve
properties:
- name
- config.guestId
- guest.ipAddress
- summary.runtime.powerState
- config.hardware.memoryMB
- config.hardware.numCPU
# Grouping
keyed_groups:
- key: config.guestId
prefix: os
- key: summary.runtime.powerState
prefix: power
- key: config.hardware.numCPU
prefix: cpu
# Host variables
compose:
ansible_host: guest.ipAddress
ansible_user: root
vm_name: name
vm_memory_mb: config.hardware.memoryMB
vm_cpus: config.hardware.numCPU
vm_os: config.guestId
Combining Multiple Inventories
Inventory Directory Structure
inventory/
├── 01-static-hosts.yml # Static hosts
├── 02-aws-ec2.yml # AWS dynamic
├── 03-azure-rm.yml # Azure dynamic
├── 04-gcp-compute.yml # GCP dynamic
├── 05-vmware.yml # VMware dynamic
├── group_vars/
│ ├── all.yml # Global variables
│ ├── production.yml # Production variables
│ ├── webservers.yml # Webserver variables
│ └── databases.yml # Database variables
└── host_vars/
├── web1.example.com.yml
└── db1.example.com.yml
Use entire directory as inventory:
ansible-playbook -i inventory/ site.yml
# Ansible automatically merges all inventory sources
Static + Dynamic Hybrid
# 01-static.yml
---
all:
children:
onprem:
hosts:
onprem-web1:
ansible_host: 10.0.1.10
onprem-db1:
ansible_host: 10.0.1.20
vars:
datacenter: on-premises
backup_location: /backup/onprem
# 02-aws.yml (dynamic)
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
compose:
datacenter: "'aws-' + placement.region"
backup_location: "'s3://backups/' + instance_id"
Constructing Inventory Programmatically
Building Inventory in Playbook
---
- name: Dynamically build inventory
hosts: localhost
gather_facts: no
tasks:
- name: Query database for servers
postgresql_query:
db: cmdb
query: "SELECT hostname, ip, role FROM servers WHERE active = true"
register: db_servers
- name: Add hosts to inventory
add_host:
name: "{{ item.hostname }}"
ansible_host: "{{ item.ip }}"
groups: "{{ item.role }}"
server_source: database
loop: "{{ db_servers.query_result }}"
- name: Query AWS API
ec2_instance_info:
filters:
"tag:Environment": production
instance-state-name: running
register: aws_instances
- name: Add AWS instances
add_host:
name: "{{ item.tags.Name }}"
ansible_host: "{{ item.public_ip_address }}"
groups: aws,production
instance_id: "{{ item.instance_id }}"
server_source: aws
loop: "{{ aws_instances.instances }}"
- name: Query API endpoint
uri:
url: https://api.example.com/servers
headers:
Authorization: "Bearer {{ api_token }}"
register: api_servers
- name: Add API-sourced hosts
add_host:
name: "{{ item.name }}"
ansible_host: "{{ item.address }}"
groups: "{{ item.environment }},{{ item.tier }}"
server_source: api
loop: "{{ api_servers.json.servers }}"
# Now use the dynamically constructed inventory
- name: Configure all discovered servers
hosts: all
tasks:
- name: Show server source
debug:
msg: "Server {{ inventory_hostname }} from {{ server_source }}"
- name: Apply baseline configuration
include_role:
name: baseline
Advanced Dynamic Inventory Script
#!/usr/bin/env python3
"""
Advanced dynamic inventory with multiple sources
"""
import json
import requests
import boto3
from azure.identity import DefaultAzureCredential
from azure.mgmt.compute import ComputeManagementClient
class MultiCloudInventory:
def __init__(self):
self.inventory = {
'_meta': {
'hostvars': {}
}
}
def add_group(self, group_name, hosts=None, vars=None, children=None):
"""Add a group to inventory"""
if group_name not in self.inventory:
self.inventory[group_name] = {}
if hosts:
self.inventory[group_name]['hosts'] = hosts
if vars:
self.inventory[group_name]['vars'] = vars
if children:
self.inventory[group_name]['children'] = children
def add_host(self, hostname, group, hostvars=None):
"""Add a host to a group with variables"""
if group not in self.inventory:
self.add_group(group, hosts=[])
if 'hosts' not in self.inventory[group]:
self.inventory[group]['hosts'] = []
self.inventory[group]['hosts'].append(hostname)
if hostvars:
self.inventory['_meta']['hostvars'][hostname] = hostvars
def get_aws_instances(self):
"""Fetch AWS EC2 instances"""
ec2 = boto3.client('ec2', region_name='us-east-1')
response = ec2.describe_instances(
Filters=[
{'Name': 'instance-state-name', 'Values': ['running']},
{'Name': 'tag:Managed', 'Values': ['ansible']}
]
)
for reservation in response['Reservations']:
for instance in reservation['Instances']:
hostname = self.get_tag(instance, 'Name', instance['InstanceId'])
environment = self.get_tag(instance, 'Environment', 'unknown')
role = self.get_tag(instance, 'Role', 'unknown')
# Add to multiple groups
self.add_host(
hostname,
'aws',
hostvars={
'ansible_host': instance.get('PublicIpAddress', instance['PrivateIpAddress']),
'ansible_user': 'ec2-user',
'instance_id': instance['InstanceId'],
'instance_type': instance['InstanceType'],
'cloud_provider': 'aws',
'region': instance['Placement']['AvailabilityZone'][:-1]
}
)
# Add to environment and role groups
self.add_host(hostname, f'env_{environment}')
self.add_host(hostname, f'role_{role}')
def get_azure_vms(self):
"""Fetch Azure VMs"""
credential = DefaultAzureCredential()
subscription_id = "your-subscription-id"
compute_client = ComputeManagementClient(credential, subscription_id)
for vm in compute_client.virtual_machines.list_all():
tags = vm.tags or {}
self.add_host(
vm.name,
'azure',
hostvars={
'ansible_host': self.get_azure_ip(vm),
'ansible_user': 'azureuser',
'vm_size': vm.hardware_profile.vm_size,
'cloud_provider': 'azure',
'location': vm.location,
'resource_group': vm.id.split('/')[4]
}
)
# Group by tags
if 'environment' in tags:
self.add_host(vm.name, f"env_{tags['environment']}")
if 'role' in tags:
self.add_host(vm.name, f"role_{tags['role']}")
def get_api_servers(self):
"""Fetch from custom API"""
response = requests.get('https://api.example.com/servers')
servers = response.json()
for server in servers:
self.add_host(
server['hostname'],
'api_managed',
hostvars={
'ansible_host': server['ip_address'],
'ansible_user': server.get('ssh_user', 'root'),
'server_id': server['id'],
'datacenter': server.get('datacenter', 'unknown')
}
)
# Add to environment group
if 'environment' in server:
self.add_host(server['hostname'], server['environment'])
def get_tag(self, instance, key, default=''):
"""Get tag value from AWS instance"""
for tag in instance.get('Tags', []):
if tag['Key'] == key:
return tag['Value']
return default
def get_azure_ip(self, vm):
"""Get Azure VM IP address"""
# Simplified - would need to query network interfaces
return "0.0.0.0"
def generate(self):
"""Generate complete inventory"""
# Fetch from all sources
self.get_aws_instances()
self.get_azure_vms()
self.get_api_servers()
# Create meta groups
self.add_group('cloud', children=['aws', 'azure'])
self.add_group('production', children=['env_production'])
self.add_group('webservers', children=['role_webserver'])
return self.inventory
if __name__ == '__main__':
import sys
inventory = MultiCloudInventory()
if len(sys.argv) == 2 and sys.argv[1] == '--list':
print(json.dumps(inventory.generate(), indent=2))
elif len(sys.argv) == 3 and sys.argv[1] == '--host':
# Return empty dict - hostvars in _meta
print(json.dumps({}))
else:
print(json.dumps({}))
Inventory Plugins
Custom Inventory Plugin
# plugins/inventory/custom_cmdb.py
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable
DOCUMENTATION = '''
name: custom_cmdb
plugin_type: inventory
short_description: Custom CMDB inventory source
description: Fetches inventory from internal CMDB
options:
plugin:
description: Name of plugin
required: true
cmdb_url:
description: CMDB API endpoint
required: true
api_token:
description: API authentication token
required: true
'''
class InventoryModule(BaseInventoryPlugin, Constructable):
NAME = 'custom_cmdb'
def verify_file(self, path):
"""Verify inventory file"""
if super(InventoryModule, self).verify_file(path):
return path.endswith(('cmdb.yml', 'cmdb.yaml'))
return False
def parse(self, inventory, loader, path, cache=True):
"""Parse inventory"""
super(InventoryModule, self).parse(inventory, loader, path, cache)
# Read configuration
self._read_config_data(path)
cmdb_url = self.get_option('cmdb_url')
api_token = self.get_option('api_token')
# Fetch data from CMDB
import requests
headers = {'Authorization': f'Bearer {api_token}'}
response = requests.get(f'{cmdb_url}/servers', headers=headers)
servers = response.json()
# Process servers
for server in servers:
hostname = server['hostname']
# Add host
self.inventory.add_host(hostname)
# Set variables
self.inventory.set_variable(hostname, 'ansible_host', server['ip'])
self.inventory.set_variable(hostname, 'ansible_user', server.get('user', 'root'))
# Add to groups
for group in server.get('groups', []):
self.inventory.add_group(group)
self.inventory.add_child(group, hostname)
# Construct composed variables
strict = self.get_option('strict')
self._set_composite_vars(
self.get_option('compose'),
self.inventory.get_host(hostname).get_vars(),
hostname,
strict
)
# Add to keyed groups
self._add_host_to_keyed_groups(
self.get_option('keyed_groups'),
server,
hostname,
strict
)
Usage configuration:
# cmdb.yml
plugin: custom_cmdb
cmdb_url: https://cmdb.example.com/api
api_token: your-api-token-here
compose:
datacenter: server_location
backup_enabled: backup_flag
keyed_groups:
- key: environment
prefix: env
- key: application
prefix: app
Performance Optimization
Caching Dynamic Inventory
# ansible.cfg
[inventory]
cache = yes
cache_plugin = jsonfile
cache_timeout = 3600
cache_connection = /tmp/ansible_inventory_cache
cache_prefix = ansible_inventory
# Or use Redis for distributed caching
# cache_plugin = redis
# cache_connection = redis://localhost:6379/0
Parallel Inventory Loading
[inventory]
# Enable inventory plugins
enable_plugins = host_list, script, auto, yaml, ini, toml, amazon.aws.aws_ec2, azure.azcollection.azure_rm
# Inventory parsing
inventory_unparsed_warning = False
# Performance
inventory_cache_enabled = True
fact_caching = redis
fact_caching_connection = localhost:6379:0
fact_caching_timeout = 86400
Advanced Inventory Techniques
Inventory Variables from External Sources
---
# group_vars/all.yml
# Fetch variables from HashiCorp Vault
db_password: "{{ lookup('hashi_vault', 'secret=secret/data/db:password') }}"
api_key: "{{ lookup('hashi_vault', 'secret=secret/data/api:key') }}"
# Fetch from AWS Secrets Manager
aws_secret: "{{ lookup('aws_secret', 'production/database', region='us-east-1') }}"
# Fetch from environment
proxy_host: "{{ lookup('env', 'HTTP_PROXY') }}"
# Fetch from file
ssh_key: "{{ lookup('file', '/path/to/key.pem') }}"
# Fetch from AWS Parameter Store
parameter: "{{ lookup('aws_ssm', '/production/config/param1', region='us-east-1') }}"
Conditional Inventory
---
# inventory/production.yml
all:
children:
webservers:
hosts:
web[01:10].prod.example.com:
vars:
ansible_user: deploy
max_connections: 1000
databases:
hosts:
db[01:03].prod.example.com:
vars:
ansible_user: dbadmin
# Only include maintenance group during maintenance window
maintenance:
hosts: "{{ groups['all'] if maintenance_mode | default(false) else [] }}"
Real-World Inventory Examples
Multi-Tier Application
---
all:
children:
# Load Balancers
loadbalancers:
hosts:
lb01.example.com:
lb_priority: primary
lb02.example.com:
lb_priority: secondary
vars:
ansible_user: lbadmin
haproxy_version: 2.4
# Web Tier
webservers:
children:
frontend:
hosts:
web[01:10].example.com:
vars:
nginx_worker_processes: 4
nginx_worker_connections: 2048
api:
hosts:
api[01:05].example.com:
vars:
app_workers: 8
app_threads: 2
vars:
ansible_user: webapp
app_version: 2.1.0
# Application Tier
appservers:
hosts:
app[01:20].example.com:
vars:
ansible_user: appuser
jvm_memory: 4096m
# Database Tier
databases:
children:
primary:
hosts:
db-master.example.com:
vars:
mysql_role: master
mysql_replicate: yes
replicas:
hosts:
db-replica[01:02].example.com:
vars:
mysql_role: replica
mysql_read_only: yes
vars:
ansible_user: dbadmin
mysql_version: 8.0
# Cache Tier
cache:
hosts:
redis[01:03].example.com:
vars:
ansible_user: redis
redis_maxmemory: 8gb
redis_cluster_enabled: yes
# Message Queue
messagequeue:
hosts:
mq[01:03].example.com:
vars:
ansible_user: mqadmin
rabbitmq_cluster: yes
# Monitoring
monitoring:
hosts:
mon01.example.com:
vars:
ansible_user: monitor
prometheus_retention: 30d
# Environment-based grouping
production:
children:
- loadbalancers
- webservers
- appservers
- databases
- cache
- messagequeue
vars:
environment: production
backup_enabled: yes
monitoring_enabled: yes
Troubleshooting Inventory
Debug Inventory Issues
# Verbose inventory parsing
ANSIBLE_DEBUG=1 ansible-inventory -i inventory --list
# Check specific host
ansible-inventory -i inventory --host web01 --yaml
# Validate inventory syntax
ansible-inventory -i inventory --list > /dev/null && echo "OK" || echo "ERROR"
# Show inventory variables for debugging
ansible all -i inventory -m debug -a "var=hostvars[inventory_hostname]"
# Test connectivity
ansible all -i inventory -m ping -vvv
# Check group membership
ansible-inventory -i inventory --graph --vars
# Export inventory to JSON for analysis
ansible-inventory -i inventory --list --export > inventory.json
Best Practices
- Use groups: Organize hosts logically (by function, environment, location)
- Separate environments: Use different inventory files for dev, staging, production
- Use host_vars and group_vars: Keep inventory file clean and maintainable
- Secure secrets: Never store passwords in plain text; use Ansible Vault
- Document: Add comments to explain complex inventory structures
- Version control: Keep inventory in git (except secrets)
- Dynamic inventory: Use for cloud environments to auto-discover hosts
- Test patterns: Use
ansible-inventory --listto verify - Cache aggressively: Enable caching for dynamic inventories
- Merge sources: Combine static and dynamic inventories in a directory
- Use inventory plugins: More powerful than scripts
- Leverage compose: Create computed variables in dynamic inventory
- Group strategically: By function, environment, location, and provider
- Avoid duplication: Use group_vars and inheritance
- Monitor performance: Large inventories can slow down playbooks
Next Steps
- Learn about Playbooks to use your inventory
- Master Ansible Vault for securing inventory credentials
- Explore Cloud Automation for provider-specific inventory
- Try inventory patterns in the Playground