Ansible Inventories
Let's fist create a file with the name ansible.cfg
,
[defaults]
inventory = hosts
Next a file with the name hosts.ini
,
[all]
centos1
In here, the ansible.cfg
file is refering to an invenstory file with the name "hosts" and the hosts.ini
file consist a host with the name "centos1". (In here it is assumed that a host with the name centos1 is reachable at this point)
Now first, I will remove the known hosts file from the ssh directory.
$ rm -rf /home/ansible/.ssh/known_hosts
Next when I try to ping to my host, it should asks me to verify the fingerprint, and I'm not going to accept it.
$ ansible all -m ping
The authenticity of host 'centos1 (172.18.0.8)' can't be established.
ECDSA key fingerprint is SHA256:gdRFM1dy+ntzCjU1mJi5oBS1k5enVlS/bPz6Wms59Ck.
Are you sure you want to continue connecting (yes/no/[fingerprint])? ^C [ERROR]: User interrupted execution
To get around with this, I'm going to ping the hosts again with a varible set up.
$ ANSIBLE_HOST_KEY_CHECKING=False ansible all -m ping
centos1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
The variable ANSIBLE_HOST_KEY_CHECKING
instructs ansible to ignore to fingerprint verification when it is set to False
. But sometimes it could be repetitive to specify this variable when executing commands. This can also be configured in the ansible.cfg
file.
[defaults]
inventory = hosts
host_key_checking = False
To make sure host's are not there, I'm going to remove the known hosts file,
$ rm -rf /home/ansible/.ssh/known_hosts
Then I'm going to ping our hosts again,
$ ansible all -m ping
centos1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
Now let me slightly modify our invenstory file hosts.ini
like below,
[centos]
centos1
centos2
centos3
[ubuntu]
ubuntu1
ubuntu2
ubuntu3
Though we have grouped these under their os type, we can still use the all
key word which will take all hosts into consideration.
$ ansible all -m ping -o
ubuntu1 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
ubuntu2 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
centos3 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"}
centos1 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"}
centos2 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"}
ubuntu3 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
We can specify only a one type of os types like below,
$ ansible centos -m ping -o
centos3 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"}
centos1 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"}
centos2 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"}
This even works with other types of commands such as listing,
$ ansible ubuntu --list-hosts
hosts (3):
ubuntu1
ubuntu2
ubuntu3
Well, this works with regular expressions as well (any number or charters and digits),
$ ansible ~.*3 --list-hosts
hosts (2):
centos3
ubuntu3
Sometimes it is essential that we run our commands as the root user. In order to perform that, we can specify the ansible_user
parameter in our inventory file.
[centos]
centos1 ansible_user=root
centos2 ansible_user=root
centos3 ansible_user=root
[ubuntu]
ubuntu1
ubuntu2
ubuntu3
To verify this, I'm going to use a different module called command
with the parameter id
like below,
$ ansible all -m command -a 'id' -o
ubuntu2 | CHANGED | rc=0 | (stdout) uid=1000(ansible) gid=1000(ansible) groups=1000(ansible),27(sudo)
ubuntu1 | CHANGED | rc=0 | (stdout) uid=1000(ansible) gid=1000(ansible) groups=1000(ansible),27(sudo)
centos3 | CHANGED | rc=0 | (stdout) uid=0(root) gid=0(root) groups=0(root)
centos2 | CHANGED | rc=0 | (stdout) uid=0(root) gid=0(root) groups=0(root)
centos1 | CHANGED | rc=0 | (stdout) uid=0(root) gid=0(root) groups=0(root)
ubuntu3 | CHANGED | rc=0 | (stdout) uid=1000(ansible) gid=1000(ansible) groups=1000(ansible),27(sudo)
Output of the above command shows us that connections to the centos has made as root
while for ubuntu hosts it still uses the ansible
user.
This brings us to another interesting scenario. What if we want to connect to our hosts as a normal user, but needs to perform some tasks with escalated user? in that case we can use ansible_become_true
parameter set to true
and ansible_become_pass
set to the password. For example, let's modify our inventory file like below,
[centos]
centos1 ansible_user=root
centos2 ansible_user=root
centos3 ansible_user=root
[ubuntu]
ubuntu1 ansible_become=true ansible_become_pass=password
ubuntu2 ansible_become=true ansible_become_pass=password
ubuntu3 ansible_become=true ansible_become_pass=password
Next we can execute the same command to see the user being used,
$ ansible all -m command -a 'id' -o
centos2 | CHANGED | rc=0 | (stdout) uid=0(root) gid=0(root) groups=0(root)
centos3 | CHANGED | rc=0 | (stdout) uid=0(root) gid=0(root) groups=0(root)
ubuntu1 | CHANGED | rc=0 | (stdout) uid=0(root) gid=0(root) groups=0(root)
centos1 | CHANGED | rc=0 | (stdout) uid=0(root) gid=0(root) groups=0(root)
ubuntu2 | CHANGED | rc=0 | (stdout) uid=0(root) gid=0(root) groups=0(root)
ubuntu3 | CHANGED | rc=0 | (stdout) uid=0(root) gid=0(root) groups=0(root)
Ansible by default assumes the ssh port is 22. But there could be situations where the ssh port is different. In that case we can specify the ssh port in one of two ways mentioned in below,
[centos]
centos1 ansible_user=root ansible_port=2222
centos2 ansible_user=root
centos3 ansible_user=root
[ubuntu]
ubuntu1 ansible_become=true ansible_become_pass=password
ubuntu2 ansible_become=true ansible_become_pass=password
ubuntu3 ansible_become=true ansible_become_pass=password
or
[centos]
centos1:2222 ansible_user=root
centos2 ansible_user=root
centos3 ansible_user=root
[ubuntu]
ubuntu1 ansible_become=true ansible_become_pass=password
ubuntu2 ansible_become=true ansible_become_pass=password
ubuntu3 ansible_become=true ansible_become_pass=password
And if we ping again, we might be able to connect to the hosts again,
$ ansible all -m ping -o
centos2 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"}
ubuntu2 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
centos3 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"}
ubuntu1 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
centos1 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"}
ubuntu3 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
We can also specify a control gorup where it will not use and ssh connection.
[control]
ubuntu-c ansible_connection=local
[centos]
centos1 ansible_user=root ansible_port=2222
centos2 ansible_user=root
centos3 ansible_user=root
[ubuntu]
ubuntu1 ansible_become=true ansible_become_pass=password
ubuntu2 ansible_become=true ansible_become_pass=password
ubuntu3 ansible_become=true ansible_become_pass=password
It is also possible to specify ranges in the inventory files.
[control]
ubuntu-c ansible_connection=local
[centos]
centos1 ansible_user=root ansible_port=2222
centos[2:3] ansible_user=root
[ubuntu]
ubuntu[1:3] ansible_become=true ansible_become_pass=password
Let's check all the hosts to verify this,
$ ansible all --list-hosts
hosts (7):
ubuntu-c
centos1
centos2
centos3
ubuntu1
ubuntu2
ubuntu3
But still our inventory file has duplicates. For example we specify the ansible user for all groups. This can be addressed using group vars. These group vards will be fed into each record during the execution.
[control]
ubuntu-c ansible_connection=local
[centos]
centos1 ansible_port=2222
centos[2:3]
[centos:vars]
ansible_user=root
[ubuntu]
ubuntu[1:3]
[ubuntu:vars]
ansible_become=true
ansible_become_pass=password
Let's ping again and see if this works,
$ ansible all -m ping -o
centos2 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"}
centos1 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"}
ubuntu-c | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
centos3 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"}
ubuntu1 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
ubuntu2 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
ubuntu3 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
We can futher group things using a parent:child relationship like below,
[control]
ubuntu-c ansible_connection=local
[centos]
centos1 ansible_port=2222
centos[2:3]
[centos:vars]
ansible_user=root
[ubuntu]
ubuntu[1:3]
[ubuntu:vars]
ansible_become=true
ansible_become_pass=password
[linux:children]
centos
ubuntu
We can now call everything falling under linux group and see if it still working,
$ ansible linux -m ping -o
centos3 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"}
centos2 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"}
ubuntu2 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
centos1 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"}
ubuntu1 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
ubuntu3 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
Now let's motify the inventory like below,
[control]
ubuntu-c ansible_connection=local
[centos]
centos1 ansible_port=2222
centos[2:3]
[centos:vars]
ansible_user=root
[ubuntu]
ubuntu[1:3]
[ubuntu:vars]
ansible_become=true
ansible_become_pass=password
[linux:children]
centos
ubuntu
[all:vars]
ansible_port=1234
You can notice that I've specified variables section for all where I define the ssh port as 1234. This indeed is a wrong port. However, these variables have a precedence effect. Since I have specified the correct port for centos1 host along with it, it will work fine, but others might fail.
$ ansible all -m ping -o
centos2 | UNREACHABLE!: Failed to connect to the host via ssh: ssh: connect to host centos2 port 1234: Connection refused
centos3 | UNREACHABLE!: Failed to connect to the host via ssh: ssh: connect to host centos3 port 1234: Connection refused
ubuntu1 | UNREACHABLE!: Failed to connect to the host via ssh: ssh: connect to host ubuntu1 port 1234: Connection refused
ubuntu2 | UNREACHABLE!: Failed to connect to the host via ssh: ssh: connect to host ubuntu2 port 1234: Connection refused
ubuntu3 | UNREACHABLE!: Failed to connect to the host via ssh: ssh: connect to host ubuntu3 port 1234: Connection refused
ubuntu-c | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
centos1 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"}
It is also possible to write the inventory files in YAML format. But in that case we have to explicitly specify the inveontory file in the ansible.cfg
file,
[defaults]
inventory = hosts.yaml
host_key_checking = False
Then we can declare our inventory file in YAML format like below,
---
control:
hosts:
ubuntu-c:
ansible_connection: local
centos:
hosts:
centos1:
ansible_port: 2222
centos2:
centos3:
vars:
ansible_user: root
ubuntu:
hosts:
ubuntu1:
ubuntu2:
ubuntu3:
vars:
ansible_become: true
ansible_become_pass: password
linux:
children:
centos:
ubuntu:
...
Similarly it is also possible to specify the the inventory file in JSON format,
{
"control": {
"hosts": {
"ubuntu-c": {
"ansible_connection": "local"
}
}
},
"centos": {
"hosts": {
"centos1": {
"ansible_port": 2222
},
"centos2": null,
"centos3": null
},
"vars": {
"ansible_user": "root"
}
},
"ubuntu": {
"hosts": {
"ubuntu1": null,
"ubuntu2": null,
"ubuntu3": null
},
"vars": {
"ansible_become": true,
"ansible_become_pass": "password"
}
},
"linux": {
"children": {
"centos": null,
"ubuntu": null
}
}
}
When using a differnt inventory file formats or multiple file, you can specify the inventory using the i
flag in the command line,
$ ansible all -i hosts.yaml --list-hosts
hosts (7):
ubuntu-c
centos1
centos2
centos3
ubuntu1
ubuntu2
ubuntu3
Last updated
Was this helpful?