Ansible 101: Playbooks, Task Controls & Handlers
Tags: ansible, devops, automation, docker
Published: 2024-01-24 08:35:58

## What is a Playbook?

Ansible playbooks allow you to declare tasks to be performed on remote hosts.

### Overview of Playbook Structure:


![Playbook analogy. A pic of a cookbook represents the playbook, Each recipe in the cookbook is a play and the steps in the recipe are tasks. Modules are represented by an image of cooking utensils and ingredients.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6kft30h7pqqnw12q7r4d.png)

1. **Play:** specifies a group of tasks to be executed on a set of hosts.
    
2. **Task:** an action to be performed on the host.
    
3. **Module:** a piece of code for a specific task.
    

## What is Task Control & Handler?

These are methods of controlling how and when your automation scripts are executed. Task control lets you specify conditions for running tasks while handlers are a form of tasks that you trigger only when changes need to be applied.

### Task Control Overview

Task control in Ansible allows you to state the conditions under which your scripts should run. This can be implemented using conditionals such as:

**When**

This conditional allows you to execute a task only if it evaluates to true.

**Ansible Facts**

`ansible_facts` are variables containing information like network interfaces, operating system, IP addresses, and more, which are gathered from host machines and can be used in conditionals.

  

### Task Handlers Overview

Handlers are tasks set to run only when certain changes occur, such as when a service's configuration file is updated by another task.

**Notify**

The `notify` directive in a task triggers a handler if the task results in any changes to the host.

**Listen**

The `listen`  directive allows multiple tasks to notify the same handler even if they use different `notify` names.

  

## Practice

We'll start with two pre-configured inventory files. Inventory-webserver1.yaml contains two hosts while inventory-webserver2.yaml contains one host.

  

1. Clone the repo
    

```bash
git clone https://github.com/perplexedyawdie/ansible-learn.git
```

2\. Spin up the environment using docker-compose

```bash
docker compose up -d --build
```

3\. SSH into the Ansible server

```bash
ssh -o StrictHostKeyChecking=no -o NoHostAuthenticationForLocalhost=yes root@localhost -p 2200# password: test123
```

4\. Change directory to `ansible_learn`

```bash
cd ansible_learn
```

### Working with Playbooks

  
5\. Create playbook file

```bash

touch install_nginx.yaml

nano install_nginx.yaml

```

6\. Add the following config to install **nginx**

```yaml
# ansible_learn/install_nginx.yaml
# setting the state param to "present" will ensure that it is installed

- name: Install NGINX Web Server
  hosts: webserver1
  tasks:
    - name: Install NGINX
      ansible.builtin.apt:
        name: nginx
        state: present
```

7\. Run the linter

```bash
ansible-lint install_nginx.yaml

```

8\. Execute the playbook on `inventory-webserver1.yaml`

```bash
ansible-playbook --key-file /root/.ssh/id_rsa_ansible -u root -i inventory-webserver1.yaml install_nginx.yaml

```

9\. Confirm installation on server1 & server2 then check if it's installed on server3

```bash

ssh -i /root/.ssh/id_rsa_ansible root@server1 nginx -V
ssh -i /root/.ssh/id_rsa_ansible root@server2 nginx -V


ssh -i /root/.ssh/id_rsa_ansible root@server3 nginx -V
```

### Task Controls & Handlers

We want to create a playbook that installs Apache on servers that do not have a specific file (e.g., `/var/www/html/index.nginx-debian.html`), and then use a handler to restart Apache if it is newly installed or updated.

  
10\. Create & open the `install_nginx.yaml` file

```bash
touch conditional_install_apache.yaml

nano conditional_install_apache.yaml
```

11\. Add condition to install Apache if `/var/www/html/index.nginx-debian.html` doesn't exist then add a handler to restart Apache if it is newly installed.

```yaml

- name: Conditional Install and Restart Apache
  hosts: all
  tasks:
    - name: Check if NGINX Default Page Exists
      ansible.builtin.stat:
        path: /var/www/html/index.nginx-debian.html
      register: nginx_page

    - name: Install Apache
      ansible.builtin.apt:
        name: apache2
        state: present
      when: not nginx_page.stat.exists
      notify: Restart Apache

  handlers:
    - name: Restart Apache
      listen: "Restart Apache"
      ansible.builtin.service:
        name: apache2
        state: restarted

# The stat module checks for the existence of the default NGINX page. The result is registered in nginx_page.
# The installation task uses the when condition to install Apache only if the file does not exist.
# If the installation task runs, it notifies the handler to restart Apache.
# The handler listens for the notification and uses the service module to restart Apache.
```

12\. Run the linter

```bash
ansible-lint conditional_install_apache.yaml

```

13\. Execute the playbook on `inventory-webserver1.yaml` & `inventory-webserver2.yaml`

```bash
# webserver1
ansible-playbook --key-file /root/.ssh/id_rsa_ansible -u root -i inventory-webserver1.yaml conditional_install_apache.yaml

#webserver2
ansible-playbook --key-file /root/.ssh/id_rsa_ansible -u root -i inventory-webserver2.yaml conditional_install_apache.yaml
```

14\. Confirm installation on server3 then check if it's installed on server1 & server2

```bash

ssh -i /root/.ssh/id_rsa_ansible root@server1 apache2 -V
ssh -i /root/.ssh/id_rsa_ansible root@server2 apache2 -V


ssh -i /root/.ssh/id_rsa_ansible root@server3 apache2 -V
```
## Recap
Awesome effort! We just learned how to define Ansible playbooks and how to control the flow of execution using conditionals. Next, we'll look on variables, gathering facts about host machines and how to manage config files using templates.