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 tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn