Jinja is a powerful template engine for Python. Inside Ansible it is often used to dynamically create files from templates and fill in placeholders with data.
The Jinja language (now in version 2) also offers features like loops and data manipulation. Building files from loops and data-structures is more complex, especially when considering whitespace and line breaks. It can get tricky very fast.
The general approach (and I think many will agree with me on this one) is to just write e.g. a Jinja loop, see what the rendered outcome is, adjust and tweak it a little bit and make it work. This could be described as an trial-and-error approach.
The whole topic of whitespace control in Jinja within Ansible seems to be manageable with about 50% know-how. The last 50% can be completed by fiddling around.
I have done this myself that way for years. Though you are never an expert, you will get the job done and reach your goal. This article is trying to fill in the missing 50% and enable you (and me) to handle white-spaces and line-breaks effortless.
Each segment has a section TL;DR
which gives you a brief summary of what’s happening - in case you return to this
article and you just quickly need the information.
We will go through a couple of scenarios in detail to understand what actually is going on. In order to give some examples, let’s agree first on common terms:
Syntax Element | Code Example | Term |
---|---|---|
for/while/if |
{% for ... %} |
Tag (within a block). |
{% foobar %}{% endfoobar %} |
{% for i in x %}...{% endfor %} |
Block. |
{{ foobar }} |
{{ i }} |
Variable (replacement). |
All examples will use the following Ansible playbook and template file as starting point:
Code example...
$ tree /tmp/ansible/ /tmp/ansible/ ├── playbook.yml └── template.js $ cat /tmp/ansible/playbook.yml --- - hosts: all gather_facts: false become: false connection: local vars: i: foobar tasks: - name: Deploy a testfile. ansible.builtin.template: src: /tmp/ansible/template.j2 dest: /tmp/ansible/outputfile $ cat /tmp/ansible/template.j2 #jinja2: trim_blocks: True, lstrip_blocks: False ---- {% if true %} {{ i }} {% endif %} ----
The Ansible playbook (playbook.yml
) and Jinja template file will, when run with
Ansible, create a file /tmp/ansible/outputfile
with rendered content.
Code example...
With this out of the way, we can have a look at how we can influence white-spaces and line-breaks in Jinja templating within Ansible.
lstrip_blocks and trim_blocks
TL;DR
lstrip_blocks: False
: IfTrue
, remove leading tabs/white-spaces from start of line until the next block.trim_blocks: True
: Remove trailing newlines in lines with only block elements.
Details
There are two main switches in Jinja templating in Ansible, that influence the overall template rendering.
lstrip_blocks
trim_blocks
Those are parameters from Jinja whitespace control.
By default Ansible templates have set trim_blocks: True
and lstrip_blocks: False
.
This differs from the default Jinja2 configuration in Python/Jinja. The example template file
/tmp/ansible/template.j2
has set the same parameters in the top of the file (line 2). The line beginning with
#jinja2
could be technically omitted, when left unchanged. I included it for clarification. We will also play around with it later on.
The syntax rules allow two formats of writing this line. Both are interchangeable. You can even mix them if you like:
#jinja2: trim_blocks: True, lstrip_blocks: False
#jinja2: trim_blocks: "true", lstrip_blocks: "false"
#jinja2: trim_blocks: "true", lstrip_blocks: False
#jinja2: trim_blocks: True, lstrip_blocks: "false"
These two parameters are the only ones included in the templating rendering engine from Jinja within Ansible. Here is what they mean:
Parameter | Description |
---|---|
lstrip_blocks |
If set to True/"true" , tabs and spaces will be removed from the beginning of the line to the start of the next block as long as no other elements are in-between. |
trim_blocks |
If set to True/"true" , the first newline after a template tag is automatically removed, if there is nothing between the newline and the template tag. This is being enabled per default within Ansible templates. |
As mentioned: trim_blocks
is set to True/"true"
in Ansible. That is why the template file renders like this:
# cat /tmp/ansible/outputfile
----
foobar
----
and not to
----
foobar
----
The lines with the if condition are removed from the output. The line only contains the if block - which renders no
output) and a following newline. Since trim_blocks: True
applies, the whole line is not rendered.
For Ansible this makes practical sense. Jinja blocks usually do not contain much besides loop, conditions, expressions and other functionality, that - when rendered - usually does not not result in any content.
The lstrip_blocks
setting has no effect in this example. First of all this parameter is disabled by default (and by the
line being present in the template file). Second, there is no
block with leading whitespace in the template file present. The line containing the if-condition does not start with a whitespace or
line-break. So lstrip_blocks
would not even apply if activated.
This example will make it clear:
Code example...
Adding white-spaces before the if-block and keeping the Ansible default setting, will render the template differently. This will be rendered to: Notice the four leading white-spaces before the output `foobar` in the output file? Two of them are from the actual line with ` {{ i }}`. The other two are from the previous line heading the if-tag. Setting `lstrip_blocks: True` in the template file will change the template rendering, remove leading white-spaces before a block and return the initial state of the rendered output file.{%-
and -%}
TL;DR
Remove all white-spaces and new-lines leading or appending until the next encountered block element.
Details
The hyphen switches only address lines within the template. Adding a hyphen into a Jinja block or variable will remove any heading or pending white-spaces and new-lines before or after that element until the next block element. The exact behaviour depends on where the hyphen is being added.
This is a long sentence, so let’s break it down in an example: Adding a hyphen into a variable will remove heading white-spaces and new-lines.
The following example just adds a hyphen and explores what happens.
Code example...
We add a hyphen in front of the variable element: This renders the template to the following output. Any whitespace before the variable element has been removed. In the following code excerpt the removed white-spaces have been replaced with `X` and newlines with `\n` for clarification. This would not actually render correctly, since the `X`, etc are not affected by the hyphens. If we do the opposite and add the hyphen at the end of the template instead, the output turns out different. Renders to This is interesting. The output is missing a line-break following the block element as well. The expectation would be line-breaks being removed until the `{{ endif }}` tag. If you agree, you forget about the effect of `trim_blocks: True` in the default settings of the Ansible template engine, which removes the first new line after any template tag. In this case this leads to the removal of two newlines and the template file rendering this way. We can even move the hyphen from the `{{ i }}` element to the `{% endif %}` block and the file template will be rendered unchanged. Is being rendered to: Unless you add another newline into the template file, you will not be able to render the output file as expected.{%+
and +%}
TL;DR
{%+
: Disablelstrip_blocks: True
for a specific line.+%}
: Disabletrim_blocks: True
for a specific line.
Details
The plus sign +
has two different effects, depending on where is is used.
Adding a plus character to the beginning of the first Jinja block in a line will disable
lstrip_blocks: True
for that particular line. This works only for the first
block in a line. Any following block on that line will be ignored.
Code example...
The example below contains two white-spaces before the closing if-condition block. Also: `lstrip_bocks: True` has been enabled! Those will be removed since `lstrip_blocks: True` has been set in the first line of the template file. If we now disable `lstrip_blocks` only for the line with the endif-block, the heading white-spaces will be preserved. The `+`-character makes all the difference. The result will be the usual: Though the white-spaces are not visible, they are in the output file. Here is the output file with `X` instead of white-spaces: It is not possible to turn around the configuration, disable `lstrip_blocks` for the whole template and then enable it for a single line. The `+`-character only disables.Adding the +
-character to the closing braces of the first block in a line disables the option
trim_blocks
if set to True/"true"
.
Code example...
This example will preserve the line-breaks that are introduced with the if-condition block. Notice thattrim_blocks: True
has been set and any leading
whitespace and line-breaks should be removed.
The output renders now to this:
In order to correct the output back to the default, we actually have to remove some line-breaks. There are several ways of doing this:
Both solutions restore the default rendering, but differ in readability. I prefer the first solution due to the clarity.
The output then will be again:
Summary
The time it took to write this article was well spent. The tinkering with the parameters and settings, trying to find any unexpected behaviour; all that increased my comfort level with the Jinja whitespace control.
The previous approach with just roughly writing the correct template and then hacking the result still works, of course. But after spending time going through the examples in detail and trying to understand what is happening, I find myself spending less time on try-and-error and more often render my templates correct on the first try.
One thing though I have to keep in mind:
Each parameter on its own is easy to handle. Confusion arises in combination with the default
settings for trim_blocks
and lstrip_blocks
which differ from standard Jinja/Python in Ansible. Since the default
settings and the line-operated switches can influence similar behaviour, unexpected things might happen.
Secondly, while the hyphen addresses white-spaces and new-lines, the plus character affects the general template rendering and the effect differs depending on if it is placed in the opening or closing braces.
This general overview served me well so far:
Tweak | Description |
---|---|
{‰- |
Remove all heading white-spaces and new-lines before a block. |
-%} |
Remove all trailing white-spaces and new-lines after the current block. |
{%+ |
Disable lstrip_blocks for the current line. |
+%} |
Disable trim_blocks for the current line. |
#jinja2: trim_bloks: True |
Remove line-breaks from block-lines. |
#jinja2: lstrip_blocks: True |
Remove leading white-spaces before elements. |
I hope this article was able to shed some light on how whitespace control works in Ansible in combination with Jinja.