Ansible one email alert for all hosts

Does anyone know how to club all output in 1 email alert? i created a playbook to check read-only filesystem and alert when it finds one. but the problem is if 2 servers have the problem i get 2 email alerts. i need single alert for all problematic hosts. Any help will be appreciated. :) i tried many thing but i get single alert for single hosts and it floods my inbox.

---

- hosts: free

remote_user: snail

gather_facts: false


tasks:

- name: run echo Command

shell: if grep "[[:space:]]ro[[:space:],]" /proc/mounts | grep -v tmpfs > /dev/null ; then echo 'read-only-FS';else echo 'all_good'; fi

register: outcome


- name: get the IP address

shell: hostname

register: host_name


- name: Send Mail Alert

local_action: mail

host="mailxxxx"

subject="[Read-Only FS] Testing Mail"

body="Read-Only FileSystem on {{ host_name.stdout }}"

to="[email protected]"

from="root"

when: outcome.stdout == "read-only-FS"

The easiest that I can think about is following:

  1. Check if there is file with some "random" name like "/tmp/ansible-mail". This should be delegate_to=localhost and run_once=True. If there is file it should be removed. (file module with state=absent)
  2. Check if FS has changed - register it.
  3. If 2 is changed append to this file - Because multiple host might want to append the serial=1 might be an option, delegate_to=localhost.
  4. Check if there is this "random" file - register it. Delegate to localhost and set run_once.
  5. If 4 is true. Then run mailing. Once more with delegation and run_once.

If there is need I can make simple POC.

EDIT:

---
- hosts: tmp1, tmp2
  # user, become etc should normaly be set in ansible.cfg
  user: ansible
  become: true
  vars:
    mail_body_file: "/tmp/ansible-mailing-tmp123" 
  tasks:
    - name: Check if there is {{mail_body_file}}
      file:
        state: absent
        path: "{{mail_body_file}}"
      delegate_to: localhost
      run_once: true
      become: false

    - name: Task looking for change, command always reports change so I used it for testing
      command: "echo 123"  
      register: my_test

    - name: Create {{mail_body_file}}
      file:
        state: touch
        path: "{{mail_body_file}}"
      delegate_to: localhost
      run_once: true
      become: false
    # Use shell because lineinfile has race condition
    # with echo >> we put race condition problem on filesystem
    - name: add line about change on host
      shell: "echo {{inventory_hostname}} has change >> {{mail_body_file}}"
      delegate_to: localhost
      when: my_test.changed
      become: false

    - name: get the mail body to variable
      register: mail_body
      command: "cat {{mail_body_file}}"
      delegate_to: localhost
      run_once: True

    - name: mail about change
      mail:
        body: "{{mail_body.stdout}}"
        to: "[email protected]"
        from: "root"
        subject: "Test"
      delegate_to: localhost
      run_once: True

Please note that you should test it.


A playbook like this could work.

We have two separate plays in the playbook, in the first play we connect to the remote hosts and gather results. In the second play we run on local host, and send the mail. An inline jinja template is used that will loop over all the hosts that match the hosts pattern defined in hosts_pattern.

---
- name: gather data
  hosts: linux_systems
  gather_facts: false
  tasks:

  - name: Command to check systems for ead only mounts
    shell: >
      if grep "[[:space:]]ro[[:space:],]" /proc/mounts |
        grep -v tmpfs > /dev/null ; then
      echo 'read-only-FS';else echo 'all_good'; fi
    register: read_only_check

  - name: Get the output of `hostname`
    command: hostname
    register: host_name

- name: email report
  hosts: localhost
  gather_facts: false
  vars:
    hosts_pattern: linux_systems
  tasks:

  - name: Email report of systems with read only filesystems
    mail:
      host: some-smtp.server.example.com
      port: 25
      to: John Smith <[email protected]>
      subject: "[Read-Only FS] Testing Mail"
      body: |
        Report from playbook check_readonly_filesystems.

        {% for host_item in lookup('inventory_hostnames', hosts_pattern, wantlist=True) %}
        {% if hostvars[host_item]['read_only_check'] is defined and
              hostvars[host_item]['read_only_check'].stdout is defined and
              hostvars[host_item]['read_only_check'].stdout == "read-only-FS" %}
        Read only - {{ hostvars[host_item]['inventory_hostname'] }} aka {{ hostvars[host_item]['host_name'].stdout }}
        {% else %}
        Good host - {{ hostvars[host_item]['inventory_hostname'] }} aka {{ hostvars[host_item]['host_name'].stdout }}
        {% endif %}
        {% endfor %}