
Ansible offre 5 directives pour découper un playbook en fichiers réutilisables : import_tasks, include_tasks, import_role, include_role, import_playbook. La distinction import_* (static) vs include_* (dynamic) est fondamentale et testée à l'EX294 : elles n'ont pas le même comportement vis-à-vis des tags, conditions, variables runtime, handlers et --list-tasks.
À la fin de cette page, vous saurez choisir la bonne directive selon le cas d'usage (boucle ? conditionnel ? variables runtime ?), comprendre pourquoi import_tasks ne supporte pas loop:, et orchestrer un déploiement multi-fichiers avec import_playbook.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Distinguer
import_*(static, résolu au start) vsinclude_*(dynamic, résolu au runtime). - Choisir la bonne directive selon
loop:,tags:,when:, variables runtime. - Découper un playbook avec
import_taskspour la lisibilité. - Boucler sur un fichier de tâches avec
include_tasks+loop:. - Orchestrer plusieurs plays avec
import_playbook. - Comprendre la propagation des
tags:etwhen:selon la directive.
Prérequis
Section intitulée « Prérequis »- Avoir validé Premier playbook.
- Maîtriser les variables et
loop:.
Tableau de référence, quelle directive utiliser
Section intitulée « Tableau de référence, quelle directive utiliser »| Cas d'usage | Directive | Pourquoi |
|---|---|---|
Découper un playbook en fichiers tasks/*.yml (sans loop) | import_tasks | Static, plus rapide, supporte --list-tasks, propage tags:/when: |
Boucler sur un fichier de tâches (loop: [...]) | include_tasks | Static refuse loop: (variable de boucle inexistante au start) |
| Tagger un bloc entier de tâches incluses | import_tasks + tags: | Le tag se propage automatiquement aux tâches importées |
| Appeler un rôle classiquement | import_role ou directive roles: | Static, équivalent au mot-clé roles: du play |
| Boucler sur un rôle | include_role + loop: | Static refuse loop: |
| Orchestrer plusieurs plays | import_playbook (only) | include_playbook n'existe pas |
| Variable de runtime pas encore connue au start | include_* | Dynamic résout au moment de la tâche |
Règle d'or : par défaut, utiliser import_* (plus rapide, plus lisible avec --list-tasks). Basculer sur include_* uniquement si vous avez besoin de loop: ou d'une variable runtime.
import_tasks vs include_tasks
Section intitulée « import_tasks vs include_tasks »Static (import_tasks), résolu au démarrage
Section intitulée « Static (import_tasks), résolu au démarrage »- hosts: db1.lab tasks: - name: Importer le bloc d'install (parsé au start) ansible.builtin.import_tasks: tasks/install.yml tags: [setup] # ← propagé à TOUTES les tâches du fichierComportement :
- Ansible lit
tasks/install.ymlau démarrage du playbook (avant exécution). --list-tasksvoit toutes les tâches importées.- Les tags se propagent à chaque tâche du fichier importé.
- Les conditions
when:se propagent à chaque tâche. - Pas de
loop:possible (la variableitemn'existe pas encore au démarrage).
Dynamic (include_tasks), résolu au runtime
Section intitulée « Dynamic (include_tasks), résolu au runtime »- name: Inclure des tâches dynamiquement hosts: db1.lab tasks: - name: Inclure dans une boucle (dynamic, résolu au runtime) ansible.builtin.include_tasks: tasks/install.yml loop: [1, 2, 3] # ← supporté par include_tasks vars: marker_path: "/tmp/marker-{{ item }}.txt"Comportement :
- Ansible résout
tasks/install.ymlau moment où la tâche est rencontrée. --list-tasksvoit uniquement la directiveinclude_tasks, pas les tâches internes.- Les tags ne se propagent pas automatiquement aux tâches internes (sauf via
apply:). - Les conditions
when:sont évaluées une seule fois sur leinclude_taskslui-même. loop:supporté, c'est la seule raison principale d'utiliserinclude_*.
🔍 Observation cruciale : la différence visible se manifeste sur les tags et la boucle. Le piège classique : on tague un include_tasks en pensant que ça suffit, mais les tâches internes ne sont pas filtrées par --tags <nom>. Pour qu'un tag s'applique aux tâches internes du include, utiliser apply: { tags: [...] }.
Le piège des tags avec include_tasks
Section intitulée « Le piège des tags avec include_tasks »- hosts: db1.lab tasks: # Cas 1 : import_tasks (tag propagé OK) - ansible.builtin.import_tasks: tasks/setup.yml tags: [config]
# Cas 2 : include_tasks SANS apply: (tag NON propagé) - ansible.builtin.include_tasks: tasks/setup.yml tags: [config] # ← tag UNIQUEMENT sur include_tasks lui-même
# Cas 3 : include_tasks AVEC apply: (tag propagé aux tâches internes) - ansible.builtin.include_tasks: tasks/setup.yml apply: tags: [config] # ← propagé à toutes les tâches internes tags: [config]Lancer avec ansible-playbook --tags config :
- Cas 1 : toutes les tâches du fichier importé tournent. ✅
- Cas 2 : rien ne tourne. Les tâches internes ne sont pas taguées
config. ❌ - Cas 3 : toutes les tâches du fichier inclus tournent. ✅
🔍 Observation : le apply: est la clé pour propager les tags (et autres attributs) à travers un include_*. Source de bug fréquent en CI quand on filtre par tag.
import_role vs include_role
Section intitulée « import_role vs include_role »- hosts: db1.lab tasks: - name: Static, équivalent au mot-clé `roles:` du play ansible.builtin.import_role: name: my_role vars: role_var: value
- name: Dynamic, boucler sur un rôle (impossible avec import) ansible.builtin.include_role: name: my_role loop: [1, 2, 3] vars: role_iter: "{{ item }}"🔍 Observation : include_role permet de boucler sur un rôle ou d'appliquer un rôle conditionnellement au runtime. Usage rare mais puissant. Pour 95 % des cas, utiliser import_role ou directement la directive roles: du play.
import_playbook, orchestrer plusieurs plays
Section intitulée « import_playbook, orchestrer plusieurs plays »---# site.yml : orchestrateur- name: Installer la base de données ansible.builtin.import_playbook: playbooks/install_db.yml- name: Installer les serveurs web ansible.builtin.import_playbook: playbooks/install_web.yml- name: Configurer l'application ansible.builtin.import_playbook: playbooks/configure_app.yml🔍 Observation : import_playbook s'utilise uniquement au niveau plays (pas dans tasks:). include_playbook n'existe pas. Pattern standard pour découper un gros déploiement en sous-playbooks réutilisables, chacun avec son propre hosts:.
Pièges classiques
Section intitulée « Pièges classiques »import_tasks+loop:→ erreur de parsing au démarrage. Solution :include_tasks.- Tag sur
include_taskssansapply:→ tag non propagé aux tâches internes. Solution : ajouterapply: { tags: [...] }. include_playbookn'existe pas. Pour orchestrer des plays :import_playbookuniquement.--list-tasksne voit pas les tâches dans uninclude_*. Pour audit complet : utiliserimport_*partout où possible.- FQCN obligatoire en 2026 :
ansible.builtin.import_tasks, pas justeimport_tasks(règlefqcn-builtins).
À retenir
Section intitulée « À retenir »import_*= static, parsé au start. Plus rapide. Tags/when propagés.include_*= dynamic, parsé au runtime. Supporteloop:. Tags/when non propagés (saufapply:).import_playbookexiste,include_playbookn'existe pas.- Boucle sur tâches/rôles →
include_*obligatoire. - Default : utiliser
import_*. Basculer surinclude_*uniquement si boucle ou variable runtime. apply:surinclude_taskspropage tags/become/when aux tâches internes.
Mettre en pratique
Section intitulée « Mettre en pratique »La différence entre statique et dynamique ne se voit qu'à l'exécution, sur le comportement des tags. Le lab vous fait charger le même fichier de tâches avec import_tasks puis avec include_tasks, boucler sur le second, et comparer ce que --tags et when: déclenchent dans chaque cas. Le contrôle regarde les fichiers posés, donc les tâches réellement jouées, pas la syntaxe écrite.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Précédence des variables : quelle valeur gagne quand un fichier importé et un
group_varsdéfinissent la même variable. - register et set_fact : les variables créées au runtime, le cas qui impose
include_*plutôt queimport_*. - Boucles avec loop : la mécanique de
loop:, seule compatible avecinclude_tasks.