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
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

VariableDescriptionExample
ansible_hostIP or hostname to connect to192.168.1.10
ansible_portSSH port2222
ansible_userSSH usernamedeploy
ansible_ssh_private_key_fileSSH key path~/.ssh/deploy_key
ansible_connectionConnection typessh, winrm, local
ansible_becomeEnable privilege escalationtrue
ansible_python_interpreterPython path on remote/usr/bin/python3

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

See also: Automate Text Capitalization with Ansible Playbooks

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

  1. Use YAML format — more readable and supports complex structures
  2. Use group_vars/ and host_vars/ — separate variables from host definitions
  3. Keep sensitive data in Ansible Vault — encrypt passwords and keys
  4. Use meaningful group names — reflect function, not location
  5. Layer groups[production:children] for logical grouping
  6. Use dynamic inventory for cloud — static files become outdated
  7. 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

VariableDescription
inventory_hostnameHost name as in inventory
ansible_hostConnection address
ansible_portSSH port (default 22)
ansible_userSSH user
ansible_connectionConnection type (ssh/local/winrm)
ansible_python_interpreterPython path on remote
groupsDict of all groups
group_namesGroups 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.

Category: windows-automation

Browse all Ansible tutorials · AnsiblePilot Home