Using Conditionals, Handlers, and Loops

Building on previous exercises, this exercise introduces the application of conditionals, handlers, and loops in Ansible playbooks. You’ll learn to control task execution with conditionals, manage service responses with handlers, and efficiently handle repetitive tasks using loops. These are advanced features in Ansible that enhance control, efficiency, and flexibility in your automation playbooks.

Understanding Conditionals, Handlers, and Loops

  • Conditionals: Enable tasks to be executed based on specific conditions.

  • Handlers: Special tasks triggered by a notify directive, typically used for restarting services after changes.

  • Loops: Used to repeat a task multiple times, particularly useful when the task is similar but needs to be applied to different items.

Conditionals

Conditionals in Ansible control whether a task should run based on certain conditions. Let’s add to the system_setup.yml playbook the ability to install the Apache HTTP Server (httpd) only on hosts that belong to the web group in our inventory.

Previous examples had hosts set to node01 but now it is set to all. This means when you run this updated Ansible playbook you will notice updates for the new systems being automated against, the user Roger created on all new systems and the Apache web server package httpd installed on all the hosts within the web group.

---
- name: Basic System Setup
  hosts: all
  become: true
  vars:
    user_name: 'Roger'
    package_name: httpd
  tasks:
    - name: Install required packages
      ansible.builtin.package:
        name:
          - git
          - vim
        state: present

    - name: Create a new user
      ansible.builtin.user:
        name: "{{ user_name }}"
        state: present
        create_home: true

    - name: Install Apache on web servers
      ansible.builtin.package:
        name: "{{ package_name }}"
        state: present
      when: inventory_hostname in groups['web']

In this example, inventory_hostname in groups['web'] is the conditional statement. inventory_hostname refers to the name of the current host that Ansible is working on in the playbook. The condition checks if this host is part of the web group defined in your inventory file. If true, the task will execute and install Apache on that host.

Handlers

Handlers are used for tasks that should only run when notified by another task. Typically, they are used to restart services after a configuration change.

Let’s say we want to ensure the firewall is configured correctly on all web servers and then reload the firewall service to apply any new settings. We’ll define a handler that reloads the firewall service and is notified by a task that ensures the desired firewall rules are in place. Add the following tasks to the existing playbook to install firewalld and enable firewalld and reload the service with the help of handlers.

---
- name: Basic System Setup
  hosts: all
  become: true
  vars:
    user_name: 'Roger'
    package_name: httpd
  tasks:
    - name: Install required packages
      ansible.builtin.package:
        name:
          - git
          - vim
        state: present

    - name: Create a new user
      ansible.builtin.user:
        name: "{{ user_name }}"
        state: present
        create_home: true

    - name: Install Apache on web servers
      ansible.builtin.package:
        name: "{{ package_name }}"
        state: present
      when: inventory_hostname in groups['web']

    - name: Install firewalld
      ansible.builtin.package:
        name: firewalld
        state: present
      when: inventory_hostname in groups['web']

    - name: Ensure firewalld is running
      ansible.builtin.service:
        name: firewalld
        state: started
        enabled: true
      when: inventory_hostname in groups['web']

    - name: Allow HTTP traffic on web servers
      ansible.posix.firewalld:
        service: http
        permanent: true
        state: enabled
      when: inventory_hostname in groups['web']
      notify: Reload Firewall

  handlers:
    - name: Reload Firewall
      ansible.builtin.service:
        name: firewalld
        state: reloaded

The handler "Reload Firewall" is triggered only if the task "Allow HTTP traffic on web servers" makes any changes.

Notice how the name of the handler is used within the notify section of the "Allow HTTP traffic on web servers" configuration task. This ensures that the proper handler is executed as there can be multiple handlers within an Ansible playbook.
system_setup.yml output

Loops

Loops in Ansible allow you to perform a task multiple times with different values. This feature is particularly useful for tasks like creating multiple user accounts in our given example. In the original system_setup.yml playbook from the previous exercise, we had a task for creating a single user:

- name: Create a new user
  ansible.builtin.user:
    name: "{{ user_name }}"
    state: present
    create_home: true

Now, let’s modify this task to create multiple users using a loop:

- name: Create a new user
  ansible.builtin.user:
    name: "{{ item }}"
    state: present
    create_home: true
  loop:
    - alice
    - bob
    - carol

What Changed?

  1. Loop Directive: The loop keyword is used to iterate over a list of items. In this case, the list contains the names of users we want to create: alice, bob, and carol.

  2. User Creation with Loop: Instead of creating a single user, the modified task now iterates over each item in the loop list. The {{ item }} placeholder is dynamically replaced with each username in the list, so the ansible.builtin.user module creates each user in turn.

When you run the updated playbook, this task is executed three times, once for each user specified in the loop. It’s an efficient way to handle repetitive tasks with varying input data.

Snippet of the output for creating a new user on all the nodes.

User creation output

Summary

You’ve successfully completed the conditionals, handlers, and loops exercises! You now have:

  • Used conditionals with when to selectively install packages based on inventory group membership

  • Implemented handlers with notify to reload services only when configuration changes occur

  • Applied loops to efficiently create multiple user accounts in a single task

  • Combined all three features into a comprehensive system setup playbook