4.4 : Boucles : loop

Imaginez devoir installer cinq paquets différents. Sans boucle, il faudrait écrire cinq tâches quasiment identiques, en changeant juste le nom du paquet à chaque fois. C’est exactement le genre de répétition qu’une chaîne de montage (souvenez-vous de la formation PowerShell) est censée éliminer. C’est le rôle de loop.

Le principe de base

- name: Installer plusieurs paquets
  ansible.builtin.apt:
    name: "{{ item }}"
    state: present
  loop:
    - curl
    - git
    - htop
    - vim

Ansible exécute cette tâche une fois par élément de la liste, en remplaçant {{ item }} par la valeur en cours. Résultat à l’exécution :

TASK [Installer plusieurs paquets] **********************
changed: [nrd-web1] => (item=curl)
changed: [nrd-web1] => (item=git)
changed: [nrd-web1] => (item=htop)
ok: [nrd-web1] => (item=vim)

Chaque élément est traité indépendamment — et vous remarquez que vim affiche ok plutôt que changed : il était déjà installé. L’idempotence s’applique élément par élément, pas seulement à la tâche entière.

Boucler sur une liste de dictionnaires

Très utile dès que chaque élément a besoin de plusieurs informations, pas juste un nom :

- name: Créer plusieurs utilisateurs avec des groupes différents
  ansible.builtin.user:
    name: "{{ item.nom }}"
    groups: "{{ item.groupe }}"
    state: present
  loop:
    - { nom: "alice", groupe: "webservers" }
    - { nom: "bob", groupe: "databases" }

On accède à chaque propriété avec item.nom, item.groupe — on retrouvera ce pattern exact dans le TP interactif de la section 7, pour provisionner plusieurs comptes en une seule tâche.

Boucler sur une variable plutôt qu’une liste écrite en dur

Dans la vraie vie, la liste ne sera presque jamais écrite directement dans la tâche : elle viendra d’une variable, définie dans l’inventaire ou dans un group_vars/host_vars (comme les docker_stacks vus en section 2) :

vars:
  paquets_a_installer:
    - curl
    - git
    - htop

tasks:
  - name: Installer les paquets définis en variable
    ansible.builtin.apt:
      name: "{{ item }}"
      state: present
    loop: "{{ paquets_a_installer }}"

C’est exactement cette mécanique qu’on retrouvera dans l’étude de cas de fin de formation (le playbook de maintenance Docker), où docker_stacks est parcouru avec loop pour traiter chaque stack une par une.

loop_control : des logs plus lisibles

Sur une boucle avec des dictionnaires un peu complexes, l’affichage par défaut (item={ "nom": "alice", "groupe": "webservers" }) devient vite illisible. loop_control.label permet de choisir ce qui s’affiche à la place :

- name: Créer plusieurs utilisateurs
  ansible.builtin.user:
    name: "{{ item.nom }}"
    groups: "{{ item.groupe }}"
  loop:
    - { nom: "alice", groupe: "webservers" }
    - { nom: "bob", groupe: "databases" }
  loop_control:
    label: "{{ item.nom }}"
changed: [nrd-web1] => (item=alice)
changed: [nrd-web1] => (item=bob)

Bien plus lisible qu’un dictionnaire complet affiché à chaque ligne — un réflexe qu’on retrouvera aussi dans l’étude de cas de fin de formation.

Ce qu’il faut retenir

  • loop répète une tâche pour chaque élément d’une liste, accessible via {{ item }}.
  • Une liste de dictionnaires permet de faire varier plusieurs paramètres à la fois (item.propriete).
  • L’idempotence s’applique indépendamment à chaque élément de la boucle.
  • loop_control.label garde les logs lisibles dès que les éléments deviennent des structures un peu complexes.