Search code examples
automationansible

How to alert via email in Ansible


I have setup a mail task in ansible to send emails if yum update is marked as 'changed'.

Here is my current working code:

- name: Send mail alert if updated
      community.general.mail:
        to:
        - 'recipient1'
        cc:
        - 'recipient2'
        subject: Update Alert
        body: 'Ansible Tower Updates have been applied on the following system: {{ ansible_hostname }}'
        sender: "ansible.updates@domain.com"
      delegate_to: localhost
      when: yum_update.changed

This works great, however, every system that gets updated per host group sends a separate email. Last night for instance I had a group of 20 servers update and received 20 separate emails. I'm aware of why this happens, but my question is how would I script this to add all the systems to one email? Is that even possible or should I just alert that the group was updated and inform teams of what servers are in each group? (I'd prefer not to take the second option)

Edit 1:

I have added the code suggested and am now unable to receive any emails. Here's the error message:

"msg": "The conditional check '_changed|length > 0' failed. The error was: error while evaluating conditional (_changed|length > 0): {{ hostvars|dict2items| selectattr('value.yum_update.changed')| map(attribute='key')|list }}: 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'yum_update'\n\nThe error appears to be in '/tmp/bwrap_1073_o8ibkgrl/awx_1073_0eojw5px/project/yum-update-ent_template_servers.yml': line 22, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n    - name: Send mail alert if updated\n      ^ here\n",

I am also attaching my entire playbook for reference:

---
- name: Update enterprise template servers
  hosts: ent_template_servers

  tasks:

    - name: Update all packages
      yum:
        name: '*'
        state: latest
      register: yum_update

    - name: Reboot if needed
      import_tasks: /usr/share/ansible/tasks/reboot-if-needed-centos.yml

    - name: Kernel Cleanup
      import_tasks: /usr/share/ansible/tasks/kernel-cleanup.yml

    - debug:
        var: yum_update.changed

    - name: Send mail alert if updated
      community.general.mail:
        to:
        - 'email@domain.com'
        subject: Update Alert
        body: |-
          Updates have been applied on the following system(s):
          {{ _changed }}
        sender: "ansible.updates@domain.com"
      delegate_to: localhost
      run_once: true
      when: _changed|length > 0
      vars:
        _changed: "{{ hostvars|dict2items|
                      selectattr('yum_update.changed')|
                      map(attribute='key')|list }}"

...

Ansible version is: 2.9.27 Ansible Tower version is: 3.8.3

Thanks in advance!


Solution

  • For example, the mail task below

        - debug:
            var: yum_update.changed
        - community.general.mail:
            sender: ansible
            to: root
            subject: Update Alert
            body: |-
              Updates have been applied to the following system:
              {{ _changed }}
          delegate_to: localhost
          run_once: true
          when: _changed|length > 0
          vars:
            _changed: "{{ hostvars|dict2items|
                          selectattr('value.yum_update.changed')|
                          map(attribute='key')|list }}"
    
    TASK [debug] ***************************************************************
    ok: [host01] => 
      yum_update.changed: true
    ok: [host02] => 
      yum_update.changed: false
    ok: [host03] => 
      yum_update.changed: true
    
    TASK [community.general.mail] **********************************************
    ok: [host01 -> localhost]
    

    will send

    From: ansible@domain.com
    To: root@domain.com
    Cc: 
    Subject: Update Alert
    Date: Wed, 09 Feb 2022 16:55:47 +0100
    X-Mailer: Ansible mail module
    
    Updates have been applied to the following system:
    ['host01', 'host03']
    

    Remove the condition below if you want to receive also empty lists

          when: _changed|length > 0
    

    Debug

    'ansible.vars.hostvars.HostVarsVars object' has no attribute 'yum_update'

    Q: "What I could try?"

    A: Some of the hosts are missing the variables yum_update. You can test it

        - debug:
            msg: "{{ hostvars|dict2items|
                     selectattr('value.yum_update.changed')|
                     map(attribute='key')|list }}"
          run_once: true
    

    Either make sure that the variable is defined on all hosts or use json_query. This filter tolerates missing attributes, e.g.

        - debug:
            msg: "{{ hostvars|dict2items|
                     json_query('[?value.yum_update.changed].key') }}"
          run_once: true
    

    Q: "The 'debug' task prior to the 'mail' task gives me the same output. But it fails when the 'mail' task is executed."

    A: Minimize the code and isolate the problem. For example, in the code below you can see

    • Variable yum_update.changed is missing on host03
    • The filter json_query ignores this
    • The filter selectattr fails
        - debug:
            var: yum_update.changed
    
        - debug:
            msg: "{{ hostvars|dict2items|
                     json_query('[?value.yum_update.changed].key') }}"
          run_once: true
    
        - debug:
            msg: "{{ hostvars|dict2items|
                     selectattr('value.yum_update.changed')|
                     map(attribute='key')|list }}"
          run_once: true
    

    gives

    TASK [debug] **************************************************
    ok: [host01] => 
      yum_update.changed: true
    ok: [host02] => 
      yum_update.changed: false
    ok: [host03] => 
      yum_update.changed: VARIABLE IS NOT DEFINED!
    
    TASK [debug] **************************************************
    ok: [host01] => 
      msg:
      - host01
    
    TASK [debug] **************************************************
    fatal: [host01]: FAILED! => 
      msg: |-
        The task includes an option with an undefined variable.
        The error was: 'ansible.vars.hostvars.HostVarsVars object'
        has no attribute 'yum_update'
    

    Both filters give the same results if all variables are present

    TASK [debug] **************************************************
    ok: [host01] => 
      yum_update.changed: true
    ok: [host02] => 
      yum_update.changed: false
    ok: [host03] => 
      yum_update.changed: true
    
    TASK [debug] **************************************************
    ok: [host01] => 
      msg:
      - host01
      - host03
    
    TASK [debug] **************************************************
    ok: [host01] => 
      msg:
      - host01
      - host03