Skip to main content

Working with Jinja2 Templates

1. Creating Template Files

Property Description Example
template_file Path to the Jinja2 template file templates/web_config.j2
dest Destination path where the rendered file will be written /etc/nginx/conf.d/app.conf
vars Variables to pass into the template context {'app_port': 8080, 'ssl_enabled': true}

Example: Basic Template File Creation

{% raw %}# templates/web_config.j2
upstream {{ app_name }} {
    server 127.0.0.1:{{ app_port }};
}

server {
    listen {{ server_port }};
    server_name {{ domain_name }};

    location / {
        proxy_pass http://{{ app_name }};
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}{% endraw %}

Example: Using Variables in Templates

{% raw %}# playbook.yml
- name: Deploy web config from template
  hosts: webservers
  tasks:
    - name: Render nginx config
      template:
        src: web_config.j2
        dest: /etc/nginx/conf.d/{{ app_name }}.conf
      vars:
        app_port: 8080
        server_port: 443
        domain_name: example.com
        app_name: myapp{% endraw %}
Note: Template files must end with .j2 extension to be recognized by Ansible.

2. Using Template Module

Parameter Description Default Notes
src Path to the template file Required Must be a valid Jinja2 template
dest Path where the rendered file will be created Required Can be a directory or full path
backup Creates a backup of the destination file before overwriting false Useful for rollback in case of errors
mode Permissions for the rendered file 0644 Must be a valid octal permission string
owner Owner of the rendered file root Applies to Unix-like systems
group Group of the rendered file root Applies to Unix-like systems

Example: Basic Template Module Usage

- name: Render a configuration file from template
  ansible.builtin.template:
    src: web_config.j2
    dest: /etc/nginx/conf.d/web.conf
    mode: '0644'
    owner: root
    group: root

Example: Template with Variables

- name: Render a config with variables
  ansible.builtin.template:
    src: app_config.j2
    dest: /opt/app/config.json
    mode: '0640'
  vars:
    app_port: 8080
    app_host: "localhost"
Note: The template module uses Jinja2 syntax for templating. Variables are referenced using {{ variable_name }} in the template file.
Warning: Ensure that the dest path is writable and has appropriate permissions to avoid rendering failures.

3. Accessing Variables

Variable Type Access Syntax Description
Host Variables hostvars[inventory_hostname] Access variables defined for a specific host
Group Variables group_vars[group_name] Access variables defined for a group
Host Facts hostvars[inventory_hostname].ansible_facts Access system facts collected by setup module
Role Variables role_var_name Access variables defined in a role's vars/ directory
Extra Vars vars['var_name'] Access variables passed via command line or inventory

Example: Accessing Host Variables

{% for host in groups['webservers'] %}
{{ hostvars[host].ansible_facts.cpu_model }}
{% endfor %}

Example: Using Group Variables

{{ group_vars['database'].db_port }}

4. Using Filters

Filter Description Example
upper Converts a string to uppercase {{ "hello world"|upper }}
lower Converts a string to lowercase {{ "HELLO WORLD"|lower }}
capitalize Capitalizes the first letter of a string {{ "hello world"|capitalize }}
title Converts a string to title case {{ "hello world"|title }}
reverse Reverses a string or list {{ "hello"|reverse }}
join Joins a list into a string with a delimiter {{ ['a', 'b', 'c']|join(',') }}
split Splits a string into a list using a delimiter {{ "a,b,c"|split(',') }}
default Provides a default value if the variable is undefined or empty {{ undefined_var|default('default value') }}
replace Replaces occurrences of a substring with another {{ "hello world"|replace('world', 'Ansible') }}
truncate Truncates a string to a specified length {{ "This is a long string"|truncate(10) }}
first Returns the first item in a list {{ ['a', 'b', 'c']|first }}
last Returns the last item in a list {{ ['a', 'b', 'c']|last }}
length Returns the length of a string or list {{ "hello"|length }}
sort Sorts a list {{ [3, 1, 2]|sort }}
unique Removes duplicate items from a list {{ [1, 2, 2, 3]|unique }}
reverse Reverses the order of a list {{ [1, 2, 3]|reverse }}
batch Splits a list into smaller lists of a given size {{ [1, 2, 3, 4, 5]|batch(2) }}
select Selects items from a list that match a condition {{ [1, 2, 3, 4]|select('odd') }}
reject Rejects items from a list that match a condition {{ [1, 2, 3, 4]|reject('odd') }}
map Applies a filter to each item in a list {{ [1, 2, 3]|map('upper') }}
sum Sums all items in a list (numeric values only) {{ [1, 2, 3]|sum }}
min Returns the smallest value in a list {{ [1, 2, 3]|min }}
max Returns the largest value in a list {{ [1, 2, 3]|max }}
abs Returns the absolute value of a number {{ -5|abs }}
round Rounds a number to the nearest integer {{ 3.14|round }}
int Converts a value to an integer {{ "123"|int }}
float Converts a value to a float {{ "123.45"|float }}
bool Converts a value to a boolean {{ "true"|bool }}
list Converts a value to a list {{ "hello"|list }}
dict Converts a value to a dictionary {{ "key: value"|dict }}
items Converts a dictionary into a list of key-value pairs {{ {'a': 1, 'b': 2}|items }}
keys Extracts keys from a dictionary {{ {'a': 1, 'b': 2}|keys }}
values Extracts values from a dictionary {{ {'a': 1, 'b': 2}|values }}
groupby Groups a list by a key function {{ [{'name': 'Alice', 'age': 30}, {'name': 'Bob', 'age': 30}]|groupby('age') }}
selectattr Selects items from a list based on attribute value {{ [{'name': 'Alice', 'age': 30}, {'name': 'Bob', 'age': 25}]|selectattr('age', 'ge', 30) }}
rejectattr Rejects items from a list based on attribute value {{ [{'name': 'Alice', 'age': 30}, {'name': 'Bob', 'age': 25}]|rejectattr('age', 'ge', 30) }}
unique Removes duplicate items from a list (case-insensitive) {{ ['a', 'A', 'b']|unique }}
urlencode Encodes a string for use in a URL {{ "hello world"|urlencode }}
urlencode Decodes a URL-encoded string {{ "hello%20world"|urldecode }}
b64encode Encodes a string in base64 {{ "hello"|b64encode }}
b64decode Decodes a base64-encoded string {{ "aGVsbG8"|b64decode }}
to_json Converts a value to JSON format {{ {'a': 1}|to_json }}
from_json Converts a JSON string to a Python object {{ '{"a": 1}'|from_json }}
to_yaml Converts a value to YAML format {{ {'a': 1}|to_yaml }}
from_yaml Converts a YAML string to a Python object {{ "a: 1"|from_yaml }}
regex_replace Replaces a regex pattern with a replacement string {{ "hello world"|regex_replace('world', 'Ansible') }}
regex_findall Finds all matches of a regex pattern in a string {{ "hello world"|regex_findall('l+') }}
regex_search Searches for a regex pattern in a string {{ "hello world"|regex_search('l+') }}
regex_match Matches a regex pattern against a string {{ "hello world"|regex_match('h.*d') }}
batch Groups items in a list into batches of a given size {{ [1, 2, 3, 4, 5]|batch(2) }}
loop.index Accesses the current index in a loop {% for item in items %}{{ loop.index }}{% endfor %}
loop.first Checks if the current item is the first in a loop {% for item in items %}{% if loop.first %}First{% endif %}{% endfor %}
loop.last Checks if the current item is the last in a loop {% for item in items %}{% if loop.last %}Last{% endif %}{% endfor %}
loop.revindex Accesses the reverse index in a loop {% for item in items %}{{ loop.revindex }}{% endfor %}
loop.length Accesses the length of a loop {% for item in items %}{{ loop.length }}{% endfor %}
loop.cycle Cycles through a list of values in a loop {% for item in items %}{{ loop.cycle('odd', 'even') }}{% endfor %}
loop.depth Accesses the depth of nested loops {% for item in items %}{% for subitem in item %}{{ loop.depth }}{% endfor %}{% endfor %}
loop.depth0 Accesses the zero-based depth of nested loops {% for item in items %}{% for subitem in item %}{{ loop.depth0 }}{% endfor %}{% endfor %}
loop.previtem Accesses the previous item in a loop {% for item in items %}{{ loop.previtem }}{% endfor %}
loop.nextitem Accesses the next item in a loop {% for item in items %}{{ loop.nextitem }}{% endfor %}
loop.changed Checks if a value has changed in a loop {% for item in items %}{{ loop.changed(item) }}{% endfor %}

5. Implementing Conditionals

Directive Description Usage
when Conditionally execute a task based on the evaluation of a boolean expression. when: ansible_facts.os_family == "RedHat"
failed_when Defines a condition under which a task is marked as failed. failed_when: "result.rc != 0"
changed_when Defines a condition under which a task is marked as changed. changed_when: "result.stdout | regex_search('restarted')"

Example: Using when with variables

- name: Install packages on Debian
  apt:
    name: nginx
    state: present
  when: ansible_os_family == "Debian"

- name: Install packages on RedHat
  yum:
    name: nginx
    state: present
  when: ansible_os_family == "RedHat"

Example: Using failed_when to check output

- name: Check if a file exists
  command: ls /etc/nonexistent
  register: result
  failed_when: "result.rc != 0"

Example: Using changed_when to control task state

- name: Restart service if config changed
  command: systemctl restart myservice
  register: result
  changed_when: "result.stdout | regex_search('restarted')"
Note: The when directive is evaluated at runtime and supports Jinja2 expressions.
Warning: Be cautious with failed_when and changed_when as they can mask actual errors or changes in your playbook.

6. Implementing Loops

Loop Type Description Example
loop Iterates over a list or array
{% for item in items %}
{{ item }}
{% endfor %}
loop.index Gives the current iteration number (1-based)
{% for item in items %}
{{ loop.index }}: {{ item }}
{% endfor %}
loop.index0 Gives the current iteration number (0-based)
{% for item in items %}
{{ loop.index0 }}: {{ item }}
{% endfor %}
loop.revindex Gives the number of iterations remaining (1-based)
{% for item in items %}
{{ loop.revindex }}: {{ item }}
{% endfor %}
loop.revindex0 Gives the number of iterations remaining (0-based)
{% for item in items %}
{{ loop.revindex0 }}: {{ item }}
{% endfor %}
loop.first True if this is the first iteration
{% for item in items %}
{% if loop.first %}First item: {% endif %}
{{ item }}
{% endfor %}
loop.last True if this is the last iteration
{% for item in items %}
{{ item }}
{% if loop.last %}Last item{% endif %}
{% endfor %}
loop.cycle Cycles through a list of values
{% for item in items %}
{{ loop.cycle('odd', 'even') }}: {{ item }}
{% endfor %}

Example: Looping Over a List

{% for host in groups['webservers'] %}
{{ host }} is part of the web server group
{% endfor %}

Example: Nested Loops with Index Tracking

{% for item in items %}
{% for subitem in item.subitems %}
{{ loop.parent.index }}.{{ loop.index }}: {{ subitem }}
{% endfor %}
{% endfor %}
Note: The loop variable is only available inside a loop block and provides useful context about the current iteration.

7. Setting Template Variables

Variable Type Description Example
hostvars Dictionary Contains variables for all hosts in the inventory {{ hostvars['web01']['ansible_fqdn'] }}
group_names List Names of groups the current host belongs to {{ group_names | join(', ') }}
groups Dictionary All groups and their hosts {{ groups['webservers'] | join(', ') }}
ansible_env Dictionary Environment variables on the target host {{ ansible_env.HOME }}
vars Dictionary Variables defined in playbooks or roles {{ vars['app_version'] }}

Example: Using hostvars in a template

{% for host in groups['webservers'] %}
Server: {{ hostvars[host]['ansible_fqdn'] }}
Port: {{ hostvars[host]['http_port'] }}
{% endfor %}

Example: Conditional variable usage

{% if ansible_os_family == 'RedHat' %}
Firewall: iptables
{% else %}
Firewall: ufw
{% endif %}

8. Using Comments

Comment Type Syntax Description
Inline Comment {# This is an inline comment #} Comments that are ignored during template processing and do not appear in the output.
Multiline Comment {#- This is a multiline comment -#} Comments that span multiple lines and are also ignored during processing.

Example: Using Inline Comments in Jinja2

{# This comment will not appear in the output #}
<div>
    <h1>{{ title }}</h1>
    <p>{{ content }}</p>
</div>

9. Including Templates

Directive Description Usage
include Includes a template file and renders it in place {% include 'template.j2' %}
import Imports a template and makes its macros available {% import 'macros.j2' as macros %}
from Imports specific macros or variables from another template {% from 'macros.j2' import macro1 %}

Example: Including a Template

{% include 'header.j2' %}

<h1>{{ title }}</h1>

{% include 'footer.j2' %}

Example: Importing Macros

{% import 'utils.j2' as utils %}

<div class="card">
  {{ utils.render_button('Click Me') }}
</div>
Note: The include directive does not pass variables from the parent template by default. Use {% include 'template.j2' with context %} to pass the current context.

10. Using Macros

Jinja2 macros in Ansible templates function similarly to functions, allowing you to define reusable code blocks. They are particularly useful for generating repetitive HTML or configuration content.

Macro Description Usage
macro_name Defines a reusable block of code {% macro macro_name(param1, param2) %}...{% endmacro %}

Example: Defining and Using a Macro

{% macro render_button(text, url, type='primary') %}
<button class="btn btn-{{ type }}" href="{{ url }}">{{ text }}</button>
{% endmacro %}

{{ render_button('Click Me', '/home', 'secondary') }}
Note: Macros must be defined before they are used in the template.

Key Points About Macros

  • Macros are defined using the {% macro %} tag.
  • They can accept parameters just like functions.
  • Macros are expanded inline at template rendering time.

11. Escaping Variables

Variable Escaped Value Description
variable {{ variable|escape }} Escapes a variable for safe HTML output
variable {{ variable|e }} Short alias for the escape filter

Example: Escaping HTML in Templates

{% set html_content = "<p>Hello & welcome</p>" %}
{{ html_content|escape }}
Note: The escape filter is particularly useful when rendering user-generated content to prevent XSS attacks.

12. Controlling Whitespace

Option Values Description
trim true / false Removes leading and trailing whitespace from template content
lstrip_blocks true / false Removes whitespace at the beginning of a line when it's immediately after a Jinja2 block
rstrip_blocks true / false Removes whitespace at the end of a line when it's immediately before a Jinja2 block

Example: Controlling Whitespace in Templates

{% filter trim %}
    This template has extra whitespace.
{% endfilter %}

{%- if true -%}
  This line will not have leading or trailing spaces.
{%- endif -%}
Note: Whitespace control is particularly useful when generating configuration files or code where formatting matters.

Was this article helpful?