Templates Jinja2
Les templates Jinja2 sont des fichiers texte qui contiennent des variables et des expressions Jinja2. Ansible utilise Jinja2 comme moteur de template par défaut pour générer des fichiers de configuration dynamiques basés sur des variables définies dans les playbooks, les inventaires ou ailleurs.
Syntaxe de base des templates Jinja2
Les templates Jinja2 utilisent trois types de délimiteurs spécifiques pour insérer des variables et des expressions dans les fichiers texte :
1. Variables : {{ ... }}
Affiche la valeur d'une variable :
Nom du serveur: {{ ansible_hostname }}
Adresse IP: {{ ansible_default_ipv4.address }}
2. Instructions : {% ... %}
Pour les structures de contrôle (conditions, boucles, etc.) :
{% if ansible_distribution == "Ubuntu" %}
Configuration pour Ubuntu
{% endif %}
3. Commentaires : {# ... #}
Non affichés dans le résultat final :
{# Ceci est un commentaire #}
{# TODO: Ajouter la configuration SSL #}
Variables Jinja2
Vous pouvez utiliser n'importe quelle variable Ansible dans vos templates Jinja2. Voici les types de variables couramment utilisées :
Accès aux variables Ansible :
{# Variables d'inventaire #}
{{ inventory_hostname }}
{{ ansible_host }}
{# Facts système #}
{{ ansible_hostname }}
{{ ansible_distribution }}
{{ ansible_distribution_version }}
{{ ansible_memtotal_mb }}
{{ ansible_processor_vcpus }}
{# Variables définies dans le playbook #}
{{ my_variable }}
{{ http_port }}
Accès aux structures de données :
{# Dictionnaires #}
{{ my_dict.key }}
{{ my_dict['key'] }}
{# Listes #}
{{ my_list[0] }}
{{ my_list[-1] }} {# Dernier élément #}
{# Variables imbriquées #}
{{ ansible_default_ipv4.address }}
{{ hostvars[inventory_hostname]['ansible_hostname'] }}
Variables de groupe et hôte :
{# Parcourir les hôtes d'un groupe #}
{% for host in groups['webservers'] %}
{{ hostvars[host]['ansible_host'] }}
{% endfor %}
Filtres Jinja2
Les filtres Jinja2 permettent de manipuler les variables et les données dans les templates. Voici quelques filtres couramment utilisés :
Filtres de chaînes de caractères :
{{ "hello" | upper }} {# HELLO #}
{{ "WORLD" | lower }} {# world #}
{{ "hello world" | capitalize }} {# Hello world #}
{{ "hello world" | title }} {# Hello World #}
{{ " text " | trim }} {# text #}
{{ "hello" | replace('h', 'j') }} {# jello #}
{{ "word1 word2 word3" | split }} {# ['word1', 'word2', 'word3'] #}
Filtres de listes :
{{ [1, 2, 3, 4, 5] | length }} {# 5 #}
{{ [1, 2, 3] | first }} {# 1 #}
{{ [1, 2, 3] | last }} {# 3 #}
{{ [3, 1, 2] | sort }} {# [1, 2, 3] #}
{{ [1, 2, 3, 2, 1] | unique }} {# [1, 2, 3] #}
{{ ['a', 'b', 'c'] | join(', ') }} {# a, b, c #}
{{ [1, 2, 3, 4, 5] | sum }} {# 15 #}
{{ [10, 5, 20, 15] | min }} {# 5 #}
{{ [10, 5, 20, 15] | max }} {# 20 #}
Filtres de valeurs par défaut :
{{ variable_non_definie | default('valeur par défaut') }}
{{ variable_vide | default('valeur', true) }} {# true = considérer les valeurs vides #}
Filtres de conversion :
{{ my_dict | to_json }} {# Conversion en JSON #}
{{ my_dict | to_nice_json }} {# JSON indenté #}
{{ my_dict | to_yaml }} {# Conversion en YAML #}
{{ my_dict | to_nice_yaml }} {# YAML indenté #}
{{ "42" | int }} {# Conversion en entier #}
{{ 42.7 | round }} {# Arrondi: 43 #}
{{ 42.7 | round(1) }} {# Arrondi à 1 décimale: 42.7 #}
Filtres mathématiques :
{{ -42 | abs }} {# 42 #}
{{ 42.7 | round }} {# 43 #}
{{ 42.3 | round(method='floor') }} {# 42 #}
{{ 42.7 | round(method='ceil') }} {# 43 #}
Filtres spécifiques à Ansible :
{{ some_password | password_hash('sha512') }}
{{ '/path/to/file' | basename }} {# file #}
{{ '/path/to/file' | dirname }} {# /path/to #}
{{ my_list | random }} {# Élément aléatoire #}
Structures de contrôle
Jinja2 prend en charge les structures de contrôle telles que les conditions et les boucles pour rendre les templates plus dynamiques. La différence entre les filtres et les conditions est que les filtres modifient ou transforment les données, tandis que les conditions contrôlent le flux d'exécution en fonction de certaines valeurs ou états.
Syntaxe de base :
{% if condition %}
# Code exécuté si vrai
{% endif %}
Avec elif et else :
{% if ansible_distribution == "Ubuntu" %}
# Configuration Ubuntu
{% elif ansible_distribution == "CentOS" %}
# Configuration CentOS
{% elif ansible_distribution == "Debian" %}
# Configuration Debian
{% else %}
# Configuration par défaut
{% endif %}
Tests de variables :
{% if variable is defined %}
Variable est définie
{% endif %}
{% if variable is undefined %}
Variable n'est pas définie
{% endif %}
{% if variable is none %}
Variable est None
{% endif %}
Opérateurs logiques :
{% if ansible_memtotal_mb >= 4096 and ansible_processor_vcpus >= 4 %}
Serveur haute performance
{% endif %}
{% if port == 80 or port == 443 %}
Port HTTP/HTTPS
{% endif %}
{% if not debug_mode %}
Mode production
{% endif %}
Tests de types :
{% if variable is string %}
{% if variable is number %}
{% if variable is even %} {# Nombre pair #}
{% if variable is odd %} {# Nombre impair #}
{% if item in liste %}
{% if key in dictionnaire %}
Boucles
Les boucles permettent d'itérer sur des listes, des dictionnaires ou d'autres structures de données.
Boucle simple :
{% for item in liste %}
{{ item }}
{% endfor %}
Boucle sur un dictionnaire :
{% for key, value in dictionnaire.items() %}
{{ key }}: {{ value }}
{% endfor %}
Variables spéciales de boucle :
{% for package in packages %}
{{ loop.index }} {# Index à partir de 1 #}
{{ loop.index0 }} {# Index à partir de 0 #}
{{ loop.revindex }} {# Index inversé à partir de 1 #}
{{ loop.revindex0 }} {# Index inversé à partir de 0 #}
{{ loop.first }} {# True si premier élément #}
{{ loop.last }} {# True si dernier élément #}
{{ loop.length }} {# Nombre total d'éléments #}
{{ loop.cycle('odd', 'even') }} {# Alterne entre valeurs #}
{% endfor %}
Boucle avec condition :
{% for package in packages %}
{% if package.startswith('python') %}
{{ package }}
{% endif %}
{% endfor %}
Boucle avec else (si liste vide) :
{% for item in liste %}
{{ item }}
{% else %}
La liste est vide!
{% endfor %}
Boucle sur l'inventaire Ansible :
{% for host in groups['webservers'] %}
Server: {{ hostvars[host]['ansible_hostname'] }}
IP: {{ hostvars[host]['ansible_host'] }}
{% endfor %}
Boucle avec range :
{% for i in range(10) %} {# 0 à 9 #}
{% for i in range(1, 11) %} {# 1 à 10 #}
{% for i in range(0, 10, 2) %} {# 0, 2, 4, 6, 8 #}
Boucle imbriquée :
{% for category in categories %}
{{ category }}:
{% for item in categories[category] %}
- {{ item }}
{% endfor %}
{% endfor %}
Module template dans Ansible
Le module template d'Ansible permet de déployer des fichiers générés depuis des templates Jinja2.
Syntaxe de base :
- name: Déployer la configuration
template:
src: config.j2
dest: /etc/app/config.conf
owner: root
group: root
mode: '0644'
Paramètres importants :
- src : Chemin du template (dans templates/)
- dest : Chemin de destination sur l'hôte cible
- owner : Propriétaire du fichier
- group : Groupe du fichier
- mode : Permissions du fichier
- backup : Créer une sauvegarde avant modification (yes/no)
- validate : Commande pour valider le fichier avant de l'appliquer
- force : Écraser le fichier s'il existe déjà (yes/no, défaut: yes)
Avec validation :
- name: Déployer la configuration Nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
validate: nginx -t -c %s
backup: yes
notify: reload nginx
Le paramètre validate teste la syntaxe avant de remplacer le fichier. Le %s est remplacé par le chemin du fichier temporaire.
Avec sauvegarde :
- name: Déployer avec backup
template:
src: app.conf.j2
dest: /etc/app/app.conf
backup: yes
Crée automatiquement un fichier de sauvegarde avec timestamp (ex: app.conf.2024-12-02@14:30:15~)
3.11 Bonnes pratiques pour les templates
1. Organisation des templates :
- Placer les templates dans le dossier
templates/du rôle ou du playbook - Utiliser l'extension
.j2pour identifier les templates - Organiser en sous-dossiers si nombreux templates :
templates/ ├── nginx/ │ ├── nginx.conf.j2 │ └── vhost.conf.j2 ├── php/ │ └── php.ini.j2 └── partials/ ├── header.j2 └── footer.j2
2. Commentaires et documentation :
- Commenter les sections complexes
- Expliquer la logique des conditions
- Documenter les variables requises en haut du fichier :
{# Template: nginx.conf.j2 Description: Configuration principale de Nginx Variables requises: - worker_processes: Nombre de workers - worker_connections: Connexions par worker - server_name: Nom du serveur Variables optionnelles: - ssl_enabled: Active SSL (défaut: false) - gzip_enabled: Active la compression (défaut: true) #}
3. Valeurs par défaut :
- Toujours utiliser le filtre
defaultpour les variables optionnelles - Définir des valeurs sensées par défaut
worker_processes {{ worker_processes | default(ansible_processor_vcpus) }}; worker_connections {{ worker_connections | default(1024) }};