Ubuntu Server 18.04 – Making your servers do your bidding

How to manage remote IIS on Windows Server 2019

As server administrators, we’re control freaks. There are few things more exciting than executing a command and having every single server obey it and carry it out. Now that we have Ansible set up, that’s exactly what we’re going to do. I’m assuming by now you have some machines you want to configure, and they’re all set up to communicate via SSH with your central server. Also, as I mentioned before, I highly recommend you utilize something like Git to store your configuration files, but that’s not required for this section.

First, we’ll need an inventory file, which is a special text file Ansible expects to find that tells it where to find servers to connect to. By default, the name of this file is simply hosts and Ansible expects to find this file in the /etc/ansible directory.

There’s actually a way to avoid needing to create an inventory file, which we’ll get into later in this chapter.

With the inventory file, I highly recommend that you don’t include it in your Ansible Git repository. Reason being, if a miscreant is looking at it, they will be able to glean important information regarding the layout of your internal servers. This file is only needed on the server that’s controlling the others anyway, so it shouldn’t be an inconvenience to not have it in the repository.

A sample inventory file will be created by default when you install the ansible package. If for some reason it doesn’t exist, you can create the file easily with the following command:

sudo touch /etc/ansible/hosts

We should also make sure that only the Ansible user account can read it. Execute the following command to change ownership (replace ansible with whatever user account you chose as your Ansible account if you’re using something different):

chown ansible /etc/ansible/hosts

Next, modify the permissions such that only the owner can view or change the file:

chmod 600 /etc/ansible/hosts

Next, populate the file with the IP addresses of the servers you wish to manage. It should look similar to the following:

That’s it; it’s simply just a list of IP addresses. I bet you were expecting some long configuration with all kinds of syntax requirements? Sorry to disappoint. All you need to do is copy a list of IP addresses of the servers you want to manage into this file. If you have DNS names set up for the machines you want to configure, you can use those instead:


Since Ansible understands IP addresses as well as DNS names, we can use either or a combination of both in order to set up our inventory file. We can also split up our hosts within the inventory file between different roles, but that is outside the scope of this tutorial. I do recommend learning about roles in Ansible if you wish to take your knowledge further.

If you decide not to store your inventory file at /etc/ansible/hosts, you must tell Ansible where to find it. There is another important file to Ansible, and that is its configuration file, located at /etc/ansible/ansible.cfg. Inside this file, we can fine-tune Ansible to get the best performance possible. While we won’t go over this file in detail, just know that you can seriously increase the performance of Ansible by fine-tuning its settings, and Ansible will read settings from its configuration file every time it runs. In our case, if we wish to store our inventory file somewhere other than /etc/ansible/ansible.cfg, we will need to add the following two lines to this file (create the file if it doesn’t exist already):

inventory = /path/to/hosts

As you can see, we’re basically telling Ansible where to find its inventory file. There are many more configuration items we can place in the ansible.cfg file to configure it further, but that’s all we need to configure right now.

Similar to the inventory file, Ansible also checks the local directory for a file named ansible.cfg to fetch its configuration, so you could actually include the configuration file in the Git repository as well, and then execute Ansible commands from within that directory. This works because Ansible will check for the existence of a configuration file in your current working directory, and use it if it’s found there. You may want to be careful of including your configuration file in your Git directory, though. While it’s not as private as the inventory file, it can potentially contain privileged information. Therefore, you may want to keep the file at /etc/ansible/ansible.cfg and mange it outside of the Git repository if you include anything private in the file (for example, encryption keys).

Now we can test out whether or not Ansible is working at this point. Thankfully, this is also easy. Just simply execute the following command:

ansible all -m ping

The results should look similar to this: | SUCCESS => {
  "changed": false,
  "ping": "pong"

Depending on how many servers you have set up, you should see that output one or more times. If it fails, double-check that the hosts are available over SSH. Success here simply means that Ansible is able to communicate with your hosts. Now that the communication exists, we can actually build some actual configuration.

Ansible uses something called a Playtutorial in order to store configuration. A Playtutorial is essentially another name for a file in the YAML format. YAML itself is beyond the scope of this tutorial, but you don’t have to master this format or even fully understand it to use it with Ansible. That will automatically come in time. The takeaway here is that YAML is simply the format that Ansible uses. A Playtutorial is basically just a collection of instructions written in this format, and each individual instruction is known as a Play. You can think of this with the analogy of a sport, like football. Although I don’t know the first thing about football, I do know that football coaches have Playtutorials containing things that they want their players to do, and each action by a player is a play. It’s the same concept here.

Let’s write our first Playtutorial. Create a file called packages.yml in your local Ansible directory. You can fill it with this content (make sure you include the hyphens):

- hosts: all
  become: true
  - name: Install htop
    apt: name=htop

We can run this Playtutorial with the following command:

ansible-playtutorial packages.yml

Just like that, all of the hosts in your inventory file will have the htop package installed. It really doesn’t matter which package you install, so long as it exists in the repositories; I just used htop as a simple example. But once you run it, you should see an overview as far as what was changed. Ansible will tell you how many items your hosts were updated, how many tasks have failed, and how many targets weren’t reachable at the time the Playtutorial was run.

Let’s take a closer look at what the instructions in this sample Playtutorial do. The hyphens at the beginning are part of the YAML format, so we really don’t need to get into that. Spacing is very important with the YAML format, as you need to be consistent throughout. In my examples, I am inserting two spaces underneath each heading. A heading starts with a hyphen:

- hosts: all

Here, we declare which hosts we want to have the commands apply to. I added all here, which basically runs the configuration against every host in the inventory file. With advanced usage, you can actually create roles in Ansible and divide your hosts between them, such as a web server, database server, and so one. Then, you can have configuration only be applied to hosts inside a particular role. We’re not going to get into that in this chapter, but just know that it is possible:

become: true

This line is basically Ansible’s term for describing sudo. We’re telling Ansible to use sudo to execute the commands, since installing packages requires root privileges:


This line starts the next section, which is where we place our individual tasks:

- name: Install htop

With name, we give the Play a name. This isn’t required but you should always include it. The importance of this is that whatever we type here is what is going to show up in the logs if we enable logging, and will also print to the Terminal as the Play runs. We should be descriptive here, as it will certainly help if a Play fails and we need to locate it in a log file that has hundreds of lines:

apt: name=htop

Next, we utilize the apt module and tell it to install a package, htop in this case. We use the apt module simply because Ubuntu uses the apt command to manage packages, but modules exist for all of the popular Linux distributions. Ansible’s support of package managers among various distributions is actually fairly extensive. All of the major distributions, such as Red Hat, Fedora, OpenSUSE, Arch Linux, Debian, and more are supported (and those are just the ones I’ve used in my lab off the top of my head). If you want to execute a Play against a server that’s running a distribution other than Ubuntu, simply adjust apt to something else, such as yum or dnf.

You can, of course, add additional packages by simply adding more Plays to the existing Playtutorial:

- hosts: all
  become: true
  - name: Install htop
    apt: name=htop
  - name: Install git
    apt: name=git
  - name: Install vim-nox
    apt: name=vim-nox

However, I think that is very sloppy. I’ll show you how we can combine multiple similar Plays in one Play. Sure, you don’t have to, but I think you’ll agree that this method looks cleaner:

- hosts: all
  become: true
  - name: Install packages
    apt: name={{item}}
      - htop
      - git
      - vim-nox

With the new format, we include just one Play to install multiple packages. This is similar to the concept of a for loop if you have programming knowledge. For every package we list, it will run the apt module against it. If we want to add additional packages, we just add a new one to the list. Simple.

We can also copy files to our hosts as well. Consider the following example Playtutorial, which I will call copy_files.yml:

- hosts: all
  become: true
  - name: copy SSH motd
    copy: src=motd dest=/etc/motd

Inside the same directory, create a file called motd and place any text in it. It doesn’t really matter what you type into the file, but this file in particular acts as a message that is printed any time a user logs into a server. When you run the Playtutorial, it will copy that file over to the server at the destination you configured. Since we created a message of the day (motd), we should see the new message the next time we log in to the server.

By now, you’re probably seeing just how useful Ansible can be. Sure, we only installed a few packages and copied one file. We could’ve performed those tasks easily ourselves without Ansible, but this is only a start. Ansible lets you automate everything, and we have to start somewhere. You can tell it to not only install a package, but also start the service for it. You can also do things such as keep packages up to date, copy a configuration file, set up a template, and so much more—it will surprise you. In fact, you can go as far as to automate the setup of a web server, a user’s workstation… you name it!

Comments are closed.