Jinja Templates with Ansible¶
Jinja templating can be used directly inside ansible playbooks, or you can use
dedicated .j2
files with the ansible.builtin.template
module.
They're great for dynamically formatting files/documents to display data in a consistent way.
Table of Contents¶
Example: Daily System Health Check¶
Exampe provided by het_tanis and modified by me¶
A collection of tasks that will gather some basic health information about a system.
Then, the information is formatted into a jinja template and sent to a webhook.
---
# tasks file for daily_system_check
#Start with gathering all the variables from the systems
- name: Gather all the uptimes from all the systems
ansible.builtin.shell: "uptime"
register: uptime
#last --since "$(date -d 'now - 24hour' +'%Y-%m-%d %H:%M')"
- name: login information from the last 24 hours
ansible.builtin.shell:
cmd: |
last --since \"$(date -d 'now - 24hour' +'%Y-%m-%d %H:%M')\" | awk '{print $1}' | grep -vE 'root|wtmp' | sort | uniq -c |xargs| cut -c 2-
register: users_check
#Copy and save the templates for every run
- name: Push out a template for system check items
ansible.builtin.template:
src: templates/daily_health_check.j2
dest: "/root/daily_health_check/daily_system_check.{{ ansible_date_time.iso8601_basic_short }}.txt"
delegate_to: localhost
run_once: yes
- name: Health report output capture
ansible.builtin.shell:
cmd: |
cat /root/daily_health_check/daily_system_check.{{ ansible_date_time.iso8601_basic_short }}.txt
register: health_report
delegate_to: localhost
run_once: yes
- name: Send something simple to sandbox server using a wrapper module for ansible.builtin.uri
ansible_discord_webhook: # A custom module
webhook_url: "{{discord_webhook_url}}"
content: "{{health_report.stdout}}"
when: discord_webhook_url is defined
delegate_to: localhost
run_once: yes
This is the template that the data will be formatted into, using the
ansible.builtin.template
module.
This is the templates/daily_health_check.j2
template.
Total systems: {{ ansible_play_hosts_all | length }}
Unreachable systems:
----------------------------------------------
{% for host in ansible_play_hosts_all %}
{% if host not in ansible_play_hosts %}
{{ host }}
{% endif %}
{% endfor %}
Reboot Report (Last 24 hours):
----------------------------------------------
{% for host in ansible_play_hosts_all %}
{% if hostvars[host].uptime is defined %}
{% if 'day' not in hostvars[host].uptime.stdout %}
{{ host }}
{% endif %}
{% endif %}
{% endfor %}
User Logins to Servers in last 24 hours:
----------------------------------------------
{% for host in ansible_play_hosts_all %}
{% if hostvars[host].users_check is defined %}
{{ hostvars[host].ansible_hostname }} - {{ hostvars[host].users_check.stdout }}
{% endif %}
{% endfor %}
Processing a Template with lookup
¶
By default, the template
module writes out to a file. This may not always be what
you want.
If you have a template to process but don't want to write it to a new file, you can
use the lookup()
filter.
This will process the template and expand all its variables, execute its if
blocks,
etc., and can be saved to a variable or used in another type of task.
- name: Process a template.
ansible.builtin.set_fact:
processed_template: "{{ lookup('template', 'template_name.j2') }}"
This will load the template file, populate it appropriately, and then save it to the
processed_template
variable (with set_fact
).
A more practical example. We have a template that contains a single entry for a Samba share configuration.
# Template for the smb.conf entry itself, not the whole file
[{{ samba_share_name }}]
path = {{ samba_dir }}
{% if browsable %}
browsable = yes
{% else %}
browsable = no
{% endif %}
{% if readonly %}
read only = yes
{% else %}
read only = no
{% endif %}
{% if guest_ok %}
guest ok = yes
{% else %}
guest ok = no
{% endif %}
{% if valid_users | length > 0 %}
valid users = {{ valid_users | join(' ') }}
{% endif %}
Now, if we used ansible.builtin.template
to render this into /etc/samba/smb.conf
, it would replace the entire contents of the smb.conf
file (and optionally make a backup), but that's not what needs to happen.
We want to append this expanded template to the smb.conf
file.
We can use the ansible.builtin.blockinfile
module in conjunction with the
lookup()
function.
- name: Append the output of the template to smb.conf
ansible.builtin.blockinfile:
path: /etc/samba/smb.conf
marker: "# {mark} ANSIBLE MANAGED BLOCK - {{ samba_share_name }}"
block: "{{ lookup('template', 'smb_conf_entry.j2') }}"
This will append the populated Jinja2 template to the smb.conf
file (or update it,
if it's already there) without overwriting the entire file.