A deep dive into Ansible Inventories

A deep dive into Ansible Inventories

Play this article

This article will make you understand what is Ansible inventory and how to connect to remote servers using Ansible inventory files.

Prerequisites

  1. One Ansible Control Node The Ansible control or mater node will be the machine we will use to connect to and control the Ansible hosts over SSH.
  2. One or more Linux client nodes
  3. SSH connection SSH connection should be enabled between Ansible control node and the remote client servers.

My Environment

HostnameIP addressOperating System
lco-ansible-master.example.com172.52.52.200CentOS Linux 7 (Core)
lco-worker1.example.com172.52.52.201CentOS Linux 7 (Core)
lco-worker2.example.com172.52.52.202CentOS Linux 7 (Core)

What is Ansible Inventory?

Ansible is a configuration management tool which you use to setup and maintain the remote servers in an automated manner.

The list or the group of lists of such remote servers is called as Ansible inventory. This can be created in multiple formats as per your project requirements.

The default location of an Ansible inventory file is /etc/ansible/hosts. The project specific inventory files also can be created in their respective locations on the system.

An example inventory file in INI format:

lco-ansible-master.example.com

[webservers]
lco-worker1.example.com
lco-worker1.example.com

[dbservers]
lco-worker3.example.com
lco-worker4.example.com

An example inventory file in YAML format:

all:
  hosts:
    lco-ansible-master.example.com:
  children:
    webservers:
      hosts:
        lco-worker1.example.com:
        lco-worker1.example.com:
    dbservers:
      hosts:
        lco-worker3.example.com:
        lco-worker4.example.com:

Create you own Inventory file

Though Ansible comes up with a default inventory file located at /etc/ansible/hosts one can always create a custom inventory file as per their project requirements.

Note: During a playbook or ansible ad-hoc command execution you need to supply that custom inventory file with -i option as it wont be called by default.

Create a directory in you home folder:

[root@lco-ansible-master ~]# mkdir ansible

Create a file called myinventory and put the following content in that:

[root@lco-ansible-master ansible]# cat myinventory
lco-ansible-master.example.com
lco-worker1.example.com
lco-worker2.example.com

This is the most basic form of an inventory file and it contains the list of all our nodes. Please note that hostnames and IP addresses are interchangeable.

Validate the inventory file:

[root@lco-ansible-master ansible]# ansible-inventory -i myinventory --list
{
    "_meta": {
        "hostvars": {}
    },
    "all": {
        "children": [
            "ungrouped"
        ]
    },
    "ungrouped": {
        "hosts": [
            "lco-ansible-master.example.com",
            "lco-worker1.example.com",
            "lco-worker2.example.com"
        ]
    }
}

In the above output all is used to refer to all servers from your inventory file and the ungrouped group refers to servers which are not part of any group.

Run an ansible ad-hoc command to test inventory file functionality:

[root@lco-ansible-master ansible]# ansible -i myinventory -m ping all
lco-worker1.example.com | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
lco-worker2.example.com | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
lco-ansible-master.example.com | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

We have used ping module to check connectivity by pinging all the hosts part of our custom inventory file.

Ansible replies with a pong if connection succeeded.

Hosts grouping in Inventory files

As I've already mentioned above that one can always group and subgroup the remote servers in an Ansible inventory file as per their project requirements.

For example look at the below file.

[root@lco-ansible-master ansible]# cat myinventory
[ansible-controller]
lco-ansible-master.example.com

[ansible-workers]
lco-worker1.example.com
lco-worker2.example.com

[web-server]
lco-worker1.example.com

[db-server]
lco-worker2.example.com

[all-systems:children]
ansible-controller
ansible-workers

Lets understand this file in detail.

  • We have grouped our hosts here for example our worker nodes are part of ansible-workers group and our Ansible control machine is part of ansible-controller group.
  • Servers are grouped on the basis of their purpose and qualities such as web-server and db-server
  • A host can be part of multiple groups.
  • We have further break down the host groups into subgroups so that you can cover smaller and specific targets whenever needed.

Adding a Range of hosts in Ansible Inventory

Generally in IT organizations you will have a lot of hosts with a similar pattern in that case you can add them as a range instead adding them separately.

For example:

[root@lco-ansible-master ansible]# cat range_inventory
[ansible-workers]
lco-worker[1:2].example.com

Test the functionality:

[root@lco-ansible-master ansible]# ansible -m ping -i range_inventory all
lco-worker1.example.com | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
lco-worker2.example.com | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

It works!

You can also define alphabetic ranges as below:

[ansible-workers]
lco-worker-[a:f].example.com

Add aliases to your hosts in Ansible Inventory

Yes you can add easy to remember aliases to your hosts in an Ansible inventory file. To use inventory aliases feature we need to add an variable called ansible_host which will have remote host's IP or hostname as its value.

For example:

[root@lco-ansible-master ansible]# cat alises_inventory
master ansible_host=lco-ansible-master.example.com
worker1 ansible_host=lco-worker1.example.com
worker2 ansible_host=lco-worker2.example.com

Now run the ansible-inventory command to analyze this file further.

[root@lco-ansible-master ansible]# ansible-inventory -i alises_inventory --list
{
    "_meta": {
        "hostvars": {
            "master": {
                "ansible_host": "lco-ansible-master.example.com"
            },
            "worker1": {
                "ansible_host": "lco-worker1.example.com"
            },
            "worker2": {
                "ansible_host": "lco-worker2.example.com"
            }
        }
    },
    "all": {
        "children": [
            "ungrouped"
        ]
    },
    "ungrouped": {
        "hosts": [
            "master",
            "worker1",
            "worker2"
        ]
    }
}

You can observe by above output that hosts are now referenced by their aliases instead of their IP addresses or hostnames. It makes it easier to run ansible commands or playbooks against these individual servers.

Adding Host Variables to Ansible Inventory

We can add variables when we want to change the default behavior which relates to a host or a group in the Ansible inventory file.

To understand it in a simpler manner we can directly add variables to the hosts and groups in our main inventory file. Going forward in our Ansible series or articles we will dig deeper into Ansible variables concept where I will show you to store variables in separate host and group variable files.

Here is my updated inventory file which has host level variables defined.

[root@lco-ansible-master ansible]# cat variable_inventory
master ansible_host=lco-ansible-master.example.com ansible_user=ansible_user ansible_ssh_port=22
worker1 ansible_host=lco-worker1.example.com ansible_user=ansible_user ansible_ssh_port=22
worker2 ansible_host=lco-worker2.example.com ansible_user=ansible_user ansible_ssh_port=22

Here we have used three ansible variables. Above example shows defining remote user (ansible_user here which has sudo access) and ssh port number when connecting to each of the nodes listed in this inventory file.

Variable can also be assigned at group level which will be applicable for all the hosts part of that group. For example:

[root@lco-ansible-master ansible]# cat group_variable_inventory
[ansible-controller]
master ansible_host=lco-ansible-master.example.com ansible_user=ansible_user ansible_ssh_port=22

[ansible-workers]
worker1 ansible_host=lco-worker1.example.com
worker2 ansible_host=lco-worker2.example.com

[ansible-workers:vars]
ansible_user=ansible_user
ansible_ssh_port=22

Verify the functionality of group variables by running the following command:

[root@lco-ansible-master ansible]# ansible-inventory -i group_variable_inventory --list
{
    "_meta": {
        "hostvars": {
            "master": {
                "ansible_host": "lco-ansible-master.example.com",
                "ansible_ssh_port": 22,
                "ansible_user": "ansible_user"
            },
            "worker1": {
                "ansible_host": "lco-worker1.example.com",
                "ansible_ssh_port": 22,
                "ansible_user": "ansible_user"
            },
            "worker2": {
                "ansible_host": "lco-worker2.example.com",
                "ansible_ssh_port": 22,
                "ansible_user": "ansible_user"
            }
        }
    },
    "all": {
        "children": [
            "ansible-controller",
            "ansible-workers",
            "ungrouped"
        ]
    },
    "ansible-controller": {
        "hosts": [
            "master"
        ]
    },
    "ansible-workers": {
        "hosts": [
            "worker1",
            "worker2"
        ]
    }
}

There are two default groups: all and ungrouped. The all group contains every host. The ungrouped group contains all hosts that don’t have another group aside from all. Every host will always belong to at least 2 groups (all and ungrouped or all and some other group).

Pattern matching to manage hosts and groups

We can use patterns to target one or more hosts which are part of our inventory in order to run commands and playbooks against them. Pattern matching supports wildcards and regex.

Common patterns

This table lists common patterns for targeting inventory hosts and groups.

DescriptionPattern(s)Targets
All hostsall (or *)All the hosts part of your inventory file
One hosthost1A single host (here host1)
Multiple hostshost1:host2 (or host1,host2)Both the hosts (host1 and host2)
One groupansible-workersA single group (here ansible-workers)
Multiple groupswebservers:dbserversall hosts in webservers plus all hosts in dbservers
Excluding groupswebservers:!milpitasall hosts in webservers except those in milpitas
Intersection of groupswebservers:&devany hosts in webservers that are also in dev

Few examples:

[root@lco-ansible-master ansible]# ansible webservers:&dev -m ping -i inventory
[root@lco-ansible-master ansible]# ansible webservers:!milpitas -m ping -i inventory

That's all.

You should be now having a fair enough idea about Ansible inventories. You now know how we can organize hosts into groups and subgroups, set up inventory variables (at host and group levels), and use patterns in order to connect different groups of servers while running commands and playbooks.

Hope you like the article. Stay Tuned and don't forget to subscribe for more.

Thank you. Happy learning!

Did you find this article valuable?

Support Learn Code Online by becoming a sponsor. Any amount is appreciated!