Image
![IT Automation ebook](/sysadmin/sites/default/files/styles/image_cta/public/2021-07/automation-expert-e-book-medium.jpg?itok=X_OfbjdH)
Photo by Christina Morillo from Pexels
Jinja2 templates are essential for handling tasks that involve advanced file modifications. When you use templates, target files on discovered hosts are automatically customized using variables and Ansible facts. A variable defined in a template is replaced with the correct value when the Jinja2 template is rendered to the target file on a managed host.
[ Download the Jinja2 cheat sheet. ]
This article demonstrates using Jinja2 templates and Ansible. To follow along, you need at least two virtual machines (VMs) managed by Ansible and one Ansible control node. I'm running Red Hat Enterprise Linux (RHEL) on one VM and CentOS Stream on the other.
Prepare your Ansible control node by installing Ansible, configuring ansible.cfg
, and creating an inventory file for the hosts managed by Ansible.
In the ansible.cfg
file, define a string called ansible_managed
to prevent administrators from overwriting files managed by Ansible. It doesn't exist yet, but later an {{ ansible_managed }}
variable, placed at the top of a Jinja2 template, will refer to the string defined here:
[defaults]
retry_files_enabled = False
timeout = 60
connection = smart
interpreter_python = auto
forks = 10
inventory = /home/vcirrus-consulting/RHCE-Ansible/inventory-gather-facts.yml
roles_path = /home/vcirrus-consulting/RHCE-Ansible/roles:/usr/share/ansible/roles
remote_user = ansible-devops
host_key_checking = False
command_warnings = False
deprecation_warnings = False
ansible_managed = Caution: This File is Managed By Ansible - DO NOT EDIT MANUALLY.
[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False
Next, create a static inventory file in a project directory with a list of hosts. I store mine in ~/RHCE-Ansible/inventory-gather-facts.yml
, and here are its contents:
all:
children:
mombasa_one_dc:
hosts:
ansible01.test.lab:
nairobi_one_dc:
hosts:
ansible13.test.lab:
prod:
hosts:
ansible01.test.lab:
ansible02.test.lab:
ansible12.test.lab:
ansible13.test.lab:
ungrouped: {}
[ Learn more about server and configuration management by downloading Ansible for DevOps. ]
Create a Jinja2 template file named rhel_managed_infra_report.j2
. Jinja2 expressions in this file refer to the ansible_facts
variable. The expressions also implement error handling by using the Jinja2 default
filter, which ensures no unnecessary errors are returned as a result of running the playbook. In this case, the playbook displays NONE
when the variable defined does not exist:
{{ ansible_managed }}
INVENTORY_HOSTNAME = {{ inventory_hostname | default('NONE',true) }}
OS_DISTRO_VERSION = {{ ansible_distribution_version | default('NONE',true) }}
Next, create a playbook called rhel_managed_infra_report.yml
and use the template
module to inject the Jinja2 template file into the playbook.
After executing the playbook on the Ansible-managed remote nodes, the dest
element stores the results. The src
element defines the Jinja2 template for Ansible to use.
Note that the src
path in the playbook can be a relative path, which means the file exists in your current working directory, or it can be expressed as an absolute path.
---
- hosts: 'prod,&mombasa_one_dc'
tasks:
- name: Deploying Template to Gather and Save info from Remote Systems
ansible.builtin.template:
src: rhel_managed_infra_report.j2
dest: /root/hwreport.txt
mode: preserve
- hosts: 'prod,&nairobi_one_dc'
tasks:
- name: Deploying Template to Gather and Save info from Remote Systems
ansible.builtin.template:
src: rhel_managed_infra_report.j2
dest: /root/hwreport.txt
mode: preserve
[…]
Run the Ansible playbook using ansible-navigator
. It consists of two tasks, the first of which must run on hosts that belong to the groups prod
and mombasa_one_dc
. The second task must run on hosts belonging to the groups prod
and nairobi_one_dc
.
$ ansible-navigator run --mode stdout rhel_managed_infra_report.yml
PLAY [prod,&mombasa_one_dc] *********************
TASK [Gathering Facts] *******************************
ok: [ansible01.test.lab]
TASK [Deploying Template to Gather and Save info from Remote Systems] ***
changed: [ansible01.test.lab]
PLAY [prod,&nairobi_one_dc] ************************
TASK [Gathering Facts] *********************************************************
ok: [ansible13.test.lab]
TASK [Deploying Template to Gather and Save info from Remote Systems] **********
changed: [ansible13.test.lab]
PLAY RECAP *********************************************************************
ansible01.test.lab: ok=2 changed=1 unreachable=0 failed=0...
ansible13.test.lab: ok=2 changed=1 unreachable=0 failed=0...
Notice that out of all the hosts listed in my hosts file, only ansible01
and ansible13
are affected. This is because they are the only ones that satisfied the playbook's group requirements.
[ Download now: Advanced Linux commands cheat sheet. ]
Run an Ansible ad hoc command to verify that the information has been recorded to the specific remote directory defined in the playbook. These results display all information translated from the Jinja2 template file on ansible13.test.lab
running RHEL:
$ ansible ansible13.test.lab -a "cat /root/hwreport.txt"
ansible13.test.lab | CHANGED | rc=0 >>
Caution: This File is Managed By Ansible - DO NOT EDIT MANUALLY.
INVENTORY_HOSTNAME = ansible13.test.lab
OS_DISTRO_VERSION = Plow
The information for ansible01.test.lab
, running CentOS Stream:
$ ansible ansible01.test.lab -a "cat /root/hwreport.txt"
ansible01.test.lab | CHANGED | rc=0 >>
Caution: This File is Managed By Ansible - DO NOT EDIT MANUALLY.
INVENTORY_HOSTNAME = ansible01.test.lab
OS_DISTRO_VERSION = Stream
Notice that the values of OS_DISTRO_VERSION
are unique in the rendered files, even though the same playbook generated both. That's the power of statements like OS_DISTRO_VERSION = {{ ansible_distribution_release | default('NONE',true) }}
, which uses local data to generate content.
[ Get a cheat sheet of Linux utilities and commands for managing servers and networks. ]
You can expand the information you gather by using other built-in ansible_facts
variables for Jinja2. Modify your rhel_managed_infra_report.j2
template file:
{{ ansible_managed }}
INVENTORY_HOSTNAME = {{ inventory_hostname | default('NONE',true) }}
OS_DISTRO_VERSION = {{ ansible_distribution_version | default('NONE',true) }}
PYTHON_VERSION = {{ ansible_python_version | default('NONE',true) }}
SELINUX_STATUS = {{ ansible_selinux | default('NONE',true) }}
BIOS_VERSION = {{ ansible_bios_version | default('NONE',true) }}
BIOS_DATE = {{ ansible_bios_date | default('NONE',true) }}
TOTAL_MEMORY = {{ ansible_memtotal_mb | default('NONE',true) }}
CURRENT_MEMORY_USAGE = {{ ansible_memory_mb['real']['used']}} mb out of {{ ansible_memory_mb['real']['total']}} mb
OS_FAMILY = {{ ansible_os_family | default('NONE',true) }}
OS_DISTRO = {{ ansible_distribution | default('NONE',true) }}
OS_KERNEL_VERSION = {{ ansible_kernel | default('NONE',true) }}
OS_ARCHITECTURE = {{ ansible_architecture | default('NONE',true) }}
PROCESSOR = {{ ansible_processor | default('NONE',true) }}
VIRT_TYPE = {{ ansible_virtualization_type | default('NONE',true) }}
VDA_SIZE = {% if ansible_devices.vda is defined %}
{{ ansible_devices.vdb.size }}{% else %}
NONE
{% endif %}
VDB_SIZE = {% if ansible_devices.vdb is defined %}
{{ ansible_devices.vdb.size }}{% else %}
NONE
{% endif %}
Run that and look at the output. That's a lot of useful information about each system, and all it takes is one template file and a playbook!
To get a list of all the ansible_facts
variables for a Jinja2 template file, use this ad hoc command on any managed node:
$ ansible <managed_host_fqdn or managed_host_ipaddress> -m setup -a "filter=ansible_*"
This article touches on the power of using Jinja2 templates with Ansible. Explore further and see how you can implement the lessons learned from this article to extract valuable information configured in your remote systems. For example, try tinkering with your remote storage configuration or collecting information in a multivendor hybrid environment.
Robert is a Linux enthusiast and an open source advocate, currently transitioning into a site reliability engineering (SRE) role. Always striving to learn more, he's pursuing Red Hat Certified Architect - Infrastructure path certification. Besides his love for Linux, he believes in helping others More about me
With AnsibleFest now part of Red Hat Summit, you can experience two essential IT events in one location. Join us May 23-25 to shape the future of Ansible.
OUR BEST CONTENT, DELIVERED TO YOUR INBOX