Aller au contenu
Infrastructure as Code medium

Jinja2 syntaxe de base : interpolation, boucles, conditions, whitespace

70 min de lecture

Logo Ansible

Jinja2 est le moteur de templating utilisé par Ansible pour générer des fichiers de configuration dynamiques. Au-delà de la simple interpolation {{ var }} que vous avez vue partout, Jinja2 supporte des boucles ({% for %}), des conditions ({% if %}), des commentaires ({# #}), et un contrôle fin du whitespace ({%- ... -%}). Cette page synthétise la syntaxe que vous taperez dans tout fichier .j2 ou dans une string Ansible avec interpolation Jinja.

L'enjeu : générer un fichier de config (motd, nginx.conf, sshd_config) à partir d'un template + variables, sans avoir à scripter ligne par ligne avec lineinfile:.

  • {{ var }} pour interpoler une variable ou une expression
  • {% for %} + {% if %} pour la logique
  • {# commentaire #} pour commenter (ne sera pas dans la sortie)
  • Le whitespace control : {%- ... -%} et l'option trim_blocks: true
  • Différence entre un fichier .j2 (template) et une string Ansible avec Jinja inline
{# Ceci est un commentaire (n'apparaît PAS dans la sortie générée) #}
Hello {{ name | upper }} ← interpolation : remplace par la valeur
{% if user_count > 0 %} ← logique : pas affiché tel quel
Vous avez {{ user_count }} utilisateurs.
{% endif %}
{% for u in users %} ← boucle
- {{ u.name }} ({{ u.email }})
{% endfor %}
MarqueurRôleApparaît dans la sortie ?
{{ ... }}Interpolation d'une variable / expressionOui (la valeur)
{% ... %}Logique (if, for, set, include)Non (mais le résultat est dans la sortie)
{# ... #}CommentaireNon
{{ ansible_distribution }} {{ ansible_distribution_version }}
{{ user.name | default('inconnu') }}
{{ ports | join(', ') }}
{{ 2 + 3 }} ← expression Python (5)

L'interpolation accepte toute expression Python valide (limitée par sandbox) + les filtres Jinja (| upper, | default, | join).

{% if env == 'prod' %}
debug = false
{% elif env == 'staging' %}
debug = true
log_level = info
{% else %}
debug = true
log_level = debug
{% endif %}

Les opérateurs disponibles : ==, !=, >, <, >=, <=, and, or, not, in. Les tests (vu page suivante) : is defined, is none, is mapping.

{% for user in users %}
- {{ user.name }} ({{ user.email }})
{% endfor %}

Variables magiques loop.* disponibles dans la boucle :

VariableValeur
loop.indexIndex 1-based (1, 2, 3...)
loop.index0Index 0-based (0, 1, 2...)
loop.firsttrue si première itération
loop.lasttrue si dernière itération
loop.lengthNombre total d'éléments
{% for u in users -%}
{{ loop.index }}. {{ u.name }}{% if not loop.last %},{% endif %}
{% endfor %}

Boucle imbriquée :

{% for env, hosts in environments.items() %}
[{{ env }}]
{% for h in hosts %}
{{ h }}
{% endfor %}
{% endfor %}
{# Cette ligne est invisible dans le fichier généré #}
{# Utile pour expliquer la logique du template aux mainteneurs #}
{# Ne pas confondre avec les commentaires shell # qui SONT dans la sortie #}

Les {# #} ne sont jamais dans la sortie, différents des # bash comments qui apparaissent dans le fichier généré (parce qu'ils sont du texte normal pour Jinja).

Sans contrôle, ce template :

Services :
{% for s in services %}
- {{ s }}
{% endfor %}

Produit (avec des lignes vides indésirables) :

Services :
- sshd
- chronyd
- firewalld

Le \n après chaque {% ... %} est conservé. Solution : {%- ... -%} strip le whitespace avant/après le tag :

Services :
{% for s in services -%}
- {{ s }}
{% endfor %}

Sortie propre :

Services :
- sshd
- chronyd
- firewalld

Convention :

  • {%- : strip whitespace avant le tag
  • -%} : strip whitespace après le tag
  • {{- ... -}} : idem pour les expressions

Plutôt que poser {%- / -%} partout, vous pouvez activer ces deux options au niveau du module template :

- name: Template avec whitespace control automatique
ansible.builtin.template:
src: motd.j2
dest: /etc/motd
trim_blocks: true # supprime le \n après chaque {% %}
lstrip_blocks: true # supprime les espaces de début de ligne devant {% %}
mode: "0644"

Avec trim_blocks: true + lstrip_blocks: true, le template précédent sans {%- produit le bon résultat. C'est la combinaison recommandée, vous l'activez par défaut.

Voici le template validé sur le lab ecrire-code-jinja2-base :

templates/motd.j2
==========================================
Bienvenue sur {{ inventory_hostname }}
==========================================
{% if host_role == "DB" %}
Profil : DB
{% endif %}
Services :
{% for s in services -%}
- {{ s }}
{% endfor %}

Le playbook qui consomme ce template :

- name: Challenge jinja2 base
hosts: db1.lab
become: true
vars:
host_role: DB
services:
- postgresql
- chronyd
- firewalld
tasks:
- name: Poser /etc/motd-challenge via template
ansible.builtin.template:
src: templates/motd.j2
dest: /etc/motd-challenge
mode: "0644"

Contenu posé sur db1.lab :

==========================================
Bienvenue sur db1.lab
==========================================
Profil : DB
Services :
- postgresql
- chronyd
- firewalld

Les if et for sont rendus, le whitespace control sur le for évite les lignes vides entre services.

Il y a deux contextes où vous écrivez du Jinja en Ansible :

- name: Générer nginx.conf
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
mode: "0644"

Le fichier complet est traité comme template Jinja2. Tous les marqueurs {{ }}, {% %}, {# #} sont actifs.

- name: Déposer foo
ansible.builtin.copy:
dest: /tmp/foo
content: "Bienvenue sur {{ inventory_hostname }}\n"
mode: "0644"

Seules les expressions {{ }} sont évaluées. Pas de boucles ou conditions inline.

Pour les logiques complexes, basculez sur un vrai fichier .j2 + le module template:.

SymptômeCauseFix
Lignes vides indésirables après {% for %}Whitespace par défaut conservé{%- ... -%} ou trim_blocks: true + lstrip_blocks: true
{# commentaire #} apparaît dans la sortieMauvais marqueur (probablement { # ... # } avec espaces)Pas d'espaces : {# comment #}
Boucle qui plante avec 'NoneType' has no attributeVariable absente dans certains itemsPréfixer par is defined ou default()
Variables Ansible non interpolées dans .j2Variables non passées au playVérifier vars: du play ou host_vars/
&#123;&#123; var &#125;&#125; non interpolé dans un YAML inlineManque les quotesToujours quoter : content: "&#123;&#123; var &#125;&#125;\n"
  • 3 marqueurs Jinja2 : {{ }} interpolation, {% %} logique, {# #} commentaire.
  • Whitespace control essentiel : {%- ... -%} ou trim_blocks: true + lstrip_blocks: true (recommandé).
  • loop.* expose index, first, last, length dans une boucle for.
  • Différence fichier .j2 (Jinja complet) vs Jinja inline (interpolation seule).
  • Le module template: transforme le .j2 côté control node, copie le résultat vers le managed node.

Le whitespace d'un template ne se corrige qu'en regardant le fichier rendu. Le lab vous fait interpoler des variables, conditionner un bloc avec {% if %}, itérer avec {% for %}, puis supprimer les lignes vides parasites par le whitespace control. Le fichier généré est comparé au résultat attendu, ce qui rend chaque saut de ligne en trop détectable.

  • Module template : le module qui consomme ce .j2, avec trim_blocks, backup et surtout validate.
  • Tests Jinja2 : les prédicats is defined et is mapping qui sécurisent les blocs {% if %} de vos gabarits.
  • lineinfile vs template : savoir reconnaître les cas où deux lignes suffisent et où un gabarit complet est disproportionné.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn