A few days ago, we have discussed how to set up a three-node Ansible lab using Vagrant in Linux. In this article, we are going to learn Ansible fundamental concepts such as Ansible inventory and configuration files in detail.
Table of Contents
Basic Structure Of Ansible
When you start working with ansible, there are two important files you should understand about. One is Ansible inventory file and the second is the Ansible configuration file.
The configuration file contains all the configurations that ansible will use during the run time. When you install ansible using the OS package manager, then you can see there is a default configuration file named "ansible.cfg
" created in /etc/ansible
directory.
The inventory file contains the IP address or DNS information about the list of managed hosts we want to work with. Ansible will read the hostnames from the inventory file and the tasks/plays will be executed on that nodes. Inventory files can be created in ini
, yaml
, and json
formats.
Below is the structure of my project. I have an ansible.cfg
configuration file, a hosts file, and a playbook which is written in yaml
format.
When I trigger the playbook, ansible will read the configuration from ansible.cfg
file and the host details from the hosts file to run the plays.
vagrant@controller:~/ansible_project$ tree . . ├── ansible.cfg ├── hosts └── playbook.yml 0 directories, 3 files
Let’s talk in detail about these two files and we will finally run some adhoc commands.
Ansible Configuration File
Ansible uses the configuration file to load the parameters that are required to run the ansible task. If you have installed ansible using the package manager, you will have an ansible.cfg
file in /etc/ansible
directory.
Below is the sample of the ansible.cfg
file.
Ansible will look for the configuration file in the following order.
ANSIBLE_CONFIG
- environment variableansible.cfg
- Current directory from where you are running the command.ansible.cfg
- Users home directory/etc/ansible/ansible.cfg
- In/etc/ansible
directory
Run ansible --version
command to see which configuration file ansible is picking.
$ ansible --version ansible [core 2.12.3] config file = /home/vagrant/ansible_project/ansible.cfg configured module search path = ['/home/vagrant/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'] ansible python module location = /usr/local/lib/python3.8/dist-packages/ansible ansible collection location = /home/vagrant/.ansible/collections:/usr/share/ansible/collections executable location = /usr/local/bin/ansible python version = 3.8.10 (default, Nov 26 2021, 20:14:08) [GCC 9.3.0] jinja version = 2.10.1 libyaml = True
I have created a custom project directory and created a ansible.cfg
file. My config file contains only two properties, inventory location, and disabled host key checking.
[defaults] inventory = /home/karthick/ansible_project/hosts host_key_checking = False
There are tons of other properties but at the moment you can start using them by adding "inventory" property alone.
Ansible Inventory File
In the inventory file, we will give the IP address or DNS of server names, network devices, cloud services, or anything that ansible can work with.
Inventories can be written in ini
, json
, and yaml
format and you also have scripts to convert one inventory format to other formats.
By default, you can find an inventory file(hosts) in /etc/ansible
directory if you have installed ansible using OS package manager. It is always recommended to create a separate project directory and create an inventory and config file.
The below table represents how my ansible lab is set up. I am going to create the inventory file for this lab setup and will run the ping module to demonstrate how things work in the inventory file.
NODE TYPE | NODE NAME | IP ADDRESS | OS FLAVOR |
Control Node | controller.anslab.com | 192.168.10.3 | ubuntu/focal64 |
Managed Node | managed1.anslab.com | 192.168.10.4 | ubuntu/focal64 |
Managed Node | managed2.anslab.com | 192.168.10.5 | ubuntu/focal64 |
Inventory File Without Explicit Grouping
Inventory file has a concept called grouping where you will be grouping your resources and run tasks against that group. It will be in the following structure.
[group-name] Resource1 Resource2 .... Resource N
You can create the inventory file without using groups. In this case, Ansible will use two default groups "all" and "ungrouped".
- ALL GROUP - All resources that are available in the inventory file by default will be assigned to all group.
- UNGROUPED - Resources that are not part of any user-defined groups will be automatically assigned to the ungrouped group.
I have created an inventory file without any groups. Now, these two nodes will come under all group as well as the ungrouped group.
$ cat hosts
managed1.anslab.com
managed2.anslab.com
You can run the following ansible command to check the list of hosts under all and ungrouped group.
$ ansible all --list-hosts hosts (2): managed1.anslab.com managed2.anslab.com
$ ansible ungrouped --list-hosts hosts (2): managed1.anslab.com managed2.anslab.com
Inventory File With Grouping
As foretold, grouping is a great way to organize your resources so it will be easy for you to run the task against the specific group. I have two managed nodes and they run Ubuntu 20.04LTS.
Let’s say I want to group my inventory file based on the OS type then the inventory file will look like below. Here ubuntu is the group name.
[ubuntu]
managed1.anslab.com
managed2.anslab.com
Tasks can be run against this group.
$ ansible ubuntu -m ping -o managed1.anslab.com | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"} managed2.anslab.com | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
If I check for the "ungrouped" group now, no host will be there.
$ ansible ungrouped --list-hosts
[WARNING]: No hosts matched, nothing to do
hosts (0):
You can create as many groups you want in the inventory file and use the same resource names.
[ubuntu] managed1.anslab.com managed2.anslab.com [dev] managed1.anslab.com [test] managed2.anslab.com
$ ansible ubuntu -m ping -o $ ansible dev -m ping -o $ ansible test -m ping -o
Inventory File With Ranges
When you have a common naming pattern for your resources, you can use ranges. Take a look at the below example where Instead of repeating the DNS I have used the range to shorten it.
[ubuntu]
managed[1:2].anslab.com
Inventory File With Child Groups
You can create a group and use other group names under it. Take a look at the below example, I have the ubuntu group which has my server names. I have created another group named server which contains the ubuntu group.
It is important you add :children to the group name otherwise the server group will treat "ubuntu" as the server name instead of the group name.
[ubuntu] managed[1:2].anslab.com [server:children] ubuntu
Inventory File With Host Vars & Group Vars
Inventory file supports host & group variables. Host variables are nothing but variables and their values passed to the host in the inventory file. In the below configuration I am telling ansible to use ostechnix as my user and instead of using standard ssh port, use port 2222.
[ubuntu]
managed1.anslab.com ansible_user=ostechnix ansible_port=2222
managed2.anslab.com ansible_user=ostechnix ansible_port=2222
Group vars is same as host vars but the variables will be applied to the entire group instead of a single host. You can see from the above config, ansible_user, and ansible_port is the same for both the nodes. So you can create a group var where the variables will be inherited by all the nodes in the group. You need to add :vars to make the group as group vars.
[ubuntu] managed1.anslab.com managed2.anslab.com [ubuntu:vars] ansible_user=ostechnix ansible_port=2222
Heads Up: When you have both group var and host var for the same node then host var takes high precedence.
There are many inventory parameters you can use and you can get the list from the official doc.
Inventory File With Alias
You can create an alias for the resource like below. Here m1 and m2 are aliases.
[ubuntu] m1 ansible_host=managed1.anslab.com m2 ansible_host=managed2.anslab.com
Now this alias can be used to run some tasks.
$ ansible m1 -m ping -o m1 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"} $ ansible m2 -m ping -o m2 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"}
Inventory File In YAML Format
Inventory files can also be written in YAML format. Whatever we have discussed in previous sections is written in YAML format.
The below image represents two nodes with a couple of host variables.
Below image represents two nodes with group variables.
Ansible Inventory Command
Ansible has an inventory command which will be very helpful to view the inventory files information.
To get the list of supported options run the help command.
$ ansible-inventory --help
To get the inventory details in the graph format, use --graph
flag. If you have different inventory files, you can use -i
flag to explicitly point to the inventory file.
$ ansible-inventory --graph
You can print the host and group var information by passing --vars
flag along with --graph
flag.
$ ansible-inventory --graph --vars
When you use the --list
flag, the output will be in JSON format.
$ ansible-inventory -list
You can get information about a particular node using the --host
flag.
$ ansible-inventory --host managed1.anslab.com
Conclusion
In this article, we have seen an important concept that forms the core of ansible architecture. If you are a beginner, focus on building the inventory with ini format and down the line you will get to know more about best practices when you start working on production projects.