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 Inventory: Complete Guide to INI, YAML & Dynamic Inventory

By Luca Berton · Published 2026-04-03 · Category: windows-automation

Complete guide to Ansible inventory. Create INI and YAML inventories, use groups, variables, dynamic inventory plugins, and manage multi-environment setups.

The Ansible inventory defines the hosts and groups of hosts that Ansible manages. It's the foundation of every Ansible deployment — without it, Ansible doesn't know what to automate.

What is an Ansible Inventory?

An inventory is a file (or script, or plugin) that lists your managed hosts. It can be as simple as a flat list of hostnames or as complex as a dynamic cloud inventory pulling from AWS, Azure, or GCP.

Default location: /etc/ansible/hosts Custom location: ansible-playbook -i inventory.yml site.yml

See also: Installing Ansible: A Step-by-Step Guide

INI Format (Traditional)

# Simple host list
web1.example.com
web2.example.com
db1.example.com

# Groups [webservers] web1.example.com web2.example.com

[databases] db1.example.com db2.example.com

# Group of groups [production:children] webservers databases

# Group variables [webservers:vars] http_port=80 ansible_user=deploy

# Host variables web1.example.com ansible_host=192.168.1.10 ansible_port=2222

all:
  children:
    webservers:
      hosts:
        web1.example.com:
          ansible_host: 192.168.1.10
          http_port: 80
        web2.example.com:
          ansible_host: 192.168.1.11
          http_port: 8080
      vars:
        ansible_user: deploy
        ansible_become: true
    databases:
      hosts:
        db1.example.com:
          ansible_host: 192.168.1.20
        db2.example.com:
          ansible_host: 192.168.1.21
      vars:
        ansible_user: dbadmin
    production:
      children:
        webservers:
        databases:

See also: Ansible Inventory: Static, Dynamic & Advanced Patterns (Complete Guide)

Host and Group Variables

Inline variables

[webservers]
web1 ansible_host=10.0.1.1 ansible_user=ubuntu app_env=production

Separate variable files (recommended)

inventory/
├── hosts.yml
├── group_vars/
│   ├── all.yml          # Variables for all hosts
│   ├── webservers.yml   # Variables for webservers group
│   └── databases.yml    # Variables for databases group
└── host_vars/
    ├── web1.yml         # Variables for web1 only
    └── db1.yml          # Variables for db1 only
# group_vars/webservers.yml
ansible_user: deploy
http_port: 80
app_version: "2.5.1"
nginx_worker_processes: auto

Connection Variables

| Variable | Description | Example | |----------|-------------|---------| | ansible_host | IP or hostname to connect to | 192.168.1.10 | | ansible_port | SSH port | 2222 | | ansible_user | SSH username | deploy | | ansible_ssh_private_key_file | SSH key path | ~/.ssh/deploy_key | | ansible_connection | Connection type | ssh, winrm, local | | ansible_become | Enable privilege escalation | true | | ansible_python_interpreter | Python path on remote | /usr/bin/python3 |

See also: Automate Text Capitalization with Ansible Playbooks

Inventory Patterns

# All hosts
ansible all -m ping

# Specific group ansible webservers -m ping

# Multiple groups (OR) ansible 'webservers:databases' -m ping

# Intersection (AND) ansible 'webservers:&production' -m ping

# Exclusion (NOT) ansible 'all:!databases' -m ping

# Wildcards ansible 'web*' -m ping

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

# Index ansible 'webservers[0]' -m ping # First host ansible 'webservers[0:2]' -m ping # First three hosts

Dynamic Inventory

AWS EC2

# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
  - us-east-1
  - eu-west-1
filters:
  tag:Environment: production
keyed_groups:
  - key: tags.Role
    prefix: role
  - key: placement.region
    prefix: aws_region
compose:
  ansible_host: private_ip_address

Azure

# azure_rm.yml
plugin: azure.azcollection.azure_rm
auth_source: auto
include_vm_resource_groups:
  - myResourceGroup
keyed_groups:
  - prefix: tag
    key: tags

Custom Script

#!/usr/bin/env python3
import json

inventory = { "webservers": { "hosts": ["web1.example.com", "web2.example.com"], "vars": {"http_port": 80} }, "_meta": { "hostvars": { "web1.example.com": {"ansible_host": "10.0.1.1"}, "web2.example.com": {"ansible_host": "10.0.1.2"} } } } print(json.dumps(inventory))

Multiple Inventory Sources

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

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

Verify Your Inventory

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

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

# Show specific host vars ansible-inventory -i inventory.yml --host web1

Best Practices

Use YAML format — more readable and supports complex structures Use group_vars/ and host_vars/ — separate variables from host definitions Keep sensitive data in Ansible Vault — encrypt passwords and keys Use meaningful group names — reflect function, not location Layer groups[production:children] for logical grouping Use dynamic inventory for cloud — static files become outdated Version control your inventory — track changes over time

FAQ

Where does Ansible look for the inventory file?

By default: /etc/ansible/hosts. Override with -i flag or inventory in ansible.cfg.

Can I use both static and dynamic inventory?

Yes. Put both files in an inventory directory and pass the directory with -i inventory/.

What's the difference between ansible_host and the hostname in inventory?

The inventory name is a label. ansible_host is the actual IP/hostname Ansible connects to. They can be different.

How do I test if my inventory is working?

Run: ansible all -i inventory.yml -m ping

YAML Inventory

# inventory.yml
all:
  children:
    webservers:
      hosts:
        web1:
          ansible_host: 192.168.1.10
        web2:
          ansible_host: 192.168.1.11
      vars:
        http_port: 80

dbservers: hosts: db1: ansible_host: 192.168.1.20 ansible_user: postgres

production: children: webservers: dbservers:

INI Inventory

[webservers]
web1 ansible_host=192.168.1.10
web2 ansible_host=192.168.1.11

[dbservers] db1 ansible_host=192.168.1.20

[production:children] webservers dbservers

[webservers:vars] http_port=80

Host Variables

# host_vars/web1.yml
http_port: 8080
ssl_enabled: true
custom_domains:
  - example.com
  - www.example.com

Group Variables

# group_vars/webservers.yml
ansible_user: deploy
http_port: 80
document_root: /var/www/html

# group_vars/all.yml (applies to everything) ansible_python_interpreter: /usr/bin/python3 ntp_server: time.google.com

Patterns (Target Hosts)

# All hosts
ansible all -m ping

# Specific group ansible webservers -m ping

# Multiple groups ansible 'webservers:dbservers' -m ping

# Intersection (in BOTH groups) ansible 'webservers:&production' -m ping

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

# Specific host ansible web1 -m ping

# Wildcard ansible 'web*' -m ping

# Regex ansible '~web[0-9]+' -m ping

Ranges

webservers:
  hosts:
    web[1:10]:
      ansible_host: 192.168.1.[10:19]
# Creates web1 through web10

Dynamic Inventory

# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions: [us-east-1]
keyed_groups:
  - key: tags.Environment
    prefix: env
filters:
  instance-state-name: running
ansible-inventory -i aws_ec2.yml --list
ansible -i aws_ec2.yml env_production -m ping

Multiple Inventory Sources

ansible-playbook -i inventory/production -i inventory/staging site.yml
# ansible.cfg
[defaults]
inventory = inventory/production,inventory/staging

Verify Inventory

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

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

# Show specific host vars ansible-inventory -i inventory.yml --host web1

Special Variables

| Variable | Description | |----------|-------------| | inventory_hostname | Host name as in inventory | | ansible_host | Connection address | | ansible_port | SSH port (default 22) | | ansible_user | SSH user | | ansible_connection | Connection type (ssh/local/winrm) | | ansible_python_interpreter | Python path on remote | | groups | Dict of all groups | | group_names | Groups current host belongs to |

FAQ

YAML vs INI — which should I use?

YAML is recommended. It handles complex nested structures, lists, and dictionaries better than INI format.

How do I test inventory without running a playbook?

ansible-inventory --list -i inventory.yml
ansible all -i inventory.yml -m ping

Can I combine static and dynamic inventory?

Yes — point to a directory containing both static files and dynamic inventory scripts/plugins.

INI Format

# inventory/hosts
[webservers]
web1 ansible_host=10.0.1.10
web2 ansible_host=10.0.1.11

[dbservers] db1 ansible_host=10.0.2.10

[webservers:vars] http_port=80 ansible_user=deploy

[production:children] webservers dbservers

YAML Format

# inventory/hosts.yml
all:
  children:
    webservers:
      hosts:
        web1:
          ansible_host: 10.0.1.10
        web2:
          ansible_host: 10.0.1.11
      vars:
        http_port: 80
    dbservers:
      hosts:
        db1:
          ansible_host: 10.0.2.10
    production:
      children:
        webservers:
        dbservers:

Common Variables

[all:vars]
ansible_user=deploy
ansible_ssh_private_key_file=~/.ssh/deploy_key
ansible_python_interpreter=/usr/bin/python3

[windows:vars] ansible_connection=winrm ansible_winrm_transport=ntlm ansible_port=5986

Patterns

# All hosts
ansible all -m ping

# Specific group ansible webservers -m ping

# Multiple groups ansible 'webservers:dbservers' -m ping

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

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

# Wildcard ansible 'web*' -m ping

# Regex ansible '~web[0-9]+' -m ping

Ranges

[webservers]
web[01:50].example.com    # web01 through web50
db-[a:f].example.com      # db-a through db-f

group_vars / host_vars

inventory/
├── hosts
├── group_vars/
│   ├── all.yml
│   ├── webservers.yml
│   └── production.yml
└── host_vars/
    ├── web1.yml
    └── db1.yml

Dynamic Inventory (AWS)

# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions: [us-east-1]
filters:
  tag:Environment: production
keyed_groups:
  - key: tags.Role
    prefix: role
compose:
  ansible_host: public_ip_address
ansible-inventory -i aws_ec2.yml --graph

Multiple Inventories

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

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

Verify Inventory

# List all hosts
ansible-inventory --list

# Show graph ansible-inventory --graph

# Show host variables ansible-inventory --host web1

FAQ

INI vs YAML format?

INI is simpler for small inventories. YAML is better for complex, nested structures. Both work identically.

Can I mix static and dynamic inventory?

Yes — put both in the same directory and Ansible merges them.

How to test connectivity?

ansible all -m ping -i inventory/hosts

INI Format

[webservers]
web1 ansible_host=192.168.1.10
web2 ansible_host=192.168.1.11

[dbservers] db1 ansible_host=192.168.1.20

[all:vars] ansible_user=deploy ansible_python_interpreter=/usr/bin/python3

[production:children] webservers dbservers

YAML Format

all:
  children:
    webservers:
      hosts:
        web1:
          ansible_host: 192.168.1.10
        web2:
          ansible_host: 192.168.1.11
    dbservers:
      hosts:
        db1:
          ansible_host: 192.168.1.20
  vars:
    ansible_user: deploy

Host Ranges

[webservers]
web[1:10].example.com     # web1 through web10
db-[a:c].example.com      # db-a, db-b, db-c
192.168.1.[10:20]         # IP range

Group Variables

inventory/
├── hosts.yml
├── group_vars/
│   ├── all.yml
│   ├── webservers.yml
│   └── production.yml
└── host_vars/
    ├── web1.yml
    └── db1.yml

Dynamic Inventory (AWS)

# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
  - us-east-1
keyed_groups:
  - key: tags.Environment
    prefix: env
filters:
  instance-state-name: running
compose:
  ansible_host: public_ip_address

Multi-Environment

inventory/
├── production/
│   ├── hosts.yml
│   └── group_vars/
└── staging/
    ├── hosts.yml
    └── group_vars/
ansible-playbook -i inventory/production site.yml
ansible-playbook -i inventory/staging site.yml

List and Debug

ansible-inventory -i hosts.yml --list
ansible-inventory -i hosts.yml --graph
ansible all -i hosts.yml -m ping

Implicit localhost

# localhost is always available
- hosts: localhost
  connection: local
  tasks:
    - debug: msg="Running locally"

FAQ

INI vs YAML format?

YAML is more flexible for complex structures. INI is simpler for small setups. Both work equally well.

How to use multiple inventory sources?

ansible-playbook -i inv1.yml -i inv2.yml site.yml
# Or set in ansible.cfg
# inventory = inventory/static,inventory/dynamic

Variable precedence in inventory?

host_vars > group_vars (child group) > group_vars (parent group) > group_vars/all.

Related Articles

Ansible Vault best practicesNginx hardening via Ansibleprivilege escalation with Ansible becomeRoute53 with Ansible

Category: windows-automation

Browse all Ansible tutorials · AnsiblePilot Home