Inventory
An inventory is a list of managed nodes, or hosts, that Ansible deploys and configures. The inventory can either be static or dynamic.
You can use group names to classify hosts and to decide which hosts you are controlling following these criteria:
- What - An application, stack, or microservice (e.g. database servers, web servers, and so on).
- Where - A datacenter or region, to talk to local DNS, storage, and so on (e.g., east, west).
- When - The development stage, to avoid testing on production resources (e.g. prod, test).
Tip
To verify that Ansible has properly read an inventory source, ansible-inventory --graph can be used. It prints the structure of hosts and groups and is especially useful for dynamic inventories.
Static inventory
The simplest inventory is a single static file that contains a list of hosts and groups. Connections are defined more precisely using variables. This approach makes getting started particularly easy.
A .ini inventory file for example might look like this:
[control]
wsl ansible_host=localhost ansible_connection=local
[target]
rocky8 ansible_host=192.168.43.77
ubuntu ansible_host=192.168.45.68
[target:vars]
ansible_ssh_private_key_file=~/.ssh/id_for_ansible.pem
The same inventory can be expressed in the YAML format.
control:
hosts:
wsl:
ansible_connection: local
ansible_host: localhost
target:
hosts:
rocky8:
ansible_host: 192.168.43.77
ubuntu:
ansible_host: 192.168.45.68
vars:
ansible_ssh_private_key_file: ~/.ssh/id_for_ansible.pem
Tip
Prefer the YAML format.
Convert INI to YAML
The most common format for the Ansible Inventory is the .ini format, but sometimes you might need the inventory file in the YAML format. You can convert your existing inventory to the YAML format with the ansible-inventory utility.
Inventory alias
The inventory_hostname is the unique identifier for a host in Ansible, but it does not need to be the actual (resolvable) hostname used for connecting to the target.
You can create a custom alias which will be shown in the Ansible output.
--- # (1)!
all:
children:
managed_nodes:
hosts:
instance1: # (2)!
ansible_host: rhel9-node1 # (3)!
vars: # (4)!
ansible_ssh_private_key_file: ~/.ssh/id_for_ansible.pem
ansible_user: ansible
ansible_ssh_pipelining: true
-
The same inventory in
.iniformat: -
The alias, corresponds to the variable
inventory_hostnameand is shown in the Ansible output. -
Specifies the resolvable name (or ideally the IP) of the host to connect to.
-
A couple of behavioral connection parameters for all
managed_nodes(group variables).
TASK [Gathering Facts] ***********************************************
ok: [instance1]
TASK [The used 'alias', shown as the target name in the output] ******
ok: [instance1] =>
inventory_hostname: instance1
TASK [The actual value used for the connection] **********************
ok: [instance1] =>
ansible_host: rhel9-node1
TASK [What the host itself reports as its FQDN] **********************
ok: [instance1] =>
ansible_facts['fqdn']: rhel9-node1.example.com
Success
Although the connection to the host is done via the actual hostname (rhel9-node1), the output shows the alias (instance1).
An additional example can be be found in the No inventory section where an alias is used for localhost to have a cleaner output when connecting to a network/API endpoint.
Dynamic inventory
If your Ansible inventory fluctuates over time, with hosts spinning up and shutting down in response to business demands, the static inventory solutions described in How to build your inventory will not serve your needs. You may need to track hosts from multiple sources: cloud providers, LDAP, Cobbler, and/or enterprise CMDB systems.
There are already loads of inventory plugins available, for example:
Custom dynamic inventory
In case no suitable inventory plugin exists, you can easily write your own. Take a look at the Ansible Development - Extending section for additional information.
In-Memory Inventory
Normally Ansible requires an inventory file, to know which machines it is meant to operate on.
This is typically a manual process but can be greatly improved by using a dynamic inventory to pull inventory information from other systems.
Suppose, however, you needed to create X number of instances, which are transient in nature and had no existing details available to populate an inventory file for Ansible to utilise. If X is a small number, you could easily hand-craft the inventory file while the playbook already runs.
Use the add_host module, which makes use of Ansible's ability to populate an in-memory inventory with information it generates while creating new instances.
Take a look at the following example, the first play creates a couple of Containers and adds them to a new group. The seconds plays targets this new group and connects to the newly created Containers.
---
- name: Add hosts to additional groups
hosts: localhost
connection: local
gather_facts: false
vars:
container_list:
- node1
- node2
- node3
tasks:
- name: Start managed node containers
containers.podman.podman_container:
name: ""
image: docker.io/timgrt/rockylinux8-ansible:latest
hostname: ".example.com"
stop_signal: 15
state: started
loop: ""
- name: Add container to new group
ansible.builtin.add_host:
name: "" # (1)!
groups: managed_node_containers # (2)!
ansible_connection: podman # (3)!
ansible_python_interpreter: /usr/libexec/platform-python # (4)!
stage: test # (5)!
loop: ""
- name: Run tasks on containers created in previous play
hosts: managed_node_containers
tasks:
- name: Output stage variable
ansible.builtin.debug:
msg: ""
- Every container instance is added by looping the variable
container_list. As thenameparameter must be a string a loop is necessary. - This is the name of the new group! It is targeted in the second play. The
groupsparameter can be a list of multiple group names. - These are variables needed to connect to the new instances. As they are Podman containers the podman connection plugin is used.
- The Python interpreter which is used in the new instances. Not always necessary, as normally Ansible discovers the interpreter pretty reliable.
- This is a custom variable for all new instances. You can add more variables here if necessary.
Playbook output
$ ansible-playbook in-memory-inventory.yml
PLAY [Add hosts to additional groups] *******************************************************************************************************************************
TASK [Start managed node containers] ********************************************************************************************************************************
ok: [localhost] => (item=node1)
ok: [localhost] => (item=node2)
ok: [localhost] => (item=node3)
TASK [Add container to new group] ***********************************************************************************************************************************
changed: [localhost] => (item=node1)
changed: [localhost] => (item=node2)
changed: [localhost] => (item=node3)
PLAY [Run tasks on containers created in previous play] *************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************
ok: [node2]
ok: [node1]
ok: [node3]
TASK [Output stage variable] ****************************************************************************************************************************************
ok: [node1] =>
msg: test
ok: [node2] =>
msg: test
ok: [node3] =>
msg: test
No inventory
An inventory is not always necessary, depending on your use-case and the modules used.
For example, network modules do not run on the managed nodes (as, in most cases, they do not have a usable Python interpreter), they are executed on the Ansible control node. Therefore, you'll need to use the local connection method running against localhost. The module itself handles the connection, mostly you'll need to provide the endpoint, username, password, certificate validation, etc. in every task (take a look at the module_defaults section to simplify this).
Still, it can be useful to provide a small inventory to have a named target for a cleaner output.
Named target
The following inventory creates an alias for localhost which will be shown in the playbook output:
The playbook targets the apic group:
- name: Automate Cisco ACI
hosts: apic
gather_facts: false # (1)!
roles:
- aci_automation
- Fact gathering can be disabled as it would get data from localhost, which is most likely not used.
$ ansible-playbook -i inventory.ini aci_automation.yml
PLAY [Automate Cisco ACI] *******************************************************************************************
TASK [aci-automation : Create tenant] *******************************************************************************
changed: [sandboxapicdc.cisco.com]
...
The output shows the name of the targeted APIC, which better indicates where the changes are done!
Showing localhost only
The target and connection method can be provided in the playbook directly:
- name: Automate Cisco ACI
hosts: localhost
connection: local
gather_facts: false # (1)!
roles:
- aci_automation
- Fact gathering can be disabled as it would get data from localhost, which is most likely not used.
Running this does not require an inventory file:
$ ansible-playbook aci_automation.yml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [Automate Cisco ACI] *******************************************************************************************
TASK [aci-automation : Create tenant] *******************************************************************************
changed: [localhost]
...
The output shows localhost as the target, although that is not the actual target...
Additionally, warnings are shown when not providing an inventory and running against localhost. Take a look at the configuration section on how to deal with those.
Configuration file
Configure your inventory file(s) in the ansible.cfg, now you do not have to provide it with --inventory/-i anymore.
You can provide multiple files as a comma-separated string, mixes of static and dynamic inventory files and scripts are also possible.
Inventory variables
Variables can be assigned to every host and/or group in the inventory file directly, but this makes the file not very readable with a growing number of hosts, groups and variables.
Success
Use the group_vars and host_vars folder.
Do not create files for the groups or hosts (e.g. group_vars/web.yml or host_vars/node1.yml), but use folders for hosts and groups instead.
Underneath these folders, you can create multiple variables-files which are all loaded by the host_group_vars plugin.