Cool Ansible 3 - Advanced Templating

Jinja is a powerful template engine for Python. It’s commonly used to place variables and other content in files. You can see it in action by using Ansible’s template task. There is much more functionality that can be used to take Ansible playbooks to the next level.

If Statements

If statements are extremely helpful in Ansible. One of the best use cases I’ve had is modifying configuration files based on Ansible facts. I use Goss to test the roles for my Bytes of Swiss project. One role requires uninstalling the netcat package and installing a different one. Debian systems with apt use netcat-traditional and RHEL systems with yum use nc. Instead of making a test file for each operating system that only varies by one line, we can use an if statement.

  {% if ansible_os_family == "Debian" %}
  {% else %}
  {% endif %}
    installed: true

    installed: false


Loops are useful for creating multiple lines in a file from a list in Ansible. If a variable is defined as shown below, the playbook will create a Samba share configuration for each set of variables in the sambashares list.

- hosts: localhost
  gather_facts: no
      - name: public
        comment: Public resources
        path: /samba/public
        read_only: yes
        browsable: yes
      - name: private
        comment: Private resources
        path: /samba/private
        read_only: no
        browsable: no
    - name: Create Samba shares
        path: ./samba.conf
        create: yes
        block: |
          {% for share in sambashares %}
            [{{ }}]
            comment = {{ share.comment }}
            path = {{ share.path }}
            read only = {{ share.read_only }}
            browsable = {{ share.browsable }}

          {% endfor %}

Another use it to make list objects for Ansible to use. You can find my example playbook at this GitHub Gist. It makes a list of strings for debug to print which outputs nicer than using with_items and actually prints on a new line rather than print the \n character.

This use can help make complex Ansible playbooks that require operations or calculations on variables before use.


Ansible extends Jinja templating by introducing lookups. Lookups can be used to read in data from outside of the playbook and its related files. There are a plethora of lookup functions but the popular two are most likely env and file. These can set a variable to the value of an environment variable and content of a file respectively.

  file_contents: "{{lookup('file', 'path/to/file.txt') }}"
  secret_key: "{{ lookup('env', 'MY_SECRET_KEY') }}"

Some other notable lookups are url which retrieves the cotent of a URL, and fileglob which returns files matching a certain search, like doing a programmatic ls /my/path/*.txt.


Filters can perform operations on variables. They work the same way as functions. There are filters you may recognize from Python like upper and lower for strings and join and reverse for lists.

Filters are used by following a variable with a pipe character | and then the filter name.

{{ [6, 8, 3, 9] | max }}

Some are simple like the above example, but other filters can take arguments and do more advanced operations.

- name: give me longest combo of three lists , fill with X
    msg: "{{ [1,2,3] | zip_longest(['a','b','c','d','e','f'], [21, 22, 23], fillvalue='X') | list }}"

Ansible created their own additional filters. One of the most useful I’ve found is ipaddr. It can calculate a subnet mask, broadcast address, etc. given an IP address in CIDR notation which is extremely helpful when working with the Netbox API. It can also return if an address is public or private or how many IP addresses are in a range and even convert IPv4 addresses to IPv6.


This is just the tip of the iceberg when it comes to templating. Jinja already has an impressive feature set and Ansible even extends that with their own plugins. All of these enable playbooks to have advanced logic and manipulate data in complex ways.


Jinja Docs

Ansible Docs about Jinja2

Written on June 21, 2019