What does “​ | “​ in Ansible jinja2 templating?

Ashkan Kamyab
Nov 1 · 5 min read
Ansible ❤ jinja2

It is a while that the Open-Community invented such a distinguished software manager for Linux/Unix systems. Even though Ansible has strong support for Windows as well(Finally Microsoft convinced to implement OpenSSH on windows by default). But what I am gonna talk about today is beyond a simple yml scripting for deployment and etc. I have recently started to write some playbooks on my own because I found it really handy to use for my personal Storages, Web and Application servers. Before I used to write functions on a Fabfile in order to solve each task. So here it makes sense to raise a Question, why I should have such a fabulous idea to re-invent the wheel when something is working very good???. The first answer would be “Just For Fun”(aka JFF) and the second answer is that Jinja2 gives me the power to program my deployment in a very dynamic and versatile approach…

So I assume that you have already known what are Jinja2 and Ansible and what they do but I write this Article to share with you some hidden features of Ansible and its wonderful colleague Jinja2. We knew that we can create some *.j2 files in the templates folder and call this template accordingly for a specific task and make our life easier. btw I have a simple example here:

{% for vhost in hosts -%} server { listen 80; server_name {{ vhost }} www.{{ vhost }}; location / { root /var/www/{{ vhost }}/public_html; index index.html index.htm; error_log /var/www/{{ vhost }}/error.log; } } {% endfor %}

So as you can see, it reads a list variable of “hosts” and iterates through it and finally creates the corresponding directive of Nginx config file. So if we call this template with the template module of Ansible tasks, ansible asks jinja2 templating to parse this file for it and complete the file accordingly.

But as we know Jinja2 has more capabilities than this. If you want to annotate Jinja as Ninja, feel free because you are absolutely right. so let’s create some tasks of debug that we can read the identical msg after jinja2 compilation

hosts: test tasks: - name: Jinja2 for statement debug: msg: > example of for statement {% for ip in ansible_all_ipv4_addresses -%} IP Address entry {{ loop.index }} = {{ ip }} {% endfor %}

So in the above example ```{{ loop.index }} ``` gives you the ability to access the actual index of your loops. As you know it might be very handy at the time you occasionally want to access something applying the index of it. It was something that I have noticed recently and I guess it might be good for you to know that such a feature exists. But I am going to talk about filters in Jinj2 which are really useful when we want to select some Specifics. As an example let’s say we need only all even numbers and we don’t like odds. In the following example, there are some points that we can speak for many hours about them. For example what would be the corresponding result if we set step of the loop 2 instead of 1, or on the other hand what would be the corresponding result if we set the range 0 to 10 instead. But thanks to the team of Python, Ansible and Jinja2 we do not need to. we can use filters.

hosts: test tasks: - name: for iteration in range debug: msg: > {% for number in range(1, 11, 1) -%} {% if number is odd -%} {% continue %} {% endif -%} {{ number }} {% endfor %}

Using filters depends on the use case could be the Best Practice. Also, maintain and housekeeping of playbooks get easier. lets jump to the topic quickly,

if you go to this page you will have literally the access to whole documentation about filter regarding jinja2 with Ansible playbooks. I will some also cover some of them here.

If you like to use a filter you need to specify it using “|”. The pip in jinja2 litriture means filter by. The two most simplest are min and max. Just take a look at the following example:

hosts: test tasks: - name: Ansible Jinja2 filters debug: msg: > ---*** Min and Max filter ***--- {{ [1, 2, 3, 4, 5] | min }} {{ [1, 2, 3, 4, 5] | max }}

If you execute ansible-playbook and passing this playbook as playbook it will give you one “1” corresponding by min filter and five “5” corresponding to max filter. In the reality you can combine this filters either with notify and handlers or with when directives to achieve your proper condition in order to have a clean deployment. There are still some simple but handy filters available that you can use them depend on your use cases, like unique, difference and random.

- > --- --- [ , , , , , , , , , ] [ , , , , ] [ , , ] [ , , ]

The most sophisticated one that I really liked when I used for first time is urlspilit which needs to be provided with one valid argument. According to the Documentation they are:

{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('hostname') }} # => 'www.acme.com' {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('netloc') }} # => 'user:password@www.acme.com:9000' {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('username') }} # => 'user' {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('password') }} # => 'password' {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('path') }} # => '/dir/index.html' {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('port') }} # => '9000' {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('scheme') }} # => 'http' {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('query') }} # => 'query=term' {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('fragment') }} # => 'fragment' {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit }} # => # { # "fragment": "fragment", # "hostname": "www.acme.com", # "netloc": "user:password@www.acme.com:9000", # "password": "password", # "path": "/dir/index.html", # "port": 9000, # "query": "query=term", # "scheme": "http", # "username": "user" # }

Or you may need to use regex to extract some IP address you need. It worth to note it down, that here in Jinja2 regex is based on python standard regular expression module(aka re)

{{ 'Some DNS servers are 8.8.8.8 and 8.8.4.4' | regex_findall('\\b(?:[0-9]{1,3}\\.){3}[0-9]{1,3}\\b') }}

Or you need to replace some text:

{{ 'foobar' | regex_replace('^f.*o(.*)$', '\\1') }}

Use map for some complex variable. in this case comma seprated mountpoints

{{ ansible_mounts | map(attribute='mount') | join(',') }}

Use quote to make a variable to string and many many other examples.

You can also combine filters

"{{ [1,2,3,4,5] | permutations | list }}" "{{ [1,2,3,4,5] | permutations(3) | list }}"

And many many other capabilities that you can invent by combine filters.

I hope you enjoyed my Article and feel free to send me your feedback and raise your question regarding Ansible.


Originally published at https://www.linkedin.com.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade