Ansible Hall of Shame: Jinja Loop Counter

Someone was working with a Jinja template that they hadn’t created and having difficulty understanding it, so they brought it to the #ansible IRC channel and I hated it so much I had to preserve it for posterity.

A slightly simplified version:

{% set count = [1] %}
{% for item in ['a', 'b', 'c'] %}
{% for node in ['d', 'e'] %}
{{ node }}:{{ item }} {{ count | regex_replace('\[', '') | regex_replace('\]', '') }}
{% if count.append(count.pop() + 1) %}{% endif %}
{% endfor %}
{% endfor %}

This produces:

d:a 1
e:a 2
d:b 3
e:b 4
d:c 5
e:c 6

While storing a count in a single-element list isn’t as silly as it first looks, since it allows you to work around Jinja’s scoping rules and set a value from inside a loop, using regex_replace() twice to munge the string representation of the list is a bad choice. Even just using replace() twice would be better, if you don’t remember that you can directly access the element via count[0].

As long as you have a reasonably recent version of Jinja, a namespace object is a better way to solve this:

{% set ns = namespace(count=1) %}
{% for item in ['a', 'b', 'c'] %}
{% for node in ['d', 'e'] %}
{{ node }}:{{ item }} {{ ns.count }}
{% set ns.count = ns.count + 1 %}
{% endfor %}
{% endfor %}