Ansible Lesson 7 – Inventory Files Explained | Dataplexa
Section I · Lesson 7

Inventory Files Explained

In this lesson

Static inventory INI vs YAML format Groups & children Host & group variables Dynamic inventory

An inventory is the file — or dynamic data source — that tells Ansible which hosts to manage and how to connect to them. Every Ansible command needs an inventory: without one, Ansible has no targets to run against. Inventories can be as simple as a single IP address in a text file, or as sophisticated as a live query against a cloud provider's API that returns thousands of servers grouped by region, environment, and role. Mastering inventory is essential because it is the bridge between your automation code and your real infrastructure.

Static Inventory Formats

A static inventory is a file you write and maintain by hand. Ansible supports two formats: INI and YAML. Both express the same information — they are purely a matter of preference and team convention. INI is more compact; YAML is more explicit and integrates naturally with the rest of your Ansible project.

INI format
Compact, minimal syntax
Familiar to anyone who has used .ini config files
Groups defined with [groupname] headers
Best for small, simple inventories
YAML format
More verbose but highly readable
Consistent with playbook and variable file syntax
Nested groups and variables are easier to express clearly
Best for larger or more complex inventories

INI inventory — inventory.ini

# Ungrouped hosts — reachable by Ansible but not in any named group
192.168.1.5

# A group called webservers
[webservers]
web01.example.com
web02.example.com ansible_port=2222   # override SSH port for this host only

# A group called databases
[databases]
db01.example.com ansible_user=dbadmin
db02.example.com

# A group of groups — 'production' contains both webservers and databases
[production:children]
webservers
databases

# Variables that apply to every host in the webservers group
[webservers:vars]
http_port=80
nginx_version=1.25

YAML inventory — inventory.yml

# The top-level key 'all' is mandatory — it is the implicit parent of every group
all:
  children:

    webservers:
      hosts:
        web01.example.com:
        web02.example.com:
          ansible_port: 2222    # override SSH port for this host only
      vars:
        http_port: 80
        nginx_version: "1.25"

    databases:
      hosts:
        db01.example.com:
        db02.example.com:
      vars:
        db_port: 5432

    production:
      children:
        webservers:
        databases:

The Contacts App Analogy

An Ansible inventory is like a contacts app for your infrastructure. Each host is a contact — it has a name (hostname or IP), connection details (SSH user, port, key), and belongs to one or more groups (like "Work" or "Family"). When you run a playbook against the "webservers" group, it is the same as sending a message to everyone in a contact group — Ansible loops through every member automatically.

Groups, Children, and the all Group

Every inventory has two implicit groups that always exist: all — which contains every host in the inventory — and ungrouped — which contains hosts not assigned to any named group. Beyond these, you define your own groups to organise hosts by role, environment, location, or any other dimension that matters to your automation.

all every host in inventory production group of groups ungrouped hosts with no group webservers web01, web02 databases db01, db02 192.168.1.5

A group can contain hosts directly or contain other groups as children. A host can belong to multiple groups simultaneously. When you run a playbook against production, Ansible resolves the full member list — including all hosts in webservers and databases — automatically.

Host Variables and Group Variables

Variables can be attached directly to hosts or groups inside the inventory file — or, for larger projects, stored in dedicated host_vars/ and group_vars/ directories. This separation keeps your inventory clean and your variables easy to find.

Variable scope and where to define them

Inline host vars Defined on the same line as the host in the inventory file. Good for connection-specific settings like ansible_user, ansible_port, and ansible_host. Applies to that host only.
Inline group vars Defined in a [groupname:vars] section (INI) or under vars: in a group (YAML). Applies to every host in that group. Useful for group-wide settings like port numbers or package versions.
host_vars/ directory A folder in your project root. Create a file named after each host — host_vars/web01.yml. Ansible loads it automatically. Best for hosts with many variables.
group_vars/ directory A folder in your project root. Create a file named after each group — group_vars/webservers.yml. Ansible loads it automatically for every host in that group. The recommended approach for all but the simplest projects.

Recommended structure — group_vars/ directory

# Project directory layout — variables live outside the inventory file
myproject/
├── ansible.cfg
├── inventory.ini
├── site.yml
├── group_vars/
│   ├── all.yml          # variables that apply to every host
│   ├── webservers.yml   # variables for the webservers group only
│   └── databases.yml    # variables for the databases group only
└── host_vars/
    └── web01.yml        # variables for web01 only (overrides group_vars)

Special Connection Variables

Ansible has a set of reserved variable names — called magic variables — that control how it connects to each host. These can be set inline in the inventory or in host_vars/ files to override the global defaults in ansible.cfg.

Connection

ansible_host

The actual IP or hostname to connect to — useful when the inventory name is an alias that differs from the real address.

Connection

ansible_port

Overrides the SSH port for this host. Default is 22. Use when a server listens on a non-standard port.

Auth

ansible_user

The SSH user to connect as. Overrides remote_user in ansible.cfg for this specific host or group.

Auth

ansible_ssh_private_key_file

Path to the SSH private key for this host. Overrides the global private_key_file setting — useful when different hosts use different keys.

Runtime

ansible_python_interpreter

Path to the Python binary on the managed node. Set this when Python is at a non-standard location, e.g. /usr/bin/python3.

Runtime

ansible_connection

The connection plugin to use. Default is ssh. Set to winrm for Windows hosts or local to run tasks on the control node itself.

Verifying Your Inventory

Before running any playbook, use ansible-inventory to inspect what Ansible sees in your inventory file. This is the fastest way to catch typos, misconfigured groups, or missing variables before they cause a failed run.

# List all hosts and groups in a human-readable tree format
ansible-inventory -i inventory.ini --graph

# Show the full inventory as JSON — reveals every variable resolved per host
ansible-inventory -i inventory.ini --list

# Show details for a single host — all variables that will apply to it
ansible-inventory -i inventory.ini --host web01.example.com
# Output of --graph
@all:
  |--@ungrouped:
  |  |--192.168.1.5
  |--@production:
  |  |--@webservers:
  |  |  |--web01.example.com
  |  |  |--web02.example.com
  |  |--@databases:
  |  |  |--db01.example.com
  |  |  |--db02.example.com

What just happened?

Ansible printed the full group hierarchy as a tree. You can immediately see which hosts belong to which groups, spot any host that ended up in ungrouped by mistake, and confirm that parent/child group relationships resolved correctly. Run this every time you edit your inventory.

Dynamic Inventory

A dynamic inventory is a script or plugin that generates the host list at runtime by querying an external source — a cloud provider API, a CMDB, or any other system that knows what servers exist. Instead of maintaining a hand-edited file, Ansible calls the script before each run and receives a fresh, up-to-date list of hosts and their metadata.

Dynamic inventory is the standard approach in cloud environments where servers are constantly being created, scaled, and terminated. You never want to maintain a static inventory for an auto-scaling group — by the time you update it, the server list has already changed. Ansible ships with built-in dynamic inventory plugins for AWS, GCP, Azure, VMware, OpenStack, and many others.

Cloud

AWS EC2

amazon.aws.aws_ec2 — queries the EC2 API, groups hosts by region, tag, VPC, and instance type automatically.

Cloud

GCP

google.cloud.gcp_compute — queries the GCP Compute API and groups by zone, label, and machine type.

Cloud

Azure

azure.azcollection.azure_rm — queries Azure Resource Manager and groups by resource group, location, and tag.

Official reference: Full documentation for all built-in dynamic inventory plugins — including setup, authentication, and filtering options for each cloud provider.

Inventory plugins ↗

Never Define Sensitive Values Directly in the Inventory File

It is tempting to put passwords, API keys, and database credentials directly in your inventory or group_vars files for convenience. These files are committed to version control — any secret stored in plain text is permanently exposed in your Git history, even if you delete it later. Use Ansible Vault to encrypt sensitive variables, covered in depth in Lesson 28.

Key Takeaways

Every inventory has two implicit groupsall (every host) and ungrouped (hosts not in any named group). These exist whether you define them or not.
Use group_vars/ and host_vars/ directories — keeping variables out of the inventory file keeps it readable and makes variable management scalable as your project grows.
Magic variables like ansible_user and ansible_port override global config — use them in inventory to apply host-specific or group-specific connection settings without touching ansible.cfg.
Always verify inventory with ansible-inventory --graph — run this after every edit to confirm groups, children, and host assignments are exactly what you expect before running a playbook.
Dynamic inventory is the right choice for cloud environments — static files cannot keep up with auto-scaling infrastructure; use a cloud provider plugin that queries the API at runtime instead.

Teacher's Note

Get into the habit of running ansible-inventory --graph after every inventory change — it takes two seconds and will save you from running a playbook against the wrong set of hosts.

Practice Questions

1. What is the name of the implicit Ansible group that automatically contains every host in the inventory?



2. What command prints your inventory as a tree showing all groups and their member hosts?



3. Which magic variable overrides the SSH port for a specific host in the inventory?



Quiz

1. A playbook targets the production group, which has webservers and databases as children. What does Ansible do?


2. What is the recommended way to manage variables for a large Ansible project?


3. Your infrastructure runs on AWS with auto-scaling groups that add and remove servers constantly. What type of inventory should you use?


Up Next · Lesson 8

Ansible Ad-hoc Commands

Run single tasks against your inventory instantly — without writing a playbook. Ad-hoc commands are Ansible's fastest tool for one-off checks, quick fixes, and fleet-wide queries.