Utiliser Jinja avec Ansible
Mise à jour :
Dans ce guide, je vais vous exposer l’utilisation de Jinja avec Ansible pour construire des fichiers de configurations dynamiques. Comme vu dans mon cours sur Python, Jinja est un moteur de template puissant qui permet de manipuler des variables, d’utiliser des boucles et des conditions.
En plus de sa capacité à générer des fichiers de configuration, Jinja peut également être utilisé directement dans les tâches Ansible pour manipuler des données à l’aide de filtres. Ces filtres permettent de transformer les valeurs des variables, par exemple en modifiant des chaînes de caractères, en formattant des dates ou en effectuant des opérations sur des listes. Cela permet d’adapter les valeurs des variables avant de les utiliser dans d’autres tâches, et donc de simplifier la gestion des données complexes.
Je vais me concentrer sur des cas d’utilisation concrets, en montrant comment tirer parti des fonctionnalités de Jinja pour optimiser vos workflows avec Ansible.
Utilisation du module template
Le module template
d’Ansible permet de générer des fichiers de
configuration dynamiques à partir de templates Jinja. En combinant ce module
avec les fonctionnalités puissantes de Jinja, vous pouvez non seulement
personnaliser les fichiers en fonction de vos variables Ansible, mais aussi vous
assurer que ces fichiers sont créés de manière sécurisée, notamment en
gérant les permissions et en masquant les informations sensibles.
Le module template
: présentation et syntaxe de base
Le module
template
d’Ansible permet de copier un fichier Jinja (généralement avec une extension
.j2
) depuis votre machine de contrôle ou votre dépôt Ansible vers l’hôte
distant, en le transformant dynamiquement grâce aux variables Jinja.
Voici la syntaxe de base du module template
:
Dans cet exemple, le fichier ssh_config.j2
est transformé en
/etc/ssh/ssh_config
sur l’hôte cible, avec les permissions adéquates. Le
propriétaire du fichier sera root
et les permissions définies avec le
mode 0640
, ce qui signifie que seul le propriétaire peut écrire dans le
fichier, mais tout le monde ne peut pas le lire.
Protégez vos données sensibles
Lors de la génération de fichiers de configuration, il est fréquent d’utiliser des informations sensibles comme des mots de passe, des clés API ou des certificats. Ansible propose plusieurs méthodes pour manipuler ces données de manière sécurisée, notamment via les variables chiffrées avec Ansible Vault.
Supposons que vous génériez une configuration pour une base de données où un mot de passe utilisateur est nécessaire. Le mot de passe est stocké dans une variable Ansible chiffrée avec Ansible Vault :
Ici, la configuration est générée avec les permissions 0600
, garantissant
que seul root
peut lire et écrire dans le fichier, empêchant ainsi tout
autre utilisateur d’accéder à des informations sensibles comme le mot de passe.
Ecriture des templates Jinja
Un template Jinja pour Ansible est un fichier texte qui contient des variables et des instructions de contrôle (comme des conditions ou des boucles) permettant de générer du contenu dynamique. Il est utilisé pour créer des fichiers de configuration ou d’autres documents en adaptant leur contenu en fonction des données fournies par Ansible.
Ces templates utilisent la syntaxe Jinja, un moteur de template qui permet de manipuler des variables, d’effectuer des transformations avec des filtres et d’ajouter des éléments dynamiques comme des boucles ou des conditions. Lors de l’exécution d’un playbook, Ansible remplace les variables dans le template par leurs valeurs réelles et génère un fichier prêt à être utilisé. Cela permet de créer des configurations personnalisées, adaptées à chaque machine ou environnement, de manière flexible et automatisée.
Voici un exemple simple de fichier de template pour la configuration du service
sshd
:
Explication des variables :
- ssh_port : Le port sur lequel SSH écoute (par défaut 22).
- permit_root_login : Autorise ou interdit la connexion de l’utilisateur root via SSH (par défaut ‘no’ pour des raisons de sécurité).
- password_auth : Active ou désactive l’authentification par mot de passe (par défaut ‘no’ pour forcer l’utilisation des clés SSH).
- x11_forwarding : Active ou désactive le forwarding X11 (par défaut ‘no’).
- allowed_users : Liste des utilisateurs autorisés à se connecter via SSH.
Plus d’infos sur la configuration du service SSH
Voici un exemple de playbook manipulant ce template :
Une fois le playbook executé le fichier suivant sera déposé sur le serveur cible :
Les filtres Jinja dans Ansible
Les filtres dans Jinja sont des outils puissants qui permettent de transformer et manipuler les variables avant leur utilisation dans les templates ou les tâches Ansible. Que ce soit pour adapter des chaînes de caractères, manipuler des structures de données, ou encore définir des valeurs par défaut, les filtres offrent une flexibilité supplémentaire dans la gestion des variables.
Les filtres s’intègrent facilement dans Ansible via les Les filtres s’intègrent facilement dans Ansible via les templates Jinja, mais aussi directement dans les taches de playbooks. Ils permettent de nettoyer ou d’adapter des données avant de les injecter dans des tâches, ce qui est particulièrement utile pour manipuler les sorties de modules ou ajuster des paramètres spécifiques selon les besoins.
Les filtres de contrôles des variables
-
Le filtre
default
permet de définir une valeur par défaut si une variable n’est pas définie ou est vide. -
mandatory
: Ce filtre force la vérification qu’une variable est définie et non vide. Si la variable est absente, Ansible arrête l’exécution avec une erreur. -
defined
: Vérifie si une variable est définie (sans s’assurer qu’elle n’est pas vide).
Les filtres manipulant des chaines de caractères
Supposons que vous vouliez configurer un fichier avec des variables
d’environnement et que vous deviez mettre les noms des serveurs en majuscules.
Vous pouvez utiliser le filtre upper
pour transformer une chaîne de
caractères.
Cela convertira la variable ansible_hostname
en majuscules avant de
l’injecter dans votre fichier de configuration.
Jinja propose de très nombreux filtres utiles. Voici quelques exemples courants utilisés avec Ansible :
-
default
: Ce filtre permet de définir une valeur par défaut si une variable n’est pas définie ou est vide.Exemple :
Ici, si la variable
nginx_port
n’est pas définie, elle prendra la valeur 80. -
replace
: Ce filtre remplace des sous-chaînes spécifiques dans une chaîne de caractères.Exemple :
Ce template changera le chemin
/usr/local/bin
en/opt/local/bin
. -
lower
etupper
: Ces filtres permettent de convertir une chaîne en minuscules ou en majuscules.Exemple :
Les Filtres manipulant des listes et des dictionnaires
Jinja permet aussi de travailler avec des listes ou des dictionnaires de manière flexible. Voici quelques exemples de filtres très utiles pour les administrateurs systèmes.
-
join
: Ce filtre permet de convertir une liste en une chaîne de caractères séparée par un délimiteur donné.Exemple :
Si
packages_list
contient["nginx", "mysql", "php"]
, cette ligne générera la chaîne suivante :"nginx, mysql, php"
. -
sort
: Ce filtre trie les éléments d’une liste.Exemple :
-
map
: Ce filtre permet d’appliquer une transformation à chaque élément d’une liste.Exemple :
Cela permet d’extraire les valeurs de la clé
shell
pour chaque utilisateur dans une liste de dictionnaires.
Des Filtres dans les tâches Ansible
Les filtres ne sont pas seulement utiles dans les templates, mais peuvent aussi être directement utilisés dans les tâches Ansible pour traiter des données avant qu’elles ne soient utilisées dans les modules. Cela permet de gérer plus facilement les données retournées par les modules ou les inventaires dynamiques.
Voici un exemple où le filtre join
est utilisé dans une tâche Ansible pour
générer une liste d’utilisateurs à partir d’une variable.
Dans cet exemple, la tâche debug affiche une liste d’utilisateurs sous forme de chaîne, séparée par des virgules.
Développer des Filtres Personnalisés
En plus des nombreux filtres fournis par Jinja, il est possible de créer des filtres personnalisés pour répondre à des besoins spécifiques. Cela peut se faire en combinant des filtres existants ou en définissant des filtres Ansible dans des plugins personnalisés.
Dans des environnements complexes, créer vos propres filtres vous permettra d’adapter Jinja à vos besoins spécifiques, par exemple en manipulant des formats de données spécifiques à votre infrastructure.
Les conditions dans Jinja
Les conditions dans Jinja permettent d’adapter dynamiquement le contenu des templates en fonction de la valeur des variables Ansible. En utilisant des instructions conditionnelles, vous pouvez contrôler le rendu du template et inclure ou exclure certaines sections selon des critères spécifiques. Cela vous permet de générer des fichiers de configuration ou d’exécuter des tâches de manière plus flexible et contextuelle, en fonction des besoins réels de votre infrastructure.
Syntaxe de base des conditions
Les conditions Jinja utilisent la syntaxe if
, elif
et else
, similaire
à celle de Python. Voici la structure de base d’une condition Jinja :
Dans cet exemple, si la variable enable_feature
est définie et a une
valeur “vraie” (comme true
ou yes
), la fonctionnalité est activée. Sinon,
elle est désactivée.
Comparaison et opérateurs logiques
Vous pouvez utiliser des opérateurs de comparaison (comme ==
, !=
, <
,
>
, <=
, >=
) et des opérateurs logiques (comme and
, or
, not
) pour
créer des conditions plus complexes.
Comparaison de variables
Dans cet exemple, la variable env
est comparée à la chaîne 'production'
.
Si elle correspond, la configuration SSL est activée, sinon elle est désactivée.
Opérateurs logiques
Ici, la condition vérifie deux critères : que env
est égal à
'production'
et que ssl_enabled
est activé. Si les deux conditions
sont remplies, le SSL est activé.
Vérification des variables définies
Il est souvent nécessaire de vérifier si une variable Ansible est définie
avant de l’utiliser dans un template, afin d’éviter des erreurs. Jinja propose
des tests comme is defined
et is not defined
pour cela.
Conditions imbriquées
Dans cet exemple, on vérifie d’abord si la variable user
est définie. Si
c’est le cas, on adapte les niveaux d’accès en fonction de l’utilisateur (admin
ou non). Si user
n’est pas défini, un accès invité est attribué.
Utilisation des conditions avec des listes ou des dictionnaires
Les conditions Jinja peuvent aussi être utilisées pour parcourir des listes ou des dictionnaires et appliquer des logiques conditionnelles à chaque élément.
Exemple : Itération avec conditions
Ici, pour chaque service dans la liste services
, on vérifie son statut et
affiche un message différent selon qu’il est actif ou non.
Conditions avec filtres
Les filtres Jinja peuvent être utilisés dans des conditions pour manipuler des données avant de les comparer. Par exemple, vous pouvez vérifier si une variable correspond à un certain modèle en utilisant des filtres de texte.
Dans cet exemple, le filtre regex_search
est utilisé pour vérifier si
l’URL commence par “https”. Si c’est le cas, la connexion est marquée comme
sécurisée.
Utilisation des filtres dans les conditions des tâches Ansible
En combinant les filtres avec les conditions dans les tâches, vous pouvez vérifier si une variable remplit certains critères ou adapter des actions selon la forme des données. Voici quelques cas d’utilisation où les filtres Jinja peuvent enrichir vos conditions de tâches Ansible.
La directive when
permet d’exécuter une tâche uniquement si une condition
est vraie. Vous pouvez y inclure des filtres Jinja pour manipuler les variables
avant de les comparer.
Exemple : Vérifier si une variable contient une certaine chaîne
Si vous souhaitez exécuter une tâche uniquement lorsque la variable
hostname
contient la chaîne “web”, vous pouvez utiliser le filtre
search
pour vérifier cela :
Ici, la tâche redémarre Apache uniquement si le hostname contient “web”. Cela peut être utile pour exécuter certaines tâches sur un sous-ensemble de serveurs.
Utilisation des filtres de texte dans les conditions
Les filtres de texte comme regex_search
et replace
sont
particulièrement utiles pour valider ou adapter des chaînes de caractères avant
de décider si une tâche doit être exécutée.
Vous pouvez utiliser regex_search
pour vérifier si une variable correspond
à un modèle spécifique, comme une adresse IP, avant d’exécuter une tâche.
Ici, la tâche debug ne s’exécutera que si my_ip
est définie et
correspond à une adresse IP valide au format IPv4.
Les boucles et itérations avec Jinja
Les boucles et les itérations sont des éléments clés lorsque vous
travaillez avec des données dans Jinja et Ansible. Elles permettent de parcourir
des listes, des dictionnaires ou d’autres structures de données complexes pour
générer automatiquement du contenu répétitif dans des fichiers de configuration,
ou pour exécuter des tâches répétitives dans vos playbooks. Jinja facilite ces
itérations à l’aide de la syntaxe {% for %}
, ce qui permet de rendre vos
templates Ansible dynamiques et adaptables à différents scénarios.
Syntaxe de base de la boucle for
La boucle for
dans Jinja fonctionne de manière similaire aux boucles en
Python. Elle permet d’itérer sur des listes, des dictionnaires ou
d’autres structures de données, tout en exécutant des actions spécifiques à
chaque itération. Voici la syntaxe de base pour utiliser une boucle dans un
template Jinja :
Cette boucle parcourra chaque élément de la liste items
et l’affichera.
Utilisée dans un contexte Ansible, cette syntaxe peut par exemple générer des
lignes de configuration à partir d’une liste de serveurs ou d’utilisateurs.
Jinja permet aussi d’itérer sur des dictionnaires, en accédant à la fois aux clés et aux valeurs à chaque itération. C’est particulièrement utile pour des configurations qui nécessitent à la fois des noms et des paramètres associés.
Voici un exemple où l’on itère sur un dictionnaire de services pour générer une configuration de démarrage automatique.
Si la variable services
est un dictionnaire de ce type :
Le template générera le contenu suivant :
Variables spéciales dans les boucles
Jinja fournit plusieurs variables spéciales dans les boucles pour rendre l’itération plus flexible. Ces variables permettent d’accéder à des informations supplémentaires sur la boucle elle-même, comme l’index actuel, le nombre total d’éléments, ou encore la parité de l’élément (pair ou impair).
Variables spéciales courantes :
loop.index
: Donne l’index 1-based de l’élément actuel (1, 2, 3, …).loop.index0
: Donne l’index 0-based de l’élément actuel (0, 1, 2, …).loop.revindex
: Donne l’index inverse de la boucle (décompte depuis la fin).loop.length
: Donne la longueur totale de la liste ou du dictionnaire itéré.loop.first
: RenvoieTrue
si c’est le premier élément de la boucle.loop.last
: RenvoieTrue
si c’est le dernier élément de la boucle.
Voici un exemple où on utilise des variables de boucle pour afficher l’index de chaque élément et ajouter une ligne spéciale pour le dernier élément.
Si la variable hosts
contient une liste de serveurs, ce template générera
:
Les inclusions dans les templates Jinja
L’utilisation des inclusions dans les templates Jinja permet de diviser vos fichiers de configuration en blocs réutilisables et modulaires. Plutôt que d’avoir un seul grand template contenant toutes les directives, vous pouvez organiser et réutiliser des parties de code commun en les séparant dans différents fichiers. Cela améliore la lisibilité, facilite la maintenance, et évite la duplication de code.
Dans Ansible, les inclusions sont particulièrement utiles pour des
configurations complexes où certaines parties peuvent être communes à plusieurs
systèmes ou services. L’instruction include
de Jinja vous permet
d’intégrer un autre fichier template directement à l’endroit où vous en avez
besoin dans le fichier principal.
Syntaxe de base de l’inclusion
La directive include
de Jinja s’utilise pour insérer un autre template à
l’intérieur du fichier principal. La syntaxe est la suivante :
Le template spécifié sera inséré à cet endroit lors du rendu. Vous pouvez inclure des templates multiples et ils peuvent contenir eux-mêmes des variables, des boucles, ou d’autres logiques Jinja.
Dans cet exemple, le fichier ssl_config.j2
est inclus à l’intérieur du
fichier principal main_config.j2
, ajoutant une configuration SSL commune à
plusieurs fichiers de configuration serveur.
Avantages de l’inclusion
L’inclusion dans les templates présente plusieurs avantages pour les administrateurs systèmes :
- Modularité : Vous pouvez diviser vos fichiers de configuration en plusieurs morceaux logiques (par exemple, configuration réseau, configuration SSL, etc.), ce qui rend la gestion plus simple.
- Réutilisabilité : Vous pouvez réutiliser des parties de templates dans plusieurs fichiers sans avoir à dupliquer le code. Cela est utile si vous avez des blocs de configuration identiques pour différents services.
- Maintenance facilitée : Lorsque vous avez besoin de changer une partie commune de la configuration, vous pouvez modifier un seul template inclus plutôt que de devoir modifier plusieurs fichiers de configuration.
Conclusion
Dans ce guide, j’ai exploré l’utilisation de Jinja avec Ansible, en mettant l’accent sur des fonctionnalités telles que les filtres, les boucles, les conditions et l’inclusion de templates. Ces outils permettent de générer des fichiers de configuration dynamiques et modulaires, tout en assurant une gestion flexible et optimisée de vos infrastructures. Grâce à ces techniques, vous pouvez simplifier et sécuriser la création de vos configurations automatisées, tout en réduisant la duplication de code.
Pour une compréhension plus approfondie de Jinja, notamment sa syntaxe et ses fonctionnalités avancées, je vous invite à vous référer au cours Python, où j’ai déjà détaillé le fonctionnement de ce moteur de templates de manière plus complète. Cela vous permettra de maîtriser pleinement les aspects de Jinja applicables à d’autres contextes, en dehors d’Ansible.