Search code examples
ansibleansible-handlers

Run an Ansible handler only once for the entire playbook


I would like to run a handler only once in an entire playbook.

I attempted using an include statement in the following in the playbook file, but this resulted in the handler being run multiple times, once for each play:

- name: Configure common config
  hosts: all
  become: true
  vars:
        OE: "{{ ansible_hostname[5] }}"
  roles:
    - { role: common }
  handlers:
    - include: handlers/main.yml

- name: Configure metadata config
  hosts: metadata
  become: true
  vars:
        OE: "{{ ansible_hostname[5] }}"
  roles:
    - { role: metadata }
  handlers:
    - include: handlers/main.yml

Here is the content of handlers/main.yml:

- name: restart autofs
  service:
    name: autofs.service
    state: restarted

Here is an example of one of the tasks that notifies the handler:

- name: Configure automount - /opt/local/xxx in /etc/auto.direct
  lineinfile:
     dest: /etc/auto.direct
     regexp: "^/opt/local/xxx"
     line: "/opt/local/xxx   -acdirmin=0,acdirmax=0,rdirplus,rw,hard,intr,bg,retry=2  nfs_server:/vol/xxx"
  notify: restart autofs

How can I get the playbook to only execute the handler once for the entire playbook?


Solution

  • The answer

    The literal answer to the question in the title is: no.

    Playbook is a list of plays. Playbook has no namespace, no variables, no state. All the configuration, logic, and tasks are defined in plays.

    Handler is a task with a different calling schedule (not sequential, but conditional, once at the end of a play, or triggered by the meta: flush_handlers task).

    A handler belongs to a play, not a playbook, and there is no way to trigger it outside of the play (i.e. at the end of the playbook).


    Solution

    The solution to the problem is possible without referring to handlers.

    You can use group_by module to create an ad-hoc group based on the result of the tasks at the bottom of each play.

    Then you can define a separate play at the end of the playbook restarting the service on targets belonging to the above ad-hoc group.

    Refer to the below stub for the idea:

    - hosts: all
      roles:
        # roles declaration
      tasks:
        - # an example task modifying Nginx configuration
          register: nginx_configuration
    
        # ... other tasks ...
    
        - name: the last task in the play
          group_by:
            key: hosts_to_restart_{{ 'nginx' if nginx_configuration is changed else '' }}
    
    # ... other plays ...
    
    - hosts: hosts_to_restart_nginx
      gather_facts: no
      tasks:
        - service:
            name: nginx
            state: restarted