Aller au contenu
Infrastructure as Code medium

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

14 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 {% %}

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.

Cas pratique — MOTD avec if + for (lab ecrire-code/jinja2-base)

Section intitulée « Cas pratique — MOTD avec if + for (lab ecrire-code/jinja2-base) »

Voici le template validé sur le lab 26-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 :

- ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf

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

- ansible.builtin.copy:
dest: /tmp/foo
content: "Bienvenue sur {{ inventory_hostname }}\n"

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.

Cette page a un lab d’accompagnement : labs/ecrire-code/jinja2-base/ dans stephrobert/ansible-training. Il contient un README.md guidé, un Makefile (make verify lance les tests), et un challenge final auto-évalué : générer un /etc/motd-challenge qui combine {% if %} (profil DB) et {% for %} (liste de services) avec whitespace control propre.

Une fois le lab provisionné :

Fenêtre de terminal
cd ~/Projets/ansible-training/labs/ecrire-code/jinja2-base/
cat README.md # tuto pas à pas
cat challenge/README.md # consigne du challenge
pytest -v challenge/tests/ # 6 tests testinfra

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn