Aller au contenu

Maitriser les Boucles Ansible

Mise à jour :

logo

Les boucles dans Ansible permettent d’automatiser l’exécution répétée d’une tâche, avec des valeurs différentes à chaque itération. Que ce soit pour installer plusieurs paquets, créer des utilisateurs ou configurer plusieurs services, les boucles rendent vos playbooks Ansible plus lisibles, maintenables et efficaces. Ce guide vous montre comment les utiliser, du simple loop à des constructions plus avancées comme les boucles imbriquées, les dictionnaires, et les contrôles avec loop_control.

Boucles de base : loop

La boucle loop est la syntaxe moderne recommandée par Ansible pour répéter une tâche sur une liste d’éléments. Elle remplace progressivement les anciennes directives comme with_items. Cependant, il existe des cas spécifiques où l’usage de loop est déconseillé, notamment avec certains modules acceptant déjà des listes.

Traitement de liste simple

Voici un exemple typique pour créer plusieurs utilisateurs :

- name: Créer des utilisateurs
ansible.builtin.user:
name: "{{ item }}"
state: present
loop:
- alice
- bob
- carol

Chaque itération passe une valeur à la variable spéciale item. Le module user est alors exécuté pour chaque utilisateur.

loop vs with_items

L’ancienne syntaxe with_items reste fonctionnelle mais est moins lisible et moins flexible :

- name: Créer des utilisateurs (ancienne méthode)
ansible.builtin.user:
name: "{{ item }}"
state: present
with_items:
- alice
- bob
- carol

Préférez loop pour les playbooks récents. Elle est plus cohérente avec les autres boucles (loop_control, until, etc.).

Utiliser des listes complexes

Vous pouvez aussi itérer sur des listes de dictionnaires pour passer plusieurs arguments :

- name: Créer plusieurs groupes avec GID
ansible.builtin.group:
name: "{{ item.name }}"
gid: "{{ item.gid }}"
loop:
- { name: 'dev', gid: 2001 }
- { name: 'ops', gid: 2002 }

Ce format est utile pour passer plusieurs variables à chaque itération de la boucle.

Recommandations d’utilisation

Certains modules comme apt, yum ou package acceptent une liste directement dans leur paramètre. Inutile (et incorrect) d’utiliser loop dans ces cas :

# ❌ Mauvais usage avec loop
- name: Installer des paquets
ansible.builtin.apt:
name: "{{ item }}"
state: present
loop:
- htop
- curl
# ✅ Bon usage avec une liste directe
- name: Installer plusieurs paquets
ansible.builtin.apt:
name:
- htop
- curl
state: present

Utiliser loop ici entraîne une exécution répétée du module, ce qui peut ralentir les déploiements ou causer des effets indésirables.

Boucles sur dictionnaires et boucles imbriquées

Ansible permet également d’itérer sur des structures plus complexes, comme des dictionnaires ou des listes de listes. Cela permet de manipuler des données hiérarchisées ou de coupler plusieurs paramètres dans une même boucle.

Boucler sur un dictionnaire

Les dictionnaires (ou hashmaps) doivent être transformés en une liste de paires clé-valeur pour pouvoir être parcourus avec loop. Cela se fait grâce au filtre dict2items.

vars:
utilisateurs:
alice: "/home/alice"
bob: "/home/bob"
tasks:
- name: Créer les répertoires des utilisateurs
ansible.builtin.file:
path: "{{ item.value }}"
state: directory
owner: "{{ item.key }}"
loop: "{{ utilisateurs | dict2items }}"

Ici, item.key contient le nom de l’utilisateur et item.value son répertoire. Cette méthode est idéale pour mapper une clé à une propriété spécifique dans une tâche.

Boucles imbriquées (nested loops)

Lorsque vous devez combiner deux listes, par exemple, affecter plusieurs rôles à chaque serveur, vous pouvez utiliser des boucles imbriquées.

vars:
serveurs:
- web1
- db1
roles:
- nginx
- firewalld
tasks:
- name: Affecter chaque rôle à chaque serveur
ansible.builtin.debug:
msg: "Serveur {{ item[0] }} reçoit le rôle {{ item[1] }}"
loop: "{{ serveurs | product(roles) | list }}"

Cette syntaxe utilise le filtre product de Jinja2 pour effectuer un produit cartésien des deux listes. Chaque item devient une liste de deux éléments ([serveur, rôle]).

Boucle imbriquée avec sous-liste

Il est également possible d’imbriquer une boucle dans une autre à l’aide de la directive include_tasks et d’une variable passée à la tâche incluse :

tasks:
- name: Boucle principale
include_tasks: sous_tache.yml
loop:
- app1
- app2
loop_control:
loop_var: app_name

Fichier sous_tache.yml :

- name: Tâche pour {{ app_name }}
ansible.builtin.debug:
msg: "Déploiement de {{ app_name }}"

Cette méthode est utile pour structurer proprement vos boucles complexes dans de grands playbooks.

Boucles conditionnelles et loop_control

Les boucles dans Ansible peuvent être combinées avec des conditions d’exécution et des options de contrôle d’affichage pour offrir un comportement plus fin et plus lisible. C’est particulièrement utile quand tous les éléments ne doivent pas être traités ou que vous voulez mieux suivre l’exécution de vos boucles.

Appliquer une condition dans une boucle

La directive when permet de filtrer les éléments pendant la boucle. Elle s’exécute à chaque itération et peut utiliser item pour tester une valeur.

- name: Ajouter des utilisateurs sauf 'admin'
ansible.builtin.user:
name: "{{ item }}"
state: present
loop:
- alice
- bob
- admin
when: item != 'admin'

Dans cet exemple, le module user sera exécuté uniquement pour alice et bob.

Vous pouvez aussi filtrer selon une propriété spécifique dans une liste de dictionnaires :

- name: Créer les comptes actifs uniquement
ansible.builtin.user:
name: "{{ item.name }}"
state: present
loop:
- { name: 'alice', actif: true }
- { name: 'bob', actif: false }
when: item.actif

Personnaliser l’affichage avec loop_control

La directive loop_control vous donne un contrôle plus précis sur les variables internes et l’affichage lors de l’exécution :

- name: Affichage avec étiquette personnalisée
ansible.builtin.debug:
msg: "Installation de {{ item.pkg }}"
loop:
- { pkg: 'htop', id: 1 }
- { pkg: 'curl', id: 2 }
loop_control:
label: "{{ item.pkg }}"

Ici, la sortie affichera Installation de htop plutôt que tout le dictionnaire JSON.

Vous pouvez également suivre l’indice de boucle avec index_var :

- name: Boucle avec index
ansible.builtin.debug:
msg: "Itération {{ index }} : {{ item }}"
loop:
- alpha
- beta
loop_control:
index_var: index

Cela permet d’identifier rapidement à quelle étape du traitement on se trouve, ce qui est très utile pour le debug.

Combiner loop, when et loop_control

Les trois peuvent être utilisés ensemble pour des boucles puissantes et bien lisibles :

- name: Redémarrer les services actifs uniquement
ansible.builtin.service:
name: "{{ item.name }}"
state: restarted
loop:
- { name: 'nginx', actif: true }
- { name: 'mysql', actif: false }
when: item.actif
loop_control:
label: "{{ item.name }}"

Dans cet exemple, seuls les services actifs seront redémarrés, et le nom du service sera affiché dans les logs.

Boucles avec until et retry

Ansible offre un mécanisme de répétition conditionnelle avec until, permettant de réessayer une tâche jusqu’à ce qu’une condition soit remplie. Contrairement à loop, qui itère sur une liste, until répète la même tâche jusqu’à obtenir un résultat attendu. C’est très utile pour les vérifications de services ou les appels à des API instables.

Syntaxe de base avec until

Voici un exemple de tâche qui vérifie qu’un fichier existe, avec répétition toutes les 5 secondes pendant 30 secondes maximum :

- name: Attendre qu’un fichier soit créé
ansible.builtin.stat:
path: /tmp/mon_fichier
register: fichier
until: fichier.stat.exists
retries: 6
delay: 5

Cette tâche :

  • S’exécute jusqu’à ce que fichier.stat.exists soit vrai,
  • Fait 6 tentatives avec 5 secondes d’intervalle,
  • Soit un total de 30 secondes maximum.

Exemple avec un service en attente

Le cas typique : attendre que MySQL réponde sur le port 3306 avant de lancer les migrations.

- name: Attendre que MySQL réponde
ansible.builtin.wait_for:
host: 127.0.0.1
port: 3306
state: started
register: result
until: result is succeeded
retries: 10
delay: 3

Ce mécanisme permet d’éviter les erreurs liées à des dépendances non encore disponibles. Cela rend vos playbooks plus robustes et moins sensibles aux délais de démarrage.

until combiné avec des boucles

Il est tout à fait possible de combiner loop et until, même si la logique devient plus complexe. Par exemple, pour vérifier la présence de plusieurs fichiers :

- name: Vérifier plusieurs fichiers
ansible.builtin.stat:
path: "{{ item }}"
register: res
until: res.stat.exists
retries: 5
delay: 2
loop:
- /tmp/fichier1
- /tmp/fichier2

Chaque fichier est vérifié individuellement, et chaque itération bénéficie d’une répétition autonome avec until.

Bonnes pratiques avec until

  • N’abusez pas des répétitions : limitez le nombre de retries pour éviter des déploiements trop longs.
  • Utilisez toujours register pour stocker la sortie.
  • Combinez until avec debug pour comprendre les blocages.

Exercices pratiques : Maîtriser les boucles Ansible

Vous avez désormais toutes les clés pour écrire des boucles efficaces avec Ansible : de la simple itération à la combinaison loop + when, sans oublier les structures complexes comme les dictionnaires et les boucles imbriquées. Passons maintenant à la pratique, avec une série de travaux concrets pour créer des tâches dynamiques, conditionnelles et lisibles.

👉 Travaux pratiques : TP 10 : Boucles Ansible

Ces exercices couvrent notamment

  • L’écriture de playbooks utilisant loop pour créer des utilisateurs, des groupes ou des fichiers personnalisés.
  • La transformation d’un dictionnaire avec dict2items pour générer des ressources conditionnelles.
  • L’utilisation de loop_control pour nommer les boucles, suivre les indices, et faciliter le debug.
  • L’intégration de until dans une tâche qui attend un état cible (ex. : fichier présent, service disponible).

Pourquoi pratiquer ?

  • Savoir distinguer quand utiliser une liste directe (par exemple pour apt) ou une boucle explicite.
  • Optimiser vos playbooks Ansible en réduisant les tâches redondantes et en améliorant leur lisibilité.
  • Créer des structures dynamiques capables de s’adapter à des données complexes (nested loops, conditions).
  • Déboguer une boucle efficacement en exploitant les variables internes (item, index_var, label, etc.).

Clonez le dépôt ou suivez les consignes directement dans les fichiers. Grâce à ces exercices ciblés, vous saurez écrire des boucles fiables, maintenables et adaptées à chaque contexte d’automatisation.

Contrôle de connaissances

Pourquoi ce contrôle ?

Cet contrôle va vous permettre de valider vos connaissances sur le sujet abordé dans le guide. Il comporte des QCM, des questions vrai/faux et des réponses ouvertes à un mot.

🕒 Le chronomètre commence dès que vous cliquez sur Démarrer le test. Vous devrez terminer l’examen avant la fin du temps imparti.

🎯 Pour réussir, vous devez obtenir au moins 80% de bonnes réponses.

💡 Je ne fournis pas directement les réponses aux questions. Cependant, si certaines sont complexes, des pistes d’explication pourront être proposées dans le guide ou après l’examen.

Bonne chance ! 🚀

Conclusion

Vous avez maintenant une compréhension solide des boucles dans Ansible, de l’utilisation de loop pour itérer sur des listes simples, à la manipulation de dictionnaires et de boucles imbriquées. Vous savez comment appliquer des conditions avec when, personnaliser l’affichage avec loop_control, et gérer les répétitions conditionnelles avec until. Nous verrons dans un prochain chapitre d’autres types de boucles.