Aller au contenu
Infrastructure as Code medium

Boucles Ansible (loop) : itérer sur listes, dicts et résultats de lookup

14 min de lecture

Logo Ansible

loop: est la directive moderne Ansible (2.5+) pour itérer une tâche sur une liste, un dict ou un résultat de lookup. Elle remplace les anciens with_items:, with_dict:, with_subelements: (page suivante : Boucles legacy with_*). Cette page couvre la syntaxe loop:, l’option loop_control: (label, index_var, pause), la combinaison avec when:, et les patterns courants : itérer sur une liste de dicts, sur un dict, ou sur le résultat d’un lookup('fileglob', ...).

C’est la directive la plus utilisée pour paramétrer un playbook : un seul module qui crée 10 users en bouclant sur une liste, plutôt que 10 tâches dupliquées.

  • La syntaxe loop: <liste> et la variable item
  • loop_control: : label, index_var, pause, extended
  • Itérer sur une liste de dicts : loop: + item.<key>
  • Itérer sur un dict via dict2items
  • Combiner loop: + when: : filtrage par item
  • register: sur une boucle : récupérer la liste des résultats
- name: Installer plusieurs paquets
ansible.builtin.dnf:
name: "{{ item }}"
state: present
loop:
- nginx
- redis
- postgresql

À chaque itération, item vaut un élément de la liste. La tâche est exécutée 3 fois.

Le pattern le plus courant en Ansible. Chaque item est un dict, on accède aux clés via item.<key> :

vars:
users:
- { name: alice, shell: /bin/bash, enabled: true }
- { name: bob, shell: /bin/zsh, enabled: false }
- { name: charlie, shell: /bin/bash, enabled: true }
tasks:
- name: Créer les users
ansible.builtin.user:
name: "{{ item.name }}"
shell: "{{ item.shell }}"
state: present
loop: "{{ users }}"
loop_control:
label: "{{ item.name }}"
when: item.enabled

Sortie console :

TASK [Créer les users]
skipping: [db1.lab] => (item=bob)
changed: [db1.lab] => (item=alice)
changed: [db1.lab] => (item=charlie)

Sans loop_control.label:, Ansible afficherait le dict complet à chaque itération — illisible.

OptionEffet
label: "<expr>"Affichage console (au lieu du dict complet)
index_var: idxVariable contenant l’index courant (0-based)
pause: <secondes>Pause entre chaque itération (utile pour API rate-limited)
extended: trueActive ansible_loop.first, ansible_loop.last, ansible_loop.index
- name: Démo loop_control complet
ansible.builtin.debug:
msg: "Item {{ idx }} sur {{ users | length }} : {{ item.name }} (last={{ ansible_loop.last }})"
loop: "{{ users }}"
loop_control:
label: "{{ item.name }}"
index_var: idx
pause: 1
extended: true
vars:
ports:
nginx: 80
redis: 6379
postgresql: 5432
tasks:
- name: Lister les ports
ansible.builtin.debug:
msg: "{{ item.key }} : {{ item.value }}"
loop: "{{ ports | dict2items }}"
loop_control:
label: "{{ item.key }}"

dict2items transforme {a: 1, b: 2} en [{key: a, value: 1}, {key: b, value: 2}]. À l’inverse, items2dict reconstruit un dict.

- name: Distribuer tous les certificats locaux
ansible.builtin.copy:
src: "{{ item }}"
dest: "/etc/ssl/{{ item | basename }}"
loop: "{{ query('fileglob', 'files/certs/*.pem') }}"

query('fileglob', ...) retourne une liste de chemins matchant le glob côté control node. Préfère query à lookup quand on itère (query retourne toujours une liste).

when: est évalué à chaque itération :

- name: Installer uniquement les services activés
ansible.builtin.dnf:
name: "{{ item.name }}"
state: present
loop: "{{ services }}"
when: item.enabled

Pour les itérations où when: est faux, la tâche est marquée skipped (pas failed).

Quand vous capturez la sortie d’une tâche dans une boucle, le résultat est une liste :

- name: Vérifier des URLs en boucle
ansible.builtin.uri:
url: "https://{{ item }}/health"
status_code: 200
loop:
- api.example.com
- app.example.com
register: health_results
- name: Afficher tous les status
ansible.builtin.debug:
msg: "{{ item.item }} → {{ item.status }}"
loop: "{{ health_results.results }}"

Le résultat est dans health_results.results (liste), chaque entrée contient l’item original + les champs de la tâche (status, content, etc.).

Cas pratique — créer des users avec filtrage (lab ecrire-code/boucles-loop)

Section intitulée « Cas pratique — créer des users avec filtrage (lab ecrire-code/boucles-loop) »

Voici l’exemple validé sur le lab 20-ecrire-code-boucles-loop :

---
- name: Challenge boucles-loop
hosts: db1.lab
become: true
vars:
challenge_users:
- { name: chal_alice, shell: /bin/bash, enabled: true }
- { name: chal_bob, shell: /bin/zsh, enabled: false }
- { name: chal_charlie, shell: /bin/bash, enabled: true }
tasks:
- name: Créer les users actifs
ansible.builtin.user:
name: "{{ item.name }}"
shell: "{{ item.shell }}"
state: present
loop: "{{ challenge_users }}"
loop_control:
label: "{{ item.name }}"
when: item.enabled
- name: Poser le récapitulatif
ansible.builtin.copy:
dest: /tmp/loop-result.txt
content: "{{ challenge_users | selectattr('enabled') | map(attribute='name') | sort | join(',') }}\n"
mode: "0644"

alice et charlie créés, bob skippé. Le récapitulatif loop-result.txt est généré côté Jinja sans loop (via selectattr | map | sort | join).

SymptômeCauseFix
Loop affiche le dict complet à chaque itérationPas de loop_control: label:Toujours ajouter label: "&#123;&#123; item.name &#125;&#125;"
'dict object' has no attribute 'foo'La clé n’existe pas dans certains items`item.foo
Loop trop lentModule qui supporte une liste mais on boucle quand mêmePasser la liste directement au module (name: ['a', 'b'])
register avec loop renvoie un dict pas une listeC’est une liste sous register_var.resultsUtiliser register_var.results, pas register_var directement
loop: ne propage pas les handlersC’est par design (un handler est dédupliqué)Pas de fix
loop: avec un pause: ralentit le playpause: est volontaire (rate limit API)Réduire la valeur ou retirer le pause:
  • loop: <liste> + item = boucle moderne (Ansible 2.5+). with_* est legacy.
  • loop_control: label: "{{ item.name }}" rend la sortie console lisible.
  • Pour itérer sur un dict, utiliser {{ ports | dict2items }} (key/value).
  • register: sur une boucle stocke la liste dans var.results.
  • Si le module supporte une liste (dnf, apt), passer la liste directement plutôt que boucler — plus rapide.
  • when: est évalué à chaque itération : utile pour filtrer par item.

Cette page a un lab d’accompagnement : labs/ecrire-code/boucles-loop/ dans stephrobert/ansible-training. Il contient un README.md guidé, un Makefile (make verify lance les tests), et un challenge final auto-évalué : créer 3 users via loop: + when: + loop_control: label, dont 2 actifs et 1 désactivé.

Une fois le lab provisionné :

Fenêtre de terminal
cd ~/Projets/ansible-training/labs/ecrire-code/boucles-loop/
cat README.md # tuto pas à pas
cat challenge/README.md # consigne du challenge final
pytest -v challenge/tests/ # lancer les tests testinfra

Si les tests passent, vous maîtrisez les concepts couverts dans ce guide. En cas de blocage, docs/troubleshooting.md à la racine du repo couvre les pièges fréquents (rate-limit SSH, clé absente, collection manquante).

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn