
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:.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »{{ 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’optiontrim_blocks: true - Différence entre un fichier
.j2(template) et une string Ansible avec Jinja inline
Prérequis
Section intitulée « Prérequis »- Avoir lu Filtres Jinja2 essentiels ;
- Connaître les variables Ansible (cf. Variables — déclaration).
Les 3 marqueurs Jinja2
Section intitulée « Les 3 marqueurs Jinja2 »{# 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 quelVous avez {{ user_count }} utilisateurs.{% endif %}
{% for u in users %} ← boucle- {{ u.name }} ({{ u.email }}){% endfor %}| Marqueur | Rôle | Apparaît dans la sortie ? |
|---|---|---|
{{ ... }} | Interpolation d’une variable / expression | Oui (la valeur) |
{% ... %} | Logique (if, for, set, include) | Non (mais le résultat est dans la sortie) |
{# ... #} | Commentaire | Non |
Interpolation {{ var }}
Section intitulée « Interpolation {{ var }} »{{ 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).
Conditions {% if %}
Section intitulée « Conditions {% if %} »{% if env == 'prod' %}debug = false{% elif env == 'staging' %}debug = truelog_level = info{% else %}debug = truelog_level = debug{% endif %}Les opérateurs disponibles : ==, !=, >, <, >=, <=, and, or, not, in. Les tests (vu page suivante) : is defined, is none, is mapping.
Boucles {% for %}
Section intitulée « Boucles {% for %} »{% for user in users %}- {{ user.name }} ({{ user.email }}){% endfor %}Variables magiques loop.* disponibles dans la boucle :
| Variable | Valeur |
|---|---|
loop.index | Index 1-based (1, 2, 3…) |
loop.index0 | Index 0-based (0, 1, 2…) |
loop.first | true si première itération |
loop.last | true si dernière itération |
loop.length | Nombre 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 %}Commentaires {# #}
Section intitulée « Commentaires {# #} »{# 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).
Whitespace control — le piège n°1
Section intitulée « Whitespace control — le piège n°1 »Sans contrôle, ce template :
Services :{% for s in services %} - {{ s }}{% endfor %}Produit (avec des lignes vides indésirables) :
Services :
- sshd
- chronyd
- firewalldLe \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 - firewalldConvention :
{%-: strip whitespace avant le tag-%}: strip whitespace après le tag{{- ... -}}: idem pour les expressions
Options globales : trim_blocks + lstrip_blocks
Section intitulée « Options globales : trim_blocks + lstrip_blocks »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 :
========================================== 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 - firewalldLes if et for sont rendus, le whitespace control sur le for évite les lignes vides entre services.
Différence : fichier .j2 vs Jinja inline
Section intitulée « Différence : fichier .j2 vs Jinja inline »Il y a deux contextes où vous écrivez du Jinja en Ansible :
1. Fichier .j2 consommé par template:
Section intitulée « 1. Fichier .j2 consommé par template: »- ansible.builtin.template: src: templates/nginx.conf.j2 dest: /etc/nginx/nginx.confLe fichier complet est traité comme template Jinja2. Tous les marqueurs {{ }}, {% %}, {# #} sont actifs.
2. Jinja inline dans une string Ansible
Section intitulée « 2. Jinja inline dans une string Ansible »- 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:.
Pièges fréquents
Section intitulée « Pièges fréquents »| Symptôme | Cause | Fix |
|---|---|---|
Lignes vides indésirables après {% for %} | Whitespace par défaut conservé | {%- ... -%} ou trim_blocks: true + lstrip_blocks: true |
{# commentaire #} apparaît dans la sortie | Mauvais marqueur (probablement { # ... # } avec espaces) | Pas d’espaces : {# comment #} |
Boucle qui plante avec 'NoneType' has no attribute | Variable absente dans certains items | Préfixer par is defined ou default() |
Variables Ansible non interpolées dans .j2 | Variables non passées au play | Vérifier vars: du play ou host_vars/ |
{{ var }} non interpolé dans un YAML inline | Manque les quotes | Toujours quoter : content: "{{ var }}\n" |
À retenir
Section intitulée « À retenir »- 3 marqueurs Jinja2 :
{{ }}interpolation,{% %}logique,{# #}commentaire. - Whitespace control essentiel :
{%- ... -%}outrim_blocks: true+lstrip_blocks: true(recommandé). loop.*exposeindex,first,last,lengthdans une bouclefor.- Différence fichier
.j2(Jinja complet) vs Jinja inline (interpolation seule). - Le module
template:transforme le.j2côté control node, copie le résultat vers le managed node.
Pratiquer dans le lab
Section intitulée « Pratiquer dans le lab »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é :
cd ~/Projets/ansible-training/labs/ecrire-code/jinja2-base/
cat README.md # tuto pas à pascat challenge/README.md # consigne du challengepytest -v challenge/tests/ # 6 tests testinfra