Before saying anything else, I must admit I wasn’t paying attention to Ansible for a very long time - far longer than I’m willing to admit. To be perfectly honest, I don’t think I would be able to write a single playbook myself last year.

Having that said, it appears I’m not the only one who hasn’t been paying too much attention to this great tool. Unfortunately lads, the time has come, there’s no going around it - IaaC is the future and we just have to skill up.

Now, don’t get too worried about it, as I already said, I had absolutely no idea how Ansible and other tools like that work a mere few months ago and it took me just a few weekends of learning to “get good”. I mean, just last week I passed a 100% Ansible-based Red Hat Certified Engineer exam and the only reason I didn’t pass it earlier is all exam centres were closed down March-June.

Without any further ado, let’s move on to the topic of this post - the basics of Ansible. This is going to be a first post in a (long?) series explaining how Ansible works and how to use it.

Most things covered in this article (excluding the Ansible glossary) are also available in video format: “Get stuff done quicker with Ansible”.

What is Ansible? Why should I care?

Ansible is an automation framework which relies on two technologies: SSH and Python. This makes Ansible very portable, agentless and easy to start with. For example, to start automating your own desktop, all you need is the ansible package which is widely available on all Linux distros and MacOS (and Windows 10 via WSL):

root@rhel.ansible.lab # yum install ansible
root@debian.ansible.lab # apt install ansible
root@arch.ansible.lab # pacman -S ansible
root@macrap.ansible.lab # pip install ansible
## etc etc etc

Once you have that installed, you can start using Ansible commands such as ansible, ansible-playbook, ansible-galaxy and - the holy grail of Ansible exams (more on that later) - ansible-docs.

For example, to get an overview of what Ansible already knows about your client machine (aka “facts” - more about that later too), we can run something like that:

ansible.lab # ansible localhost -m setup
localhost | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "10.0.0.1"
        ],
        "ansible_all_ipv6_addresses": [
            "fe80::xxx:xxxx:xxxx:xxxx"
        ],
(...)

In a more realistic scenario (client machine and single/many/sh*tload servers), all you’d have to do aside from installing ansible on the client, would be to make sure your server boxes have Python 2 or 3 installed and have SSH access via authorized keys enabled - it’s a required for your user on the client machine to have their SSH keys copied across to all target systems (how to do that).

Ansible files and glossary

At minimum, Ansible uses two files - both available in a template form under /etc/ansible - the configuration file (asnible.cfg) and an inventory file (hosts). Usually, we begin setting up an Ansible client by copting both of these files into a different directory where we won’t run into any permission or access issues:

vlku@client.ansible.lab : cp /etc/ansible/* /home/vlku/ansible/

This also makes sure we won’t lose the template files which can be useful if we ever mess up the config or forget how to create an inventory.

ansible.cfg

First file we’re going too look at is the Ansible config file - ansible.cfg. By default, Ansible first looks for this file in the current working directory, then in user’s home and lastly in /etc/ansible. This is important as it allows us to easily configure Ansible differently depending on the hosts we want to manage. For example, you could have one version of ansible.cfg saved in a “Production” folder along with a list of production systems and a second in a “Test” directory used for managing all the non-prod boxes.

A fresh, untouched copy of Ansible config can be viewed under this link: ansible.cfg

At this stage of this series, we’re not going to look too much into the different bits and pieces about ansible.cfg as this could be a post of its own. For now, I would like to highlight just a few lines which are important for starting up:

#### inventory sets a path to a file containing a list of all machines we would like to manage
#inventory      = /etc/ansible/hosts


#### the following four lines control the remote user settings

## change sudo_user to override the default user Ansible "su'es" to
## (by default it's root)
#sudo_user      = root

## control wheter Ansible should ask for a password when switching to a priveledged user
## (by default Ansible assumes our user appears in /etc/sudoers)
#ask_sudo_pass = True

## default user to use for playbooks if user is not specified
## (Ansible will use current user as default)
#remote_user = root

## uncomment this line if your target systems require a password on top of SSH-key authentication
#ask_pass      = True


#### what port should Ansible use for SSH
#remote_port    = 22


#### path to Ansible roles (very important for Ansible Galaxy)
#roles_path    = /etc/ansible/roles

Hopefully, you won’t have to change much to get started. In most of my deployments, the only two thing I edit in the config are the paths toinventory and roles.

inventory (aka hosts)

Right, we’ve got our Ansible config ready but what are our target systems? Well, that’s what the inventory file is for.

Inventory files can be a very controversial topic - some like their inventories automated, clean and efficient, while others might prefer having a list of IPs with no fancy Asnible magic. Thankfully, in Ansible we can do either:

vlku@client.ansible.lab : cat /home/vlku/ansible/basic/inventory
### Basic Inventory file
10.0.1.1
10.0.1.2
10.0.1.3
host1.ansible.lab
host2.ansible.lab
host3.ansible.lab


vlku@client.ansible.lab : cat /home/vlku/ansible/pro/inventory
### Pro Inventory file
[webservers]
web[01:10].ansible.lab

[dbservers]
db0[1:5].ansible.lab

[production:children]
webservers
dbservers

[testweb]
10.0.2.[1:50]

[testdb]
10.0.2.[1:10]

[test:children]
testweb
testdb

[allweb:children]
webservers
testweb

[alldb:children]
dbservers
testdb

[RHEL:children]
allweb
dbserver

[CentOS:children]
testdb

A fresh, untouched copy of Ansible inventory file can be viewed under this link: hosts

As already explained in the previous part of this post, we can tell Ansible where to look for the inventory in the ansible.cfg.

Ansible Glossary

With config and inventory files done, we can move on to all the different terms essential to understand Ansible. I already used some of them above but hopefully you were able to grasp the basics. Future posts will probably cover most of these terms in depth so don’t worry about understanding it all based on the short descriptions below:

1) PlaybooksA playbook is a YAML file which is used to carry out multiple operations (plays) against the target systems. You can think of a playbook as a to-do list which your hosts must follow.

1.1) PlaysAs mentioned above, plays are essentially what we tell the hosts to do. For example, a play could tell your hosts to change their hostname to a certain variable or to pull a file from your client machine.

1.2) VariablesJust as in programming, variables are “containers” for data. We can use variables to store complex data under a more recognisable name (i.e. instead of typing a very long filename many times in our playbook, we could store it in a variable calledlongname and spare our keyboard a lot of redundant keystrokes). Variables can also be used to store dynamic data which changes during playbook’s execution (current time, CPU usage in %, number of files in a directory etc.) or depending on the host (IP address, OS version, available disk space etc.)

1.2.1) FactsFacts are a special kind of variables about the hosts in the inventory which are discovered by Ansible. For example, we can use ansible_facts['hostname'] to quickly access system’s hostname when writing a playbook.

1.3) HandlersSometimes we only want to perform an action under certain cirmumstances and that’s exactly what handlers are for. Think how you’d only want a service to restart once all changes to the config are succesfully applied.

1.4) ModulesIn Ansible, you don’t really use bash scripting. Well, you kinda can do that but it’s not what Ansible is really for. Instead of making Ansible run loads of bash commands, what you should be doing is using modules. We’ll cover this in detail in a post about playbooks but for now think about modules as “Ansible alternatives” for pretty much every command you’d need to run against your hosts. You can view the index of Ansible modules under this link.

2) InventoryJust to have all terms in one place - in simple terms, an inventory is a list of target systems which will be managed with Ansible. You can have one or many inentory files on your client system which can be both as simple as a list of IPs and complex utilising groups, wildcards and much, much more.

3) Ansible ConfigAgain, just to keep this organised - an Ansible config defines all different parameters used during playbook execution and any other Ansible operation. There can be multiple configs on the client system.

4) RolesImagine you wrote a brilliant set of playbooks, variables, scripts etc. which when used together will build an endless supply of perfectly functioning webservers. With roles, you can “pack” all these files together for future use or share them out with Ansible community so others could bask in your greatness.

4.1) Ansible GalaxySpeaking of sharing roles with others, Ansible Galaxy is a place where you can both publish and download roles. It’s a huge, free to use marketplace of great roles allowing you to skip tedious work on basic setups and focus on what really you want to accomplish with Ansible.

5) Ansible VaultWhen writing a script, you often come to a point where you need to pass some important, vulnurable data (passwords!!!) over to the target system. The “old school” way of doing this comes with many disadvanteges - mainly, lack of security. Ansible vault is a way to address this by providing an easy method of encrypting data used in playbooks.

Ansible ad-hoc

To finish off this post, I’d like to go through the simplest of use of Ansible - the ad-hoc commands. While writing playbooks is what really defines your skill in Ansible, running ad-hoc commands is what makes you see how much Ansible can improve your work from day one.

To keep it simple, ad-hoc commands is a way of running Ansible the easy way. It won’t allow you to do anything fancy or super-efficient, but it’s still good enough for speeding up day-to-day operations.

In this post, I already used an Ansible ad-hoc command at the very beggining:

vlku@client.ansible.lab : ansible localhost -m setup

…which I described as a way to see what Ansible “knows” about the client system. In reality, that was the simplest ad-hoc Ansible command you could run. Let’s break it down:

  • The first bit -ansible - is used to launch ad-hoc commands;
  • Next, localhost, indicates what should be the target of this operation. We could replace it with all to run the command against all hosts in our inventory, or with a specigic group/host name;
  • Moving on, the -m switch is used to specify a module which should be used;
  • And finally, setup is the name of the module we want to use.

As you are now far more familliar with Ansible terminology, I will now explain a bit better what that command does - it is also a very important command which you will run loads of times when writing playbooks: it displays all Ansible facts available for the systems defined in your inventory. It is useful not only for seeing what info you can freely utilize in your scripts, but also an easy way of checking if your Ansible client can communicate with target systems (it is also risk-free since the setup module absolutely does not interfere with the target systems).

An example of a more “advanced” ad-hoc command could be something like this:

vlku@client.ansible.lab : ansible webservers -m service "name=httpd state=restarted"

As you can probably guess at this point, running this command (with a properly configured Ansible config, inventory etc.) would restart a httpd service on all target systems defined as members of the webservers group.

Closing thought

I hope you found this first post in the series useful and interesting. I would love to hear your feedback as I consider writing this multi-post guide a huge undertaking which while I hope will be fun for me, should also be understandable and useful to my readers. My DMs are open if you’d like to reach out @WilkITWizard