Running a playbook
Now, it's time (yes, finally!) to run the playbook. To instruct Ansible to execute a playbook instead of a module, we will have to use a different command (ansible-playbooks) that has a syntax very similar to the ansible command we already saw:
$ ansible-playbook -i HOST, setup_apache.yaml
As you can see, aside from the host-pattern (that is specified in the playbook), which has disappeared, and the module option, which has been replaced by the playbook name, nothing has changed. So, to execute this command on my machine, the exact command is as follows:
$ ansible-playbook -i test01.fale.io, setup_apache.yaml
The result is the following:
PLAY [all] ***********************************************************
TASK [Gathering Facts] ***********************************************
ok: [test01.fale.io]
TASK [Ensure the HTTPd package is installed] *************************
changed: [test01.fale.io]
TASK [Ensure the HTTPd service is enabled and running] ***************
changed: [test01.fale.io]
PLAY RECAP ***********************************************************
test01.fale.io : ok=3 changed=2 unreachable=0 failed=0
Wow! The example worked. Let's now check whether the httpd package is installed and is now up-and-running on the machine. To check if HTTPd is installed, the easiest way is to ask rpm:
$ rpm -qa | grep httpd
If everything worked properly, you should have an output like the following:
httpd-tools-2.4.6-80.el7.centos.1.x86_64
httpd-2.4.6-80.el7.centos.1.x86_64
To see the status of the service, we can ask systemd:
$ systemctl status httpd
The expected result is something like the following:
httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
Active: active (running) since Tue 2018-12-04 15:11:03 UTC; 29min ago
Docs: man:httpd(8)
man:apachectl(8)
Main PID: 604 (httpd)
Status: "Total requests: 0; Current requests/sec: 0; Current traffic: 0 B/sec"
CGroup: /system.slice/httpd.service
├─604 /usr/sbin/httpd -DFOREGROUND
├─624 /usr/sbin/httpd -DFOREGROUND
├─626 /usr/sbin/httpd -DFOREGROUND
├─627 /usr/sbin/httpd -DFOREGROUND
├─628 /usr/sbin/httpd -DFOREGROUND
└─629 /usr/sbin/httpd -DFOREGROUND
The end state, according to the playbook, has been achieved. Let's briefly look at exactly what happens during the playbook run:
PLAY [all] ***********************************************************
This line advises us that a playbook is going to start here, and that it will be executed on all hosts:
TASK [Gathering Facts] ***********************************************
ok: [test01.fale.io]
The TASK lines show the name of the task (setup, in this case), and its effect on each host. Sometimes, people get confused by the setup task. In fact, if you look at the playbook, there is no setup task. This is because Ansible, before executing the tasks that we have asked it to, will try to connect to the machine and gather information about it that could be useful later. As you can see, the task resulted in a green ok state, so it succeeded, and nothing was changed on the server:
TASK [Ensure the HTTPd package is installed] *************************
changed: [test01.fale.io]
TASK [Ensure the HTTPd service is enabled and running] ***************
changed: [test01.fale.io]
These two task's states are yellow, and spell changed. This means that those tasks were executed and have succeeded, but they have actually changed something on the machine:
PLAY RECAP ***********************************************************
test01.fale.io : ok=3 changed=2 unreachable=0 failed=0
Those last few lines are a recapitulation of how the playbook went. Let's rerun the task now and see the output after both the tasks have actually run:
PLAY [all] ***********************************************************
TASK [Gathering Facts] ***********************************************
ok: [test01.fale.io]
TASK [Ensure the HTTPd package is installed] *************************
ok: [test01.fale.io]
TASK [Ensure the HTTPd service is enabled and running] ***************
ok: [test01.fale.io]
PLAY RECAP ***********************************************************
test01.fale.io : ok=3 changed=0 unreachable=0 failed=0
As you would have expected, the two tasks in question give an output of ok, which means that the desired state was already met prior to running the task. It's important to remember that many tasks, such as the gathering facts task, obtain information regarding a particular component of the system and do not necessarily change anything on the system; so, these tasks didn't display the changed output earlier.
The PLAY RECAP section in the first and second run are shown as follows. You will see the following output during the first run:
PLAY RECAP ***********************************************************
test01.fale.io : ok=3 changed=2 unreachable=0 failed=0
You will see the following output during the second run:
PLAY RECAP ***********************************************************
test01.fale.io : ok=3 changed=0 unreachable=0 failed=0
As you can see, the difference is that the first task's output shows changed=2, which means that the system state changed twice due to two tasks. It's very useful to look at this output, since, if a system has achieved its desired state and then you run the playbook on it, the expected output should be changed=0.
If you're thinking of the word idempotency at this stage, you're absolutely right and deserve a pat on the back! Idempotency is one of the key tenets of configuration management. Wikipedia defines idempotency as an operation that, if applied twice to any value, gives the same result as if it were applied once. The earliest examples of this that you would have encountered in your childhood would be multiplicative operations on the number 1, where 1*1=1 every single time.
Most of the configuration management tools have taken this principle and applied it to the infrastructure as well. In a large infrastructure, it is highly recommended to monitor or track the number of changed tasks in your infrastructure and alert the concerned tasks if you find oddities; this applies to any configuration management tool in general. In an ideal state, the only time you should see changes is when you're introducing a new change in the form of any Create, Remove, Update, or Delete (CRUD) operation on various system components. If you're wondering how you can do it with Ansible, keep reading the book and you'll eventually find the answer!
Let's proceed. You could have also written the preceding tasks as follows, but when the tasks are run from an end user's perspective, they are quite readable (we will call this file setup_apache_no_com.yaml):
---
- hosts: all
remote_user: vagrant
tasks:
- yum:
name: httpd
state: present
become: True
- service:
name: httpd
state: started
enabled: True
become: True
Let's run the playbook again to spot any difference in the output:
$ ansible-playbook -i test01.fale.io, setup_apache_no_com.yaml
The output would be as follows:
PLAY [all] ***********************************************************
TASK [Gathering Facts] ***********************************************
ok: [test01.fale.io]
TASK [yum] ***********************************************************
ok: [test01.fale.io]
TASK [service] *******************************************************
ok: [test01.fale.io]
PLAY RECAP ***********************************************************
test01.fale.io : ok=3 changed=0 unreachable=0 failed=0
As you can see, the difference is in the readability. Wherever possible, it's recommended to keep the tasks as simple as possible (the KISS principle: Keep It Simple, Stupid) to allow for the maintainability of your scripts in the long run.
Now that we've seen how you can write a basic playbook and run it against a host, let's look at other options that would help you while running playbooks.