16
votes

I want to pass a variable to a notification handler, but can't find anywhere be it here on SO, the docs or the issues in the github repo, how to do it. What I'm doing is deploying multiple webapps, and when the code for one of those webapps is changed, it should restart the service for that webapp.

From this SO question, I got this to work, somewhat:

- hosts: localhost
  tasks:
  - name: "task 1"
    shell: "echo {{ item }}"
    register: "task_1_output"
    with_items: [a,b]
  - name: "task 2"
    debug:
      msg: "{{ item.item }}"
    when: item.changed
    with_items: task_1_output.results

(Put it in test.yml and run it with ansible-playbook test.yml -c local.)

But this registers the result of the first task and conditionally loops over that in the second task. My problem is that it gets messy when you have two or more tasks that need to notify the second task! For example, restart the web service if either the code was updated or the configuration was changed.

AFAICT, there's no way to pass a variable to a handler. That would cleanly fix it for me. I found some issues on github where other people run into the same problem, and some syntaxes are proposed, but none of them actually work.

Including a sub-playbook won't work either, because using with_items together with include was deprecated.

In my playbooks, I have a site.yml that lists the roles of a group, then in the group_vars for that group I define the list of webapps (including the versions) that should be installed. This seems correct to me, because this way I can use the same playbook for staging and production. But maybe the only solution is to define the role multiple times, and duplicate the list of roles for staging and production.

So what is the wisdom here?

4

4 Answers

19
votes

Variables in Ansible are global so there is no reason to pass a variable to handler. If you are trying to make a handler parameterized in a way that you are trying to use a variable in the name of a handler you won't be able to do that in Ansible.

What you can do is create a handler that loops over a list of services easily enough, here is a working example that can be tested locally:

- hosts: localhost
  tasks:
  - file:  >
      path=/tmp/{{ item }}
      state=directory
    register: files_created
    with_items:
      - one
      - two
    notify: some_handler

  handlers:
    - name: "some_handler"
      shell: "echo {{ item }} has changed!"
      when: item.changed
      with_items: files_created.results
6
votes

I finally solved it by splitting the apps out over multiple instances of the same role. This way, the handler in the role can refer to variables that are defined as role variable.

In site.yml:

- hosts: localhost
  roles:
  - role: something
    name: a
  - role: something
    name: b

In roles/something/tasks/main.yml:

- name: do something
  shell: "echo {{ name }}"
  notify: something happened

- name: do something else
  shell: "echo {{ name }}"
  notify: something happened

In roles/something/handlers/main.yml:

- name: something happened
  debug:
    msg: "{{ name }}"

Seems a lot less hackish than the first solution!

6
votes

To update jarv's answer above, Ansible 2.5 replaces with_items with loop. When getting results, item by itself will not work. You will need to explicitly get the name, e.g., item.name.

- hosts: localhost
  tasks:
  - file:  >
      path=/tmp/{{ item }}
      state=directory
    register: files_created
    loop:
      - one
      - two
    notify: some_handler

  handlers:
    - name: "some_handler"
      shell: "echo {{ item.name }} has changed!"
      when: item.changed
      loop: files_created.results
1
votes

I got mine to work like this - I had to add some curly brackets

tasks:
    - name: Aktivieren von Security-, Backport- und Non-Security-Upgrades
      lineinfile:
        path: /etc/apt/apt.conf.d/50unattended-upgrades
        regexp: '^[^"//"]*"\${distro_id}:\${distro_codename}-{{ item }}";'
        line: '        "${distro_id}:${distro_codename}-{{ item }}";'
        insertafter: "Unattended-Upgrade::Allowed-Origins {"
        state: present
      register: aenderung
      loop:
        - updates
        - security
        - backports
      notify: Auskommentierte Zeilen entfernen

    handlers:
        - name: Auskommentierte Zeilen entfernen
          lineinfile:
            path: /etc/apt/apt.conf.d/50unattended-upgrades
            regexp: '^\/\/.*{{ item.item }}";.*'
            state: absent
          when: item.changed
          loop: "{{ aenderung.results }}"