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 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