How to Use Handlers in Ansible Playbooks

Ansible Handlers Example

Handlers in Ansible are special tasks triggered by the notify directive within a playbook. These tasks are executed only when the associated task makes a change on the target system. Unlike regular tasks, handlers help optimize resource usage by ensuring idempotency in configuration management.

Ansible Handlers allow you to trigger specific actions based on the results of a task. They are commonly used for tasks such as restarting services, reloading configurations, or notifying dependent systems when a task has been executed successfully.

In this article, we’ll dive deep into what handlers are, their syntax, usage, and how to leverage them in real-world examples.

Basic Syntax of Handlers

Handlers are declared under the handlers section in a playbook. Below is a basic syntax:

- name: Demonstrate Basic Syntax of Handlers
  hosts: web
  tasks:
    - name: Update web server configuration
      template:
        src: /path/to/template.j2
        dest: /etc/nginx/nginx.conf
      notify: Restart NGINX

  handlers:
    - name: Restart NGINX
      service:
        name: nginx
        state: restarted

In this example:

  • A template task updates the nginx.conf file.
  • The notify directive calls the Restart NGINX handler, which restarts the NGINX service.

Using Handlers in Ansible Playbooks

Here’s a step-by-step guide to using handlers effectively:

Step 1: Create a Playbook

Start with defining your tasks and specifying the notify keyword for any task that requires triggering a handler.

Step 2: Define the Handlers

Handlers should be placed in the handlers section of the playbook, ensuring they match the names used in notify.

In this Example Playbookthe copy module copies the nginx.conf configuration file to the remote host and restart the Nginx service.

---
- name: Manage web server
  hosts: web
  tasks:
    - name: Copy NGINX configuration
      copy:
        src: nginx.conf
        dest: /etc/nginx/nginx.conf
      notify: Restart NGINX

    - name: Install NGINX
      apt:
        name: nginx
        state: present

  handlers:
    - name: Restart NGINX
      service:
        name: nginx
        state: restarted

Step 3: Run the Playbook

Execute the playbook using:

 # ansible-playbook playbook.yml

If the copy task modifies the NGINX configuration file, the handler will restart the service. Otherwise, the handler won’t run.

Using Multiple Handlers for Different Notifications

Multiple handlers are defined in the handlers section of your playbook. Each handler must have a unique name and can be notified by one or more tasks.

Here is an example:

- name: Use Multiple Handlers for Different Notifications
  hosts: web
  tasks:
    - name: Update NGINX configuration
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify:
        - Restart NGINX
        - Reload Firewall

    - name: Update firewall rules
      copy:
        src: firewall.rules
        dest: /etc/firewall.rules
      notify: Reload Firewall

  handlers:
    - name: Restart NGINX
      service:
        name: nginx
        state: restarted

    - name: Reload Firewall
      command: firewall-cmd --reload

This playbook updates the NGINX configuration using a template file and notifies handlers to restart NGINX and reload the firewall.

Handling Task Errors

It’s essential to ensure that errors in the notified tasks are handled gracefully to avoid service disruption or inconsistent states. Below are some strategies to manage task errors effectively in Ansible playbooks:

1. Use ignore_errors

The ignore_errors directive can be applied to a task to allow the playbook to continue execution even if the task fails.

- name: Handle Task Errors with ignore_errors
  hosts: web
  tasks:
    - name: Copy configuration file
      copy:
        src: nginx.conf
        dest: /etc/nginx/nginx.conf
      notify: Restart NGINX
      ignore_errors: yes

  handlers:
    - name: Restart NGINX
      service:
        name: nginx
        state: restarted

This playbook will continue to run even if the copy task fails. However, be cautious when using this directive, as it may hide critical issues.

2. Use failed_when to Define Failure Conditions

By default, Ansible determines task success or failure based on the return code. However, you can customize these conditions using failed_when.

Here is an example playbook:

- name: Handle Task Errors with failed_when
  hosts: web
  tasks:
    - name: Validate configuration file syntax
      command: nginx -t
      notify: Restart NGINX
      failed_when: "'syntax is ok' not in stdout"

  handlers:
    - name: Restart NGINX
      service:
        name: nginx
        state: restarted

In this example:

The task fails only if the string ‘syntax is ok’ is missing from the command output, ensuring the handler is triggered only for valid configurations.

3. Use rescue and always Blocks

Ansible provides error handling blocks (rescue and always) to handle failures more granularly.

Here is an example:

- name: Handle Task Errors with rescue and always blocks
  hosts: web
  tasks:
    - name: Copy configuration file with error handling
      block:
        - name: Copy configuration file
          copy:
            src: nginx.conf
            dest: /etc/nginx/nginx.conf
          notify: Restart NGINX
      rescue:
        - name: Handle copy task failure
          debug:
            msg: "Failed to copy configuration file. Skipping handler."
      always:
        - name: Log completion message
          debug:
            msg: "Task completed, regardless of the outcome."

  handlers:
    - name: Restart NGINX
      service:
        name: nginx
        state: restarted

Explanation:

  • block: Contains tasks that might fail.
  • rescue: Executes if a task in the block fails, preventing the handler from being triggered unnecessarily.
  • always: Runs regardless of task success or failure, ensuring cleanup or logging.

4. Log Task and Handler Errors

Logging errors can help identify issues quickly. Use the debug module with when conditionals to log outputs or error messages.

Here is the example playbook:

- name: Log Task Errors for Debugging
  hosts: web
  tasks:
    - name: Copy configuration file
      copy:
        src: nginx.conf
        dest: /etc/nginx/nginx.conf
      notify: Restart NGINX

    - name: Log error message
      debug:
        msg: "Task failed to copy configuration file!"
      when: copy_task_failed is defined and copy_task_failed

  handlers:
    - name: Restart NGINX
      service:
        name: nginx
        state: restarted

Conclusion

Handlers in Ansible are powerful features that enable you to manage system state changes efficiently. They are very useful for automating actions like restarting services or reloading configurations. They only run when tasks notify them, making your playbooks efficient.

FAQs

1. Can a handler be notified multiple times?

Yes, but it will execute only once per playbook run, regardless of the number of notifications.

2. What happens if no task notifies a handler?

The handler will not run if it is not notified by any task.

3. Can multiple tasks notify the same handler?

Yes, multiple tasks can notify a single handler, which ensures efficient execution.

About Hitesh Jethva

I am Hitesh Jethva, Founder and Author at Code2DevOps.com. With over 15 years of experience in DevOps and open source technologies, I am passionate about empowering teams through automation, continuous integration, and scalable solutions.

View all posts by Hitesh Jethva