
Avant Ansible 2.5, on utilisait with_items:, with_dict:, with_subelements: pour les boucles. Depuis 2.5, la forme moderne est loop: couplée à des filtres Jinja2 (dict2items, subelements, product). Cette page explique pourquoi loop: est préféré, comment migrer chaque variante legacy, et le piège de continuer à utiliser with_* dans un projet RHCE 2026 (perte de points).
Les with_* ne sont pas dépréciés au sens strict, ils continuent de fonctionner, mais la doc Ansible et les recommandations RHCE convergent toutes vers loop:.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- L'équivalence entre les
with_*legacy et la forme moderneloop: - La table de migration pour 6 cas courants
- Pourquoi
loop:est plus expressif (combinaison naturelle avec filtres) - L'impact RHCE 2026 (préférence à
loop:)
Prérequis
Section intitulée « Prérequis »- Avoir lu Boucles, loop ;
- Connaître les filtres Jinja2 essentiels (cf. Filtres Jinja2 essentiels).
La table de migration
Section intitulée « La table de migration »| Forme legacy | Forme moderne |
|---|---|
with_items: ma_liste | loop: ma_liste |
with_list: ma_liste | loop: ma_liste (alias) |
with_dict: mon_dict | loop: "{{ mon_dict | dict2items }}" |
with_fileglob: 'files/*.pem' | loop: "{{ query('fileglob', 'files/*.pem') }}" |
with_subelements: [users, ssh_keys] | loop: "{{ users | subelements('ssh_keys') }}" |
with_nested: [list1, list2] | loop: "{{ list1 | product(list2) | list }}" |
with_indexed_items: ma_liste | loop: ma_liste + loop_control: index_var: idx |
with_together: [list1, list2] | loop: "{{ list1 | zip(list2) | list }}" |
with_first_found: liste_paths | loop: "{{ query('first_found', paths) }}" |
with_random_choice: ma_liste | loop: "{{ [my_list | random] }}" (un seul choix) |
Pourquoi migrer
Section intitulée « Pourquoi migrer »Trois raisons concrètes :
-
Lisibilité :
loop:accepte directement des filtres Jinja2. Compare :# ❌ Legacy : trois directives différentes selon le type de sourcewith_items: "{{ users }}"with_dict: "{{ ports }}"with_fileglob: 'files/*.pem'# ✅ Moderne : une seule directive, filtre appropriéloop: "{{ users }}"loop: "{{ ports | dict2items }}"loop: "{{ query('fileglob', 'files/*.pem') }}" -
Cohérence avec
loop_control:: la forme moderne se combine naturellement avecloop_control: label:,index_var:,pause:,extended:. -
Convention RHCE 2026 :
ansible-lintprofil production signale leswith_*comme anti-pattern (use-looprule). Sur l'examen RHCE, l'examinateur attend la forme moderne.
Migration en pratique, with_items
Section intitulée « Migration en pratique, with_items »# Avant- name: Installer ansible.builtin.dnf: name: "{{ item }}" with_items: - nginx - redis
# Après- name: Installer ansible.builtin.dnf: name: "{{ item }}" loop: - nginx - redisIdentique au mot près, juste with_items → loop.
Migration en pratique, with_dict
Section intitulée « Migration en pratique, with_dict »# Avant- name: Lister ansible.builtin.debug: msg: "{{ item.key }}={{ item.value }}" with_dict: nginx: 80 redis: 6379
# Après- name: Lister ansible.builtin.debug: msg: "{{ item.key }}={{ item.value }}" loop: "{{ ports | dict2items }}" vars: ports: nginx: 80 redis: 6379Le filtre dict2items transforme {a:1, b:2} en [{key:a, value:1}, {key:b, value:2}], chaque item est un dict avec .key et .value.
Migration en pratique, with_fileglob
Section intitulée « Migration en pratique, with_fileglob »# Avant- name: Distribuer ansible.builtin.copy: src: "{{ item }}" dest: /etc/ssl/ mode: "0644" with_fileglob: 'files/certs/*.pem'
# Après- name: Distribuer ansible.builtin.copy: src: "{{ item }}" dest: /etc/ssl/ mode: "0644" loop: "{{ query('fileglob', 'files/certs/*.pem') }}"query('fileglob', ...) retourne la liste de fichiers, équivalent direct.
Migration en pratique, with_subelements
Section intitulée « Migration en pratique, with_subelements »# Avant- name: Pour chaque user, déposer chacune de ses clés SSH ansible.posix.authorized_key: user: "{{ item.0.name }}" key: "{{ item.1 }}" with_subelements: - "{{ users }}" - ssh_keys
# Après- name: Pour chaque user, déposer chacune de ses clés SSH ansible.posix.authorized_key: user: "{{ item.0.name }}" key: "{{ item.1 }}" loop: "{{ users | subelements('ssh_keys') }}"Le filtre subelements('attr') sur une liste de dicts éclate la sous-clé en paires (parent, sub).
Cas pratique, migration validée
Section intitulée « Cas pratique, migration validée »Voici l'exemple validé sur le lab ecrire-code-boucles-with-deprecated :
---- name: Challenge migration boucles legacy vers loop hosts: db1.lab become: true
vars: ports: nginx: 80 redis: 6379
tasks: - name: Iteration sur liste avec loop ansible.builtin.copy: dest: "/tmp/withitems-{{ item }}.txt" content: "{{ item }}\n" mode: "0644" loop: - apple - banana - cherry
- name: Iteration sur dict avec loop et dict2items ansible.builtin.copy: dest: "/tmp/withdict-{{ item.key }}.txt" content: "{{ item.value }}\n" mode: "0644" loop: "{{ ports | dict2items }}" loop_control: label: "{{ item.key }}"Aucun with_*, tout passe par loop: et dict2items. Les fichiers
/tmp/withitems-{apple,banana,cherry}.txt et /tmp/withdict-{nginx,redis}.txt
sont produits exactement comme avec les boucles legacy.
Que reste-t-il aux with_* ?
Section intitulée « Que reste-t-il aux with_* ? »Une seule justification fragile : lisibilité courte. with_items: ma_liste est un caractère plus court que loop: ma_liste. Mais ce gain est annulé dès qu'on ajoute loop_control:.
Recommandation : pas de nouveau code en with_* en 2026. Pour le code existant, migrer progressivement via ansible-lint --fix (qui propose la conversion automatique).
Pièges fréquents
Section intitulée « Pièges fréquents »| Symptôme | Cause | Fix |
|---|---|---|
with_dict migré en loop: my_dict (sans dict2items) | Oubli du filtre | loop: "{{ my_dict | dict2items }}" |
loop: itère sur une string caractère par caractère | La variable est une string, pas une liste | split() ou wantlist=true selon contexte |
with_subelements migré incorrectement | Différence de structure | Tester avec debug: avant de remplacer |
loop: plus lent que with_items | Idem, le coût est le même | Aucun fix nécessaire |
ansible-lint signale use-loop | Code legacy | Migrer ; ansible-lint --fix peut aider |
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
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
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
À retenir
Section intitulée « À retenir »loop:remplace tous leswith_*depuis Ansible 2.5, convention RHCE 2026.- Pour un dict, ajouter le filtre
dict2items:loop: "{{ ports \| dict2items }}". - Pour un fileglob, utiliser
query('fileglob', ...):loop: "{{ query('fileglob', 'files/*.pem') }}". ansible-lint --fixpropose la migration automatique sur du code existant.- Aucune raison technique de garder les
with_*en nouveau code en 2026.
Mettre en pratique
Section intitulée « Mettre en pratique »Migrer du code existant est plus formateur que lire une table de correspondance. Le lab vous fait repérer with_items, with_dict et with_subelements dans un playbook hérité, puis les réécrire en loop: avec les filtres Jinja2 correspondants. Vous terminez en laissant ansible-lint --fix faire la conversion mécanique, pour mesurer ce qu'il traite seul et ce qu'il vous laisse.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Templates Jinja2 : la vue d'ensemble de la syntaxe Jinja2 que la migration vers
loop:mobilise. - Filtres Jinja2 avancés : le catalogue des filtres qui remplacent
with_dict,with_subelementset leurs voisins. - Quiz Écrire du code : 30 questions pour vérifier que la forme moderne des boucles est acquise.