Aller au contenu
Infrastructure as Code medium

Maitriser les Boucles Ansible

15 min de lecture

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.

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.

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.

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.).

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.

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.

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.

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.

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]).

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.

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.

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

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.

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.

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.

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.

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.

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.

  • 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.

Cet exemple permettra de comprendre le fonctionnement de ce type de boucle :

vars:
keys:
- key1
- key2
- key3
tasks:
- name: Distribute SSH keys among multiple users
ansible.builtin.lineinfile:
dest: /home/{{ item[0] }}/.ssh/authorized_keys
line: {{ item[1] }}
state: present
with_nested:
- [ ’calvin’, ’josh’, ’alice’ ]
- ’{{ keys }}’

Ici Ansible va boucler sur chaque utilisateur et remplira leur fichier authorized_keys avec les 3 clés définies dans la liste.

Les boucles sur dictionnaires : En terminologie Python, un dictionnaire est un ensemble défini de variables possédant plusieurs valeurs :

vars:
users:
alice:
name: Alice
telephone: 123-456-7890
bob:
name: Bob
telephone: 987-654-3210
tasks:
- name: Print phone records
ansible.builtin.debug:
msg="User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"
with_dict: "{{users}}"

Il existe toute une série de type de boucle répondant à des besoins spécifiques. Si vous avez des choses complexes à construire n’hésitez pas à jeter un œil à cette section de la documentation Ansible.

Exercices pratiques : Maîtriser les boucles Ansible

Section intitulée « 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

  • 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).
  • 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

Validez vos connaissances avec ce quiz interactif

7 questions
5 minutes
Seuil : 80%

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

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.