Email hostnames in a group attachment rather than individual mails per server - ansible

Regarding monthly patching, we currently have an pre_task mail notification sent out prior to the updates being installed but due to the {{ inventory_hostname }} being included in the body of the email, this is sent out on a per server basis.
Is there a way to replace the inventory_hostname in the body to reference all servers within the hosts group (in this case groupOne) so that one email is sent out with all hostnames rather than individual emails?
hosts: groupOne
become: true
any_errors_fatal: true
pre_tasks:
name: Notification email of patching beginning
mail:
host: XXXXXXXXXXXXXXXXXXXXX.com
port: XXXXXX
to: hello#gmail.com
sender: patching#gmail.com
subject: Patching_Notification
body: "Monthly patching is about to commence for {{ inventory_hostname }}. A further email will be sent on completion"

You can use the group_names variable to get the group name(s) of your host.
https://docs.ansible.com/ansible/latest/reference_appendices/special_variables.html#magic
You probably want to run your pre_tasks notification only once with run_once: yes.

Related

Create a fact that summarizes content from each host a play runs against in an inventory?

I'm trying to start a service on a series of hosts. The requestor for this Ansible play wants an email (only one email) sent at the end of the play summarizing the status of the service for each given host in the inventory. In order to do this, I need to create some sort of method for storing the information about the status of the service for each host as Ansible processes it to use in this summary email.
In essence, if I was talking about about say Java I would need to have a variable that is then added to by a copy of itself plus additional content over a loop (think this is a maybe compound assignment operator?) . I.E
(string) service status = service status + current hosts status
or
(string) service status += current host status
When I try something like the above and "print out" the value of a "Summary" fact in my playbook it only returns information on the service from the latest host in the inventory for example
I want:
host1:
Service Status - good
host 2:
Service Status - good
host 3:
Service Status - good
I get:
host 3:
Service Status - good
Is there a way to do this in Ansible?
I have tried the following:
- name: Get current status
shell: /usr/app/current/app/bin/status_script.sh status
register: app_status
- name: Create an empty fact one time and locally to store our summary email text
set_fact:
Summary: " "
cacheable: yes
run_once: true
delegate_to: localhost
- name: Populate the fact with more information for each host we check the status on
set_fact:
Summary: " {{ Summary }} {{ app_status.stdout_lines }} "
cacheable: yes
delegate_to: localhost
I have looked at the following:
Is it possible to set a fact of an array in Ansible?
Ansible set_fact array and populate it from in loop
How to assign an array to a variable in an Ansible-Playbook
Please note I am only using facts inside the context of the same play.
I would guess you want either to update your final set_fact: to loop over all hosts in the (playbook, or some group) and extract their facts into one consolidated fact, or to otherwise use a separate play after your "main" one
Something akin to:
- name: Populate the fact with more information for each host we check the status on
set_fact:
AllSummaries: |
{%- for h in groups["all"] %}
{{ hostvars[h].Summary }} {{ hostvars[h].app_status.stdout_lines }}
{%- endfor -%}
cacheable: yes
run_once: yes
delegate_to: localhost

Ansible attach multiple files in single mail

I am trying to attach multiple files in single mail. I have to attach my host logs within this mail, which gets generated dynamically. As off now I have 2 hosts. Dynamic files are generated in /ansible_log/10.0.0.1_log.txt & /ansible_log/10.0.0.2_log.txt (and so on). Here I can send mail, below is the script:
Inventory File:
[logs]
x.x.x.1
x.x.x.2
- name: Send e-mail to users, attaching report
mail:
host: x.x.x.x
port: xx
to: "{{ mailid }}"
subject: Server Logs
body: Please find attached logs.
attach:
- /ansible_log/{{ item }}_log.txt
delegate_to: localhost
with_items: "{{ inventory_hostname }}"
run_once: True
tags: send_mail
Here I want to send a mail which attaches both log files in a single mail. If I remove run_once: True then it sends two seperate mails with 2 host log files. If the inventory list grows, then these log files mails will bombard user mail box. To avoid this I want to consolidate all the log files in a single mail and to the recipient as a bunch.
You cannot use a loop there. You must give it a prebuild list of files.
You can use a setfact task to build a list of files before running this task.

Ansible 2.6: Is there a way to reference the playbook's name in a role task?

Given a playbook like this:
- name: "Tasks for service XYZ"
hosts: apiservers
roles:
- { role: common }
Is there a way to reference the playbook's name ("Tasks for service XYZ")? (i.e. a variable)
EDIT:
My intention is to be able to reference the playbook's name in a role task, i.e. sending a msg via slack like
- name: "Send Slack notification indicating deploy has started"
slack:
channel: '#project-deploy'
token: '{{ slack_token }}'
msg: '*Deploy started* to _{{ inventory_hostname }}_ of `{{ PLAYBOOK_NAME }}` version *{{ service_version }}*'
delegate_to: localhost
tags: deploy
It was added in 2.8:
ansible_play_name
The name of the currently executed play. Added in 2.8.
No, the special variables for Ansible are documented here, and you can see that there is no variable to return the playbook name.
As mentioned in the comments, however, you can always do this:
---
- name: "{{ task_name }}"
hosts: localhost
vars:
task_name: "Tasks for service XYZ"
tasks:
- debug:
msg: "{{ task_name }}"
From your circumstances, it looks like you only want this for audit/notification purposes? In that case (and assuming unixy clients), using
lookup('file', '/proc/self/cmdline') | regex_replace('\u0000',' ')
will give you the entire command line that ansible-playbook was called with, parameters and all, which would include the playbook name. Depending on your circumstances, that might be a useful enough tradeoff.

Ansible lookup file variable

I'm trying to email a template file that has variable in html such as {{user_name}}, html renders correctly in my mail client, however {{user_name}} doesn't get resolved and displays as string {{user_name}}
- name: Send e-mail to a bunch of users, attaching files
mail:
host: mail.server.com
subtype: html
subject: email template
body: '{{ lookup("file", "roles/binding/templates/email.j2") }}'
to: "{{ user_email }}"
Example output
Hi {{ user_name }} ....
Desired output
Hi John Doe
Any ideas on how I can resolve this issue?
Simply replace the file lookup plugin with the template lookup plugin in the body parameter:
body: '{{ lookup("template", "roles/binding/templates/email.j2") }}'
you can add a template task to process the file, right before the task you have for sending the mail. Example:
- name: prepare mail body from template
template:
src: email.j2
dest: /tmp/email.out
The variable replacement will take place in this task.
Then, you shall send tha mail with the task you have already prepared, but body will have to point to the /tmp/mail.out file.
template module documentation

A special variable for ansible roles installed?

Is there a variable or a method allowing one to list all the roles applied to a group of ansible hosts?
For example:
- hosts: webservers
gather_facts: true
roles:
- nginx
- php-fpm
tasks:
- debug:
msg: {{ item }} installed
with_items: ansible_roles
or perhaps another way to achieve this?
It depends on what applied means in your question.
The variable role_names holds all roles of the current play, so it would be an array: [nginx, php-fpm].
- debug:
msg: {{ item }} installed
with_items: role_names
But these roles not necessarily have been applied to the hosts, if you mean by that they have been processed. There is no such thing that will update once a role has been run on a host.
If that is what you're looking for you could implement it yourself with a callback plugin. AFAIK there is no callback for starting/completing a role itself. But since the names of the roles are present in the task names you could simply use the playbook_on_task_start, extract the role name from the task name and store it in some way. I have not yet looked into callback plugins in Ansible 2 where the API changed, but I expect you have access to the global variable list and can manipulate it.

Resources