Les imports et includes Ansible
Mise à jour :
Dans l’écriture de code d’infrastructures Ansible, il devient nécessaire de diviser les playbooks en blocs plus petits et réutilisables pour simplifier la maintenance et améliorer la clarté du code. Cela devient particulièrement indispensable lorsque l’on gère des environnements complexes avec de nombreuses configurations différentes. Pour répondre à ces besoins, Ansible propose plusieurs modules permettant d’importer et d’inclure des playbooks, rôles, tâches et variables de manière efficace. Ces modules permettent de construire des playbooks modulaires et flexibles, en facilitant la réutilisation du code tout en garantissant une structure logique et bien organisée.
Différence entre import
et include
Avant de plonger dans les détails techniques, il est important de bien différencier les modules import et include dans Ansible. Chaque groupe possède ses propres caractéristiques et usage spécifique.
Modules import (statique)
- import_tasks : Importe une liste de tâches statiquement et les exécute immédiatement.
- import_playbook : Importe un playbook complet pour l’exécuter comme partie intégrante du playbook principal.
- import_role : Importe un rôle pour l’exécuter dès le début, sans conditions.
Modules include (dynamique)
- include_vars : Charge des variables depuis un fichier de manière dynamique, utile pour gérer différentes configurations d’environnement.
- include_role : Charge et exécute un rôle dynamiquement, avec la possibilité de le conditionner.
- include_tasks : Inclut une liste de tâches de façon dynamique, exécutée selon des conditions définies.
Un fonctionnement différent
Comprendre la différence entre les modules import et include est indispensable pour bien structurer vos playbooks Ansible. Bien qu’ils semblent similaires, ces deux types de modules ont des comportements très distincts qui influencent la manière dont les tâches, rôles ou variables sont chargés et exécutés.
Avec les modules import, le chargement du contenu est statique. Cela signifie que lorsque le playbook est exécuté, tout le contenu importé (qu’il s’agisse d’un rôle, de tâches ou d’un playbook) est immédiatement chargé et évalué au moment où Ansible lit le fichier. Le traitement des éléments importés se fait donc dès le début, indépendamment de toute condition ou logique d’exécution. Cela garantit que tout ce qui est importé sera exécuté, peu importe les circonstances.
Par exemple, dans le cas de l’utilisation de import_tasks
:
Ici, les tâches contenues dans taches_communes.yml
seront systématiquement
incluses et évaluées au chargement du playbook, avant même l’exécution
proprement dite.
En revanche, les modules include fonctionnent de manière dynamique. Le contenu n’est chargé que lorsqu’il est nécessaire, c’est-à-dire au moment où la ligne contenant le module include est atteinte pendant l’exécution du playbook. Ce mécanisme permet d’inclure des tâches, des rôles ou des variables de manière conditionnelle, en fonction du contexte ou des données spécifiques du système.
Voici un exemple avec include_tasks
:
Dans ce cas, les tâches ne seront incluses et exécutées que si le système
cible utilise Ubuntu. C’est ici que l’aspect dynamique prend tout son sens : le
fichier taches_specifiques.yml
n’est chargé que si la condition est remplie.
Cela vous permet d’adapter votre playbook à différents environnements sans
avoir à dupliquer des fichiers ou du code.
En résumé, import est une approche prévisible où tout le contenu est chargé dès le début, tandis que include offre une plus grande flexibilité en s’exécutant uniquement quand les conditions sont remplies. Utiliser import est judicieux lorsque l’on sait que toutes les parties d’un playbook ou d’un rôle doivent être systématiquement exécutées. À l’inverse, include est plus adapté lorsque l’on souhaite conditionner l’exécution à certains critères, ce qui permet de réduire la duplication de code et d’adapter les playbooks à de divers environnements.
import_tasks
: Importer une liste de tâches
Le module import_tasks permet d’importer une liste de tâches à partir d’un fichier externe dans un playbook Ansible. L’importation est statique, ce qui signifie que les tâches sont chargées et évaluées dès le début de l’exécution du playbook, avant même que les conditions ou les étapes d’exécution ne soient atteintes. Ce mécanisme statique est idéal lorsque vous savez que toutes les tâches doivent être exécutées systématiquement, sans avoir besoin d’une évaluation conditionnelle.
L’importation de tâches est simple. Supposons que vous ayez un fichier de
tâches réutilisables appelé taches_communes.yml
. Vous pouvez l’importer
dans votre playbook comme suit :
Dans cet exemple, toutes les tâches définies dans le fichier
taches_communes.yml
seront importées et évaluées lors du chargement du
playbook, ce qui signifie qu’elles seront exécutées sans conditions
supplémentaires. C’est une approche idéale si vous avez besoin de structurer un
playbook complexe et souhaitez organiser les tâches en plusieurs
fichiers.
import_playbook
: Importer un playbook
Le module import_playbook permet d’importer et d’exécuter un autre playbook à partir d’un playbook principal. Comme avec les autres modules d’import, cette inclusion est statique, ce qui signifie que le contenu du playbook importé est chargé et évalué au moment où le playbook principal est lancé. L’importation d’un playbook est particulièrement utile pour organiser et diviser des playbooks complexes en plusieurs fichiers, rendant ainsi la gestion et la maintenance plus aisées.
Voici un exemple d’utilisation du module import_playbook. Supposons que vous
ayez un playbook secondaire nommé sous_playbook.yml
que vous souhaitez
inclure dans votre playbook principal :
Dans cet exemple, le playbook sous_playbook.yml
sera importé et toutes les
tâches qu’il contient seront exécutées de manière statique, comme si elles
étaient directement écrites dans le playbook principal. Cela permet de
séparer logiquement les playbooks, tout en conservant une exécution
linéaire.
Le contenu de sous_playbook.yml
pourrait être le suivant :
Dans cet exemple, lorsque le playbook principal inclut sous_playbook.yml
,
la tâche d’installation d’Apache sera exécutée comme si elle était directement
incluse dans le playbook principal.
Variables globales partagées
Lorsque vous utilisez import_playbook, les variables définies dans le playbook principal peuvent être utilisées dans les playbooks importés, et vice versa. Cela permet de partager facilement des informations entre les différentes parties d’un projet.
Exemple :
Dans le playbook config_web.yml
, vous pouvez accéder à la variable
http_port
sans avoir besoin de la redéclarer :
Cette fonctionnalité rend l’utilisation de import_playbook très puissante, car elle permet de centraliser les variables et de les partager entre plusieurs playbooks.
import_role
: Importer un rôle dans un playbook
Le module import_role permet d’importer un rôle directement dans un playbook Ansible. Contrairement à include_role, qui charge les rôles de manière dynamique, import_role les charge de façon statique, c’est-à-dire que l’exécution du rôle est déterminée dès que le playbook est chargé par Ansible. Cela garantit que toutes les tâches du rôle seront exécutées et l’importation se fait sans condition.
Ce module est particulièrement utile pour des scénarios où l’exécution des rôles est systématique et où il n’est pas nécessaire de l’évaluer dynamiquement, ce qui simplifie la structure du playbook tout en assurant une exécution immédiate et cohérente des rôles.
L’utilisation du module import_role est simple et directe. Voici un exemple
d’importation d’un rôle nommé mon_role
:
Dans cet exemple, le rôle mon_role
sera importé statiquement et exécuté
avec toutes ses tâches dès que le playbook est lancé. Cela permet de
s’assurer que tout le contenu du rôle sera exécuté, sans possibilité
d’exécution conditionnelle.
Voici un exemple de contenu d’un rôle nommé mon_role
qui se trouve dans le
répertoire roles/mon_role/
:
Lorsque le rôle est importé via import_role dans un playbook principal, les tâches de ce fichier seront exécutées immédiatement après le chargement du playbook.
Utilisation de variables avec import_role
Vous pouvez passer des variables spécifiques à un rôle lors de son importation. Cela permet de personnaliser l’exécution du rôle sans avoir à modifier directement les fichiers de rôle eux-mêmes. Voici un exemple où des variables sont passées à un rôle :
Dans cet exemple, le rôle config_web
sera importé avec les variables
http_port
et server_name
, qui peuvent ensuite être utilisées dans les
tâches du rôle pour personnaliser la configuration du serveur web.
Voici un exemple de l’utilisation de ces variables dans le rôle :
Grâce à l’utilisation de variables, vous pouvez adapter le rôle à des environnements spécifiques sans avoir à modifier le code source du rôle lui-même.
include_vars
: Charger des variables dynamiquement
Le module include_vars permet de charger des variables depuis un fichier externe directement dans un playbook Ansible. Il est extrêmement utile pour rendre un playbook plus dynamique et modulaire en séparant les variables de configuration du reste du code. Contrairement à une déclaration de variables standard dans un playbook, include_vars permet de les charger de manière conditionnelle et d’ajuster leur contenu en fonction de l’environnement ou des spécificités d’un hôte.
Le module include_vars permet de charger des variables depuis plusieurs formats de fichier, y compris YAML, JSON ou INI. En utilisant ce module, vous pouvez facilement gérer différentes configurations pour des environnements de production, de développement ou de tests, sans devoir dupliquer vos playbooks.
Prenons un exemple où nous avons un fichier vars_fichier.yml
contenant des
variables spécifiques à une configuration :
Contenu du fichier vars_fichier.yml
:
Dans le playbook, nous allons utiliser include_vars pour charger ces variables :
Dans cet exemple, les variables contenues dans vars_fichier.yml
(comme
http_port
, db_user
, etc.) sont chargées au moment de l’exécution et peuvent
être utilisées dans le playbook immédiatement après leur inclusion.
Inclusion conditionnelle des variables
L’un des avantages majeurs de include_vars est la possibilité de charger des variables de manière conditionnelle, en fonction du système d’exploitation, de la distribution, ou de tout autre paramètre pertinent.
Voici un exemple qui charge des variables spécifiques en fonction de la distribution Linux de l’hôte :
Dans cet exemple, si l’hôte exécute un système Debian, le fichier
vars_debian.yml
sera chargé. Si l’hôte est sur RedHat, c’est le fichier
vars_redhat.yml
qui sera inclus. Cela permet de gérer des environnements
multiples dans un seul playbook, en adaptant dynamiquement les variables
en fonction du contexte d’exécution.
Utilisation avancée : Chargement de plusieurs fichiers
Le module include_vars peut également être utilisé pour charger un ensemble de fichiers dans un répertoire, ce qui est pratique pour centraliser les variables par rôle, par hôte, ou par environnement.
Exemple de chargement de toutes les variables présentes dans un dossier
variables/
:
Dans cet exemple, Ansible charge toutes les variables contenues dans les
fichiers .yml
présents dans le répertoire variables/
. Cela permet de
structurer les variables par fichier et de les organiser de façon plus
claire.
Charger des variables dynamiquement avec include_vars
et first_found
Dans Ansible, il est souvent nécessaire d’adapter les configurations en fonction
de l’environnement ou du type de système d’exploitation de l’hôte. Pour répondre
à ce besoin, Ansible offre des moyens puissants pour charger des fichiers de
variables dynamiquement selon des critères spécifiques, tout en ayant des
options de secours en cas de non-correspondance. L’utilisation du module
include_vars avec le plugin lookup first_found
permet de réaliser cela
de manière efficace.
Le module include_vars est utilisé pour charger un fichier de variables
dans un playbook Ansible. Ce fichier de variables contient des valeurs clés
qui peuvent ensuite être utilisées dans les tâches du playbook. En combinant
ce module avec le plugin lookup, notamment la fonction first_found
,
Ansible peut rechercher un fichier de variables parmi plusieurs options et
charger le premier fichier trouvé.
Le principe de first_found est de parcourir une liste de fichiers possibles et de charger le premier fichier valide qu’il trouve. Cela permet d’adapter les variables selon différents critères comme le nom de la distribution, la famille du système d’exploitation, ou, à défaut, un fichier de variables par défaut.
Dans cet exemple :
- include_vars est utilisé pour inclure un fichier de variables.
- lookup(‘ansible.builtin.first_found’, params) : Cette expression utilise le plugin first_found pour rechercher le premier fichier de variables existant dans une liste.
- params : Définit les fichiers à rechercher et le chemin dans lequel
chercher ces fichiers.
- files : La liste des fichiers potentiels. Les fichiers sont construits
dynamiquement en fonction des facts Ansible :
{{ansible_distribution}}.yaml
: Un fichier correspondant à la distribution de l’OS, par exempleUbuntu.yaml
ouCentOS.yaml
.{{ansible_os_family}}.yaml
: Un fichier correspondant à la famille de l’OS, par exempleDebian.yaml
ouRedHat.yaml
.default.yaml
: Un fichier de variables par défaut, utilisé si aucun des fichiers spécifiques à l’OS n’est trouvé.
- paths : Indique où Ansible doit chercher ces fichiers. Dans cet exemple,
les fichiers de variables sont recherchés dans le répertoire
vars/
.
- files : La liste des fichiers potentiels. Les fichiers sont construits
dynamiquement en fonction des facts Ansible :
Cas d’utilisation
Prenons un exemple pratique où un hôte sous Ubuntu exécute ce playbook. Ansible procédera comme suit :
- Cherche un fichier nommé
Ubuntu.yaml
dans le répertoirevars/
. - Si ce fichier n’existe pas, il cherche
Debian.yaml
(car Ubuntu appartient à la famille Debian). - Si aucun de ces deux fichiers n’est trouvé, Ansible charge
default.yaml
.
Ce comportement garantit que des variables spécifiques à l’environnement seront utilisées si elles existent et que des valeurs par défaut seront employées en dernier recours si aucun fichier spécifique n’est trouvé.
Avantages
- Adaptabilité : En utilisant les facts d’Ansible, vous pouvez facilement adapter les variables à différentes distributions ou familles de systèmes d’exploitation.
- Robustesse : Le fichier par défaut (
default.yaml
) assure qu’aucun playbook ne manque de variables essentielles, même si les fichiers spécifiques à l’OS ne sont pas trouvés. - Centralisation des variables : Les variables sont centralisées dans des fichiers séparés, facilitant la gestion et la maintenance de configurations multi-environnements. Par exemple, vous pouvez avoir un fichier de configuration pour chaque distribution Linux ou famille d’OS et un fichier de secours pour les cas imprévus.
include_tasks
: Inclure dynamiquement une liste de tâches
Le module include_tasks permet d’inclure une liste de tâches provenant d’un fichier externe, de manière dynamique, dans un playbook Ansible. Ce chargement dynamique permet également de conditionner l’inclusion des tâches, en les exécutant uniquement si certaines conditions sont remplies.
L’utilisation de include_tasks est assez simple. Supposons que nous ayons un
fichier de tâches appelé taches_communes.yml
qui contient des tâches
réutilisables, nous pouvons l’inclure dans notre playbook comme suit :
Dans cet exemple, le fichier taches_communes.yml
est inclus dynamiquement
lorsque la ligne est atteinte pendant l’exécution du playbook. Toutes les
tâches contenues dans ce fichier seront exécutées comme si elles faisaient
partie du playbook principal.
Voici un exemple de ce à quoi pourrait ressembler le fichier
taches_communes.yml
:
En incluant ce fichier avec include_tasks, ces tâches seront ajoutées au flux principal du playbook et exécutées dans l’ordre, exactement comme si elles étaient définies dans le playbook lui-même.
Inclusion conditionnelle de tâches
L’un des grands avantages de include_tasks est la possibilité d’exécuter des tâches uniquement si certaines conditions sont remplies. Par exemple, vous pouvez inclure une liste de tâches spécifiques en fonction du système d’exploitation de l’hôte :
Dans cet exemple, si l’hôte appartient à la famille de systèmes d’exploitation
Debian, les tâches de taches_debian.yml
seront incluses et exécutées. Si
l’hôte est sous RedHat, les tâches de taches_redhat.yml
seront incluses à
la place. Cela vous permet de gérer des configurations spécifiques à des
environnements différents tout en maintenant un playbook modulaire et
flexible.
Gestion des erreurs avec include_tasks
Vous pouvez également gérer les erreurs de manière plus fine en utilisant
include_tasks avec des blocs ou des instructions comme ignore_errors
ou
failed_when
. Cela vous permet d’assurer une certaine robustesse dans vos
playbooks, en contrôlant la façon dont Ansible doit se comporter lorsqu’une
erreur survient pendant l’inclusion des tâches.
Dans cet exemple, si une erreur se produit lors de l’inclusion des tâches de
taches_specifiques.yml
, le bloc rescue
est exécuté, affichant un message de
débogage. Cela permet de maintenir un contrôle précis sur l’exécution du
playbook, même en cas de problème.
Inclure des tâches dynamiquement avec include_vars
et with_first_found
Le principe décrit dans le chapitre sur include_vars
peut être reproduit de la
même manière :
include_role
: Charger et exécuter un rôle de façon dynamique
Le module include_role permet de charger et exécuter un rôle de manière dynamique dans un playbook Ansible. Contrairement à import_role, qui charge et exécute un rôle au moment où le playbook est lu, include_role est évalué dynamiquement, c’est-à-dire que son exécution ne se fait que lorsque la ligne contenant le module est atteinte. Cela permet une exécution conditionnelle des rôles, offrant ainsi plus de flexibilité pour ajuster les opérations en fonction des paramètres de l’environnement ou d’une situation particulière.
Voici un exemple simple d’utilisation de include_role pour charger et
exécuter un rôle nommé mon_role
:
Dans cet exemple, Ansible va inclure et exécuter le rôle mon_role
lorsque
cette tâche sera atteinte. Cela peut être pratique pour des playbooks où
l’exécution d’un rôle dépend de certaines conditions spécifiques.
L’un des principaux avantages de include_role est la possibilité de conditionner l’exécution d’un rôle en fonction de variables ou de faits recueillis sur l’hôte. Par exemple, on peut vouloir exécuter un rôle spécifique uniquement sur des systèmes Linux de type Debian.
Dans ce cas, le rôle debian_role
ne sera exécuté que si la condition
ansible_os_family == "Debian"
est remplie, ce qui permet d’adapter
dynamiquement le playbook en fonction de l’environnement de l’hôte. Si
l’hôte utilise une autre famille de systèmes d’exploitation, le rôle ne sera pas
exécuté.
Passage de paramètres avec include_role
Vous pouvez également passer des paramètres spécifiques au rôle lorsque vous utilisez include_role. Cela permet de personnaliser davantage l’exécution d’un rôle sans modifier directement le contenu du rôle lui-même. Voici un exemple où un rôle reçoit des variables dynamiques :
Dans cet exemple, le rôle config_serveur_web
recevra les variables
http_port
et server_name
, qui peuvent être utilisées à l’intérieur du rôle
pour configurer un serveur web de manière flexible.
Conclusion
L’utilisation des modules d’import et d’inclusion dans Ansible, tels que import_playbook, import_role, import_tasks, include_role, include_tasks et include_vars, permet de structurer vos playbooks de manière modulaire, tout en optimisant leur maintenabilité et leur flexibilité. Chaque module offre des avantages distincts en fonction des besoins, que ce soit pour une exécution systématique avec les modules d’import ou pour une gestion plus conditionnelle et dynamique avec les modules d’inclusion.
Maîtriser ces modules vous permet de gérer des infrastructures complexes tout en maintenant une architecture claire et réutilisable. En fonction de vos besoins, vous pouvez facilement charger des tâches, des rôles, des variables, ou même des playbooks entiers en fonction des environnements et des conditions spécifiques. Cette approche modulaire garantit un déploiement fiable et adaptable dans des environnements variés, tout en minimisant la duplication du code.
Avec des outils comme include_vars et le plugin first_found, vous pouvez également rendre vos configurations encore plus dynamiques, en adaptant vos playbooks aux spécificités des systèmes d’exploitation et des environnements cibles. En somme, une bonne utilisation de ces modules permet de concevoir des playbooks puissants, maintenables et évolutifs, capables de répondre aux besoins d’infrastructures modernes.