Mastering Ansible Playbooks: A Guide to Task Controls & Handlers

Written by cloudkungfu | Published 2024/01/24
Tech Story Tags: ansible | devops | automation | docker | ansible-playbooks | ansible-modules | task-controls | infrastructure-as-code

TLDRAnsible playbooks allow you to declare tasks to be performed on remote hosts. Task control lets you specify conditions for running tasks. Handlers are tasks set to run only when certain changes occur, such as when a service's configuration file is updated by another task. via the TL;DR App

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

Overview of Playbook Structure:

  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 are 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 Exercises

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
git clone https://github.com/perplexedyawdie/ansible-learn.git

2. Change the directory to playbooks-task-control-handlers

cd ansible-learn/playbooks-task-control-handlers

  1. Spin up the environment using docker-compose
docker compose up -d --build

4. SSH into the Ansible server

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

Working with Playbooks

  1. Create playbook file
cd ansible_learn

touch install_nginx.yaml

nano install_nginx.yaml

  1. Add the following config to install nginx
# 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

  1. Run the linter
ansible-lint install_nginx.yaml

  1. Execute the playbook on inventory-webserver1.yaml
ansible-playbook --key-file /root/.ssh/id_rsa_ansible -u root -i inventory-webserver1.yaml install_nginx.yaml

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

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.

  1. Create & open the install_nginx.yaml file
touch conditional_install_apache.yaml

nano conditional_install_apache.yaml

  1. Add a 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.

- 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.

  1. Run the linter
ansible-lint conditional_install_apache.yaml

  1. Execute the playbook on inventory-webserver1.yaml & inventory-webserver2.yaml
# 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

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

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.

Also published here.


Written by cloudkungfu | Sharing DevOps thoughts, ideas & experiments.
Published by HackerNoon on 2024/01/24