Ansible From A to M

Author: Xavier C Proofreader(s): Jilliam S, and Jason M

Introduction

This is just a brief summarization on how to at least get a foot in the door for learning ansible. This "guide" doesn't cover any particular lab, but if you're going off of Areeb's labs, it will help for any ansible lab after the 4th one. This is more of a reference rather than a complete guide aimed to be at least slightly fun, or visually appealing to read.

What I aim to teach

This guide should familiarize you with the general usage of Ansible. Such as:

Guide reference

In this guide. I will be using a Mint 18.2 host and a Debian 10 target. Both of the machines are isolated in a VirtualBox NAT with its own addressing scheme. This environment is all based on what was setup on my previous guide. Some things may change from target to target, or even from host to host. But you should still be able to scrounge up the pieces to get Ansible working in your environment! Debian 10 Mint 18.2

 

SSH key generation and user management

You may be thinking why SSH is so important for Ansible. If you wanted to configure machines, you'd just SSH into them manually and type all the commands out no problem. But scale this a couple hundred more times, and you'll have a problem on your hands. SSH is the gear that gets ansible moving. Ansible relies on SSH to actually do things on the machine.

SSH key generation

Generating an SSH key makes connecting to the target machine from the host easier. It also prevents the need to install additional programs like sshpass. First we need to generate an ssh key on our Ansible host machine:

MAKE SURE TO NOT RUN THIS COMMAND IN SUDO!!

After typing in the command, you just need to spam enter until you see some super cool art on the screen. What this does is generate a public and private SSH key for our host Ansible machine to use.

SSH user management

Now the only information we need to know is what user we want to use on the target machine, and what the IP of the target machine is. The command looks like this:

MAKE SURE TO NOT RUN THIS COMMAND IN SUDO!!

Let's analyze this command a bit more. When I specified (user here), this will be the account you want to use for the Ansible target. So you must ensure that this account has sudo privileges, or is a root user. If not, the only thing useful the user can do is use the ping module. The other part after the @ sign specifies the IP address of the target machine. If you have multiple or different users on the target machine, you may run this command again to have keys for all the users you need.

Using the root user

Sometimes, you want to use the root user so you don't have to specify the become option in your playbook. This requires some changes on the target machine. Sometimes you need to edit your sshd_config file (located usually in /etc/ssh/sshd_config) and add in PermitRootLogin yes. Other times, your distro may just not set a root password. So you would need to specify a new one with sudo passwd root. You can find an example of doing permit root login in my first guide. Or a use case of setting a root password on Michael's guide.

"Chapter" Reference (aka: How Xav remembers things)

Target: A target refers to the machine you intend to manage and modify with ansible. Host: A host refers to the machine you installed ansible on, and will be using to manage other machines Become: A term used to specify that you want to run something in root. "Become" root. Or sudo Module: A module is used in a playbook to reference a command or action you want to use. Playbook: A set of instructions used by ansible to specify what to run and what target to run it on

 

Inventory and You (The Ansible "inventory" system)

So what exactly is an "inventory"? And why do I keep putting it in quotes? To wrap it neatly with a bow, it's really just a text file that contains the IP address of the machine you want to manage. We call that machine you want to manage a target machine. Of course there are different ways you can go about doing this. Some more specified than others, so here's all of the ones I know how to do:

A very basic ansible inventory

For the next couple of examples, we will be viewing a file on the host Ansible machine. This would be the mint 18.2 VM. We will be viewing a file located in /etc/ansible/hosts. Keep in mind that anytime you edit a file located inside /etc/, you will need to open it in sudo.

Here is an example of a very basic ansible inventory:

As you can see, this is plainly an IP address at the end of a text file. If we wanted to reference this when doing an ansible ping test, we would type in a command like this:

Let's analyze the structure of this command. The -m part of the command specifies what module we want to execute on the target machine. In this case, it is the ping module. It requires no special permissions to execute as this is just testing our connection to the target machine. The -u part of the command specifies what user to ssh in with. This is important because with this super basic inventory, we don't reference a user or anything! We can do a little bit better than that.

A spicy ansible inventory (A medium level ansible inventory)

In this one, I specified 2 things. The IP address of the target machine, and the user of that target machine we're going to use. This line puts both of those statements into an alias. So when running a ping test, I only need to do type this:

Specifying a group

In most cases, you won't be just applying changes to 1 machine. After all, this automation comes in handy when you're working with 2 or more computers. So instead of specifying just 1 host, we can shove IPs under a group like this:

In this example, I put some IPs and a hostname under some brackets. I also defined the user to SSH with for the entire group at the bottom. There is another way of adding this if you want to slightly segment your configuration. You know, leave the targets with targets, and variables with variables. We can actually specify a file for variables. Let's say my group is called "BeegGroup". I would need to edit a file in /etc/ansible/group_vars/BeegGroup. And in the file, I would add this line:

The gist of that is you need to create a file in /etc/ansible/group_vars/ with your group's name to specify variables for that specific group.

Testing the group is the same as testing the "spicy inventory". We just specify the group when running the ansible command:

 

Your first Ansible playbook!

Now that we've setup your inventory, we can now finally begin making changes to the target machine! There are a couple rules when it comes to creating these. But it all boils down to "can you write a yaml file".

The first little steps

Here is an example of the first part of a playbook:

The 3 dashes in the beginning specify the beginning of a YAML file. I put them in the start of all my YAML files, so you will also put them in the start of all your YAML files. The second line begins with a dash. What this tells me is it's the name of a specific task we want to execute. But instead of a task, we're just specifying a few things to get the playbook working. The hosts line specifies which target group or computer this playbook will run on. If you just have a plain IP address in there, you're gonna need to also specify a user when running the ansible-playbook command like this:

The become section at the bottom tells the target computer that we want to run everything in sudo. This is necessary for when you're running modules like apt or when copying files to special directories.

 

What do I run? (Actual tasks and basic module usage)

Now I'm going to modify the above playbook to include more information:

In this example, I added the apt module and specified a couple things to it. First of all, I have a name for the task "apache install". These can pretty much knock out the need to comment your playbook unless you name the task "Install stupid app" or something not very descriptive. I also tell ansible that this task needs sudo privileges with the "become" keyword. Then I tell the task that I want to run the "apt" module. Under the apt module, I tell it to update my cache, and install the package labeled "apache2".

 

When messing around with modules, sometimes you don't have to specify what you think you need to specify. For example, I can modify that playbook to just be this:

It's much shorter and still technically specifies what I want it to do. But I am just missing certain things that might not make the task succeed. If you're familiar with the apt package manager, I will compare what these 2 playbooks accomplish on the command line level:

The first playbook:

The second playbook:

The YAML spacing

This is the bane of my existence being an average tab enjoyer. Now I haven't tested this, but I'm sure due to how YAML works, you need to use spaces instead of tab. If you have an editor like VIM or some other advanced text editor, you can convert tab to become 4 spaces instead. I'll show the above playbook again, but I will comment the amount of spaces I used:

As you can see, the spacing keeps incrementing by 2 depending on how deep it goes.

So how do I actually use this YAML file? (Running the playbook)

Now that we've put in our blood sweat and tears into a YAML file, we can finally tell ansible to execute it on the target vm. For this example, I assume you have a "Spicy Ansible Inventory" or a group that specifies an ansible_user variable already. If you're running this with an ansible user that isn't root. The command will look like this:

Which will prompt you to enter your sudo password. However, if you feel like you're too good to just be a normal user and want to run everything in root, you can remove the --ask-become-pass part of the command because you would already be root:

 

Variables in Ansible

Essentially, variables are placeholders for passwords, names, or anything really. The neat thing about ansible playbooks is we can define a wack load of variables, and it will just dynamically change the script depending on what you set and how you set it.

Gathering the facts (Variables Part 1)

What exactly are these facts? They're just bits of information about the target collected by ansible. If you want to see a list of all available default variables to use, just run this command:

It will show something like this:

These are default variables generated with the setup module. So if you wanted to reference a certain aspect about a target, lets say their IP address. You would do it like this in a playbook:

This example kinda sucks. But the naming would change to "Installing for (ip address of target)". Say you're installing a webserver, and need to display or reference the target's ip address. You can use these default ansible variables to specify the ip address, especially if it changes all the time.

Customized variables in a playbook (Variables Part 2)

Sometimes we want to specify something more than just the target's properties. Say we use a password for a database, or want to name a database a specific thing. We can solve this using custom variables defined inside of your playbook. Here is an example:

In this example, we define a custom variable for a password. This can be called by using "{{ stupidpassword }}". This may not be useful for just plainly defining it in a playbook. But it becomes useful when using it in the template module. Actually, speaking of which..

 

Modules in Ansible

Modules are the building blocks that make ansible go! They're little small tools that build up to something bigger like a LAMP stack, or even a full wordpress site! There are a multitude of modules to use in ansible, you can find the full list and documentation here. But I will just list and give examples of modules that I find the most useful. It may be daunting at first, but scrounging up little pieces of examples and googling errors is exactly how I learned to use ansible:

The apt module

Module Reference

The apt module is useful for installing packages on Debian based distributions. Whether it be on Debian, Ubuntu, Mint, Elementary, basically any non-specialty distro that uses apt as it's package manager. Here is an example of how this module would look like in a playbook:

What this is telling ansible is, "update my package repository. then install a package named apache2". Or in command form:

We can also tell the apt module to install many packages by changing up the structure a little bit:

Which is equivalent to:

The template module

Module Reference

This module allows us to do magical things with the variables we defined at the top of the playbook. You know, like place them into files. Usually we would create a playbook yaml file in a folder, and also create a folder within that one called "files". This is where we'd store our template files (files that end in the .j2 extension) and other things we'd like to copy to the target machine. Lets say we want to create a file named foo.txt inside the /tmp folder on the target machine. In this file, we want to place a password, and the machine's hostname. First we create foo.txt.j2 inside the files folder. Then we modify it to look like this:

And the playbook would look like this:

Upon running this playbook and inspecting the file we created with the module on the target machine, it would look like this:

The copy module

Module Reference

Essentially, the copy module is the exact same thing as the template module, but it is solely used for copying, not shoving variables into template files. Again, this example references a folder called "files" which would be in the same folder the playbook yaml file is in:

The sqluser and sqldb module(s)

mysql_user reference

mysql_db reference

!!HEADS UP!!

FOR DEBIAN 10, YOU WILL NEED TO INSTALL THE python3-pymysql AND python-mysqldb PACKAGES ON THE TARGET MACHINE

Protip: Use the apt module to install these packages on the target machine

The mysql_user and mysql_db modules deal with the mariadb backend. This includes creating sql users, managing databases, and setting the sql root password. Here is an example of setting the root sql password using the mysql_user module:

And here's an example of the mysql_db module. We will be using the module to create a database using that root account:

The shell module

Module Reference

The shell module is what I like to call the "catch all" module. Why do I call it that? Because the purpose of this module is to literally run a shell command. Now imagine if I didn't know how to use the get_url module in my playbook. But I do know that the target machine has wget. It'd be safe to do this:

What this does is downloads whatever is contained in that url, and stores it in /tmp on the target machine.

When running commands in this module, sometimes ansible will tell you what module to actually use. You can choose to try and change your script to match ansible compliance, or you can be glad that your playbook just works.

 

Troubleshooting Ansible

A lot of the times, your playbook won't work. Or something out of the ordinary will happen to you. Fortunately, there's always a way to solve these issues. Whether its pressing, or just really annoying.

How to correctly google the error (Part 1 of Troubleshooting)

When running the playbook and receiving an error, ansible will show text in red. Sometimes it will be just fine and you can ignore it. (especially when its the last task in the playbook and its something like deleting some temp files) But other times, it will prevent you from progressing any further.

When googling the error, you want to make sure to strip any information relating to you or your machine. Take this error for example:

Now you can't just google this entire string. Google will be so confused about what you're asking. In this case, you can trim it down to this:

The way this text is formatted looks like its an ansible specific error. Or you can strip it down even further to this:

Furthermore, you can enable debugging details to further analyze the error and get a better idea of what to do by appending -vvv to the end of an ansible or ansible-playbook command.

Seeing how someone else did it (Part 2 of Troubleshooting)

Hey, sometimes you really just don't know how to accomplish a certain thing in ansible. But chances are, somebody already went through the same thing, or already has a full playbook. Lets say I wanted to deploy a LEMP stack to a target, but I don't know how to go about doing that. Fortunately, google lead me to a premade LEMP stack playbook. Now from here, I can choose to just copy everything this person did. Or I peak through the files and see how they accomplished the specific thing I couldn't do.

How to ask for help (Part 3 of Troubleshooting)

When worse comes to worse. You're just genuinely confused on how to go about fixing a specific error you have. But don't fret! You can ask somebody you think knows the answer like another peer or the teacher! You just need to make sure you provide all the relevant details.

When asking your question, include the following:

Trust me, its easier to respond when you include those details and ask a question like: "I tried to use the mysql_user module to create a user, but the step keeps failing on this error message". It's more helpful than if you just posted a screenshot with the message "idk what's wrong". It's good to be descriptive, but don't be overly descriptive. Example: "Hey so I ran into this error with the error code 0x38134. I ran the program at 1643771299 UNIX time (UTC) on a computer supporting an intel core i5 processor running....".

Conclusion

Ansible is a whole lot of fun when you get to understanding it. So whether its your first time using ansible, or you're just skimming this to complete the lab. I hope you actually understand and have fun with it!

-Xav :)