For official proper definition please click here.
For now, we need to understand 2 things:
Control Node: Machine with Ansible installed that run tasks on remote machines (managed nodes)
Managed Nodes: Remote machines that execute the tasks Control Node asked it to do.
Ansible is a full-blown infrastructure automation tool that executes one or multiple tasks on one or multiple machines known as Managed Nodes.
It is a Configuration management and deployment tool.
All we do is to open a file and start adding tasks.
A task could be Installing NGINX webserver, for example.
In Ansible, we name a task and write down the command we want it to execute.
A task can be part of bigger thing like bringing up our e-commerce website.
Other tasks like applying updates, adding our custom config file can also be added.
The bigger thing or a group of tasks is grouped in what we call a Playbook.
A Playbook is just a file where we tell Ansible the tasks we want it to execute in an orderly fashion.
Ansible doesn't depend on additional daemons, client or servers.
As long as we have Ansible installed in our Control node, managed nodes running Linux only need Python and SSH installed.
If remote managed nodes are Windows machines, PowerShell+ and .NET 4.0+ have to be installed.
A WinRM listener also has be running.
There's also an experimental SSH support for Windows but according to documentation, we use it at our own risk.
A Control node (that has Ansible installed) reads a Playbook file and executes the tasks listed in the playbook.
We also mention in the playbook the host or group of hosts where such tasks should be executed.
The inventory file is where we have a list of individual hosts.
We can group individual hosts into groups within the Inventory file.
In the example below, we execute ansible-playbook <playbook_name> command on Ansible control node (10.10.10.100).
It then reads a Playbook file that has 2 tasks.
Task1 is executed on DBServers group of hosts and Task2 on WebServers group:
The Inventory file is only probed to check how to reach Managed nodes.
Managed nodes can be specified either individually or in groups and the format above can also be written in YAML.
ansible.cfg (sample file here) is where we find general Ansible's config.
Each setting is described here.
Ansible first looks for whatever config file is defined on ANSIBLE_CONFIG environment variable.
If not set, Ansible searches for ansible.cfg in the following order:
This is where we inform Ansible which hosts it's going to execute our tasks.
Inventories can be configured in different formats but the most common are the following:
There are plugins for other formats but it's out of scope of this article.
We can imagine how hard it'd be to write down everything from scratch when it comes to creating Ansible tasks.
Modules are specified in the Playbook according to the task we want Managed nodes to carry out.
In my explanation about ad-hoc vs playbook execution modes below, I will show 2 examples.
First example executes a raw Linux who command and the other example uses a module (apt) to update our Ubuntu managed nodes.
I could've used a raw apt-get command instead for the second example but I used apt module instead.
It has much better readability and enforces in the proper and simple way the state I want my managed nodes to be.
Now, let's move on to execution modes.
In all cases, we need to configure our Ansible control node (running Linux) with the appropriate SSH credentials to access the remote servers.
They're regular SSH credentials that can be added setting ansible_user and/or ansible_password in our inventory file as shown here.
When we add sensitive data to our files, it's strongly recommended to encrypt such data.
Appendix 2 section shows how we can encrypt a file with sensitive data.
The section Variables and Vaults summarises the best practices for storing sensitive variable data.
What we call ad-hoc commands are similar to when we need to execute a one-liner bash command.
They're mostly used for non-repeatable and simple tasks.
We use ansible command for that.
In the below example, I've got a group of 2 hosts called 'ubuntu' and I simply executed the Linux command 'who' on both hosts with the -a parameter on ansible command to see who is logged in:
Notice I didn't have to use ansible_password in above example as I authenticated by adding Ansible's control node public key to Managed nodes so I don't have to type a password.
This tutorial here shows you how to do that.
Playbooks on the other hand, are analogous to a full blown bash script.
We use ansible-playbook command for that.
A tasks that would require many lines or even different commands is a good candidate for Playbooks.
Let's create a simple playbook that simply check if NGINX is installed and if it's running:
The above playbook has 2 task:
We need to make sure we make it a habit to read Ansible's module options so we fully know what each module we intend to use is capable of.
This is what happens when we execute our playbook:
Notice first task is called Gathering Facts.
It collects all relevant information that can potentially be useful for most tasks from a node such as IP addresses, OS information, etc.
We might want to disable it if we're running Ansible against hundreds of nodes, for example.
To disable it, we can change gather_facts to no in our Playbook.
Here's the description of each field from our Playbook above:
hosts: individual or group of hosts that we want some action to take place. This name should be in the Inventory!
remote_user: Ansible user we execute commands in remote host
become: do I need to become root user to perform the tasks?
become_method: the method we use to become root
connection: how are we going to connect to hosts
gather_facts: information about operation system, state, what it has installed and things Ansible has to know if it has to perform certain tasks.
vars: variables in playbook so we only need to change it once or what you can pass to our playbook at runtime. This line was just added to show the syntax as we're not using variables here.
tasks: list of tasks we want Ansible to perform.
apt: the module we’re using to perform the task of installing nginx package
→ state: latest means if nginx package is not the latest version, the latest version will be installed. If it is latest version, nothing will be done.
service: the module we're using to perform the task of managing nginx daemon's state.
→ state: started means if nginx daemon is not running, it will make sure it is running. If it is already running, nothing will be done.
Hopefully, you now have some understanding of Ansible.
My advice is to try out the playbook above or a custom one to get used with Ansible and then try out other modules.
When a playbook gets complex, we have the option to break it up into components through a known file structure known as roles.
Ansible roles are out of the scope of this introductory article but it's also useful to know what it is all about as we should bump into roles in the real world.
There's also a public repository for roles.
We can use roles as a reusable ready-made playbook that can easily be shared to perform a collection of complex tasks.
We can even search for available roles using ansible-galaxy command:
For password and any sensitive data, it is recommended to hash your password using mkpasswd and then using Ansible Vault to encrypt the file we're storing our passwords.
To encrypt a file, it is as easy as executing ansible-vault create <filename> and then set a password (per file😞
Within above file, we can type in whatever we want to be encrypted.
Here are some random things I typed in:
After we save above file, we can confirm file is in fact encrypted:
To edit the file again we can issue ansible-vault edit <filename> command.
It only asks for our Vault's password that was initially set up when we first created the file:
To completely decrypt the file (if we ever need to) we can use ansible-vault decrypt <filename> command:
If we want to encrypt an existing file we use ansible-vault encrypt <filename> command.
A new password will be prompted:
There is also the option to create encrypted variables to add to our playbooks.