- "Do I Know This Already?" Quiz
- Using Modules to Manipulate Files
- Managing SELinux Properties
- Using Jinja2 Templates
- Summary
- Exam Preparation Tasks
- Review All Key Topics
- Memory Tables
- Define Key Terms
- Review Questions
- Exercise Answers
- Lab 8-1: Generate an /etc/hosts File
- Lab 8-2: Manage a vsftpd Service
Using Jinja2 Templates
A template is a configuration file that contains variables and, based on the variables, is generated on the managed hosts according to host-specific requirements. Using templates allows for a structural way to generate configuration files, which is much more powerful than changing specific lines from specific files. Ansible uses Jinja2 to generate templates.
Working with Simple Templates
Jinja2 is a generic templating language for Python developers. It is used in Ansible templates, but Jinja2-based approaches are also found in other parts of Ansible. For instance, the way variables are referred to is based on Jinja2.
In a Jinja2 template, three elements can be used. Table 8-4 provides an overview.
Table 8-4 Jinja2 Template Elements
Element |
Example |
|---|---|
data |
sample text |
comment |
{# sample text #} |
variable |
{{ ansible_facts['default_ipv4']['address'] }} |
expression |
{% for myhost in groups['web'] %} {{ myhost }} {% endfor %} |
To work with a template, you must create a template file, written in Jinja2. Next, this template file must be included in an Ansible playbook that uses the template module. Listing 8-13 shows what a template file might look like, and Listing 8-14 shows an example of a playbook that calls the template.
Listing 8-13 Sample Template
# {{ ansible_managed }}
<VirtualHost *:80>
ServerAdmin webmaster@{{ ansible_facts['fqdn'] }}
ServerName {{ ansible_facts['fqdn'] }}
ErrorLog logs/{{ ansible_facts['hostname'] }}-error.log
CustomLog logs/{{ ansible_facts['hostname'] }}-common.
log common
DocumentRoot /var/www/vhosts/{{ ansible_facts['hostname'] }}/
<Directory /var/www/vhosts/{{ ansible_facts['hostname'] }}>
Options +Indexes +FollowSymlinks +Includes
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
The sample template in Listing 8-13 starts with # {{ ansible_managed }}. This string is commonly used to identify that a file is managed by Ansible so that administrators are not going to change file contents by accident. While processing the template, this string is replaced with the value of the ansible_managed variable. This variable can be set in ansible.cfg. For instance, you can use ansible_managed = This file is managed by Ansible to substitute the variable with its value while generating the template.
As for the remainder, the template file is just a text file that uses variables to substitute specific variables to their values. In this case that is just the ansible_fqdn and ansible_hostname variables that are set as Ansible facts. To generate the template, you need a playbook that uses the template module to call the template. Listing 8-14 shows an example.
Listing 8-14 Sample Playbook
---
- name: installing a template file
hosts: ansible1
tasks:
- name: install http
yum:
name: httpd
state: latest
- name: start and enable httpd
service:
name: httpd
state: started
enabled: true
- name: install vhost config file
template:
src: listing813.j2
dest: /etc/httpd/conf.d/vhost.conf
owner: root
group: root
mode: 0644
- name: restart httpd
service:
name: httpd
state: restarted
In the sample playbook in Listing 8-14, the template module is used to work on the source file specified as src, to generate the destination file, specified as dest. The result is that on the managed host the template is generated, with all the variables substituted to their values.
Applying Control Structures in Jinja2 Using for
In templates, control structures can be used to dynamically generate contents. A for statement can be used to iterate over all elements that exist as the value of a variable. Let’s look at some examples.
To start with, Listing 8-15 shows a template where a for statement is shown.
Listing 8-15 Exploring Jinja2 for Statements
{% for node in groups['all'] %}
host_port={{ node }}:8080
{% endfor %}
In this Jinja2 file, a variable with the name host_ports is defined on the second line (which is the line that will be written to the target file). To produce its value, the host group all is processed in the for statement on the first line. While processing the host group, a temporary variable with the name node is defined. This value of the node variable is replaced with the name of the host while it is processed, and after the host name, the string :8080 is copied, which will result in a separate line for each host that was found. As the last element, {% endfor %} is used to close the for loop. In Listing 8-16 you can see an example of a playbook that runs this template.
Listing 8-16 Generating a Template with a Conditional Statement
---
- name: generate host list
hosts: ansible2
tasks:
- name: template loop
template:
src: listing815.j2
dest: /tmp/hostports.txt
As you can see, the sample playbook in Listing 8-16 uses the template as the source file and, based on the template, produces the file /tmp/hostports.txt on the managed host. To verify, you can use the ad hoc command ansible ansible2 -a "cat /tmp/hostports.txt".
Using Conditional Statements with if
The for statement can be used in templates to iterate over a series of values. The if statement can be used to include text only if a variable contains a specific value or evaluates to a Boolean true. Listing 8-17 shows a sample template file that reacts on a variable that is set in the sample playbook in Listing 8-18.
Listing 8-17 Template Example with if
{% if apache_package == 'apache2' %}
Welcome to Apache2
{% else %}
Welcome to httpd
{% endif %}
Listing 8-18 Using the Template with if
---
- name: work with template file
vars:
apache_package: 'httpd'
hosts: ansible2
tasks:
- template:
src: listing817.j2
dest: /tmp/httpd.conf
Using Filters
In Jinja2 templates, you can use filters. Filters are a way to perform an operation on the value of a template expression, such as a variable. The filter is included in the variable definition itself, and the result of the variable and its filter is used in the file that is generated. Table 8-5 gives an overview of some common filters. In Exercise 8-3 you practice your skills and work with templates that use a conditional statement.
Table 8-5 Common Filters Overview
Filter Example |
Use |
|---|---|
{{ myvar | to_json}} |
Writes the contents of myvar in JSON format |
{{ myvar || to_yaml }} |
Writes the contents of myvar in YAML format |
{{ myvar | ipaddr }} |
Tests whether myvar contains an IP address |
