Ansible propose la loop, with_et until pour exécuter une tâche plusieurs fois. Des exemples de boucles couramment utilisées incluent le changement de propriété sur plusieurs fichiers et/ou répertoires avec le mot-clé module de fichier, la création de plusieurs utilisateurs avec le module module utilisateur, et la répétition d'une étape de sondage jusqu'à ce qu'un certain résultat soit atteint.

Note

  • Nous avons ajouté loop dans Ansible 2.5. Ce n'est pas encore un remplacement complet de with_, mais nous le recommandons pour la plupart des cas d'utilisation.
  • Nous n'avons pas déprécié l'utilisation de with_ - cette syntaxe sera encore valable dans un avenir prévisible.
  • Nous cherchons à améliorer loop syntaxe - surveillez cette page et le changelog pour les mises à jour.
  • Comparaison de loop et with_*
  • Boucles standard

    • Itération sur une liste simple
    • Itération sur une liste de hachages
    • Itération sur un dictionnaire
  • Enregistrement de variables avec une boucle
  • Boucles complexes

    • Itération sur des listes imbriquées
    • Réessayer une tâche jusqu'à ce qu'une condition soit remplie
    • Boucle sur l'inventaire
  • Assurer la saisie de la liste pour loop: utiliser query plutôt que lookup
  • Ajouter des contrôles aux boucles

    • Limiter la sortie de la boucle avec label
    • Mettre en pause à l'intérieur d'une boucle
    • Suivi de la progression dans une boucle avec index_var
    • Définition des noms de variables internes et externes avec loop_var
    • Variables de boucle étendues
    • Accéder au nom de votre loop_var
  • Migration de with_X vers loop

    • avec_liste
    • avec_items
    • avec_éléments_indexés
    • avec_flatté
    • avec_together
    • avec_dict
    • avec_séquence
    • avec_subelements
    • avec_nested/with_cartesian
    • avec_choix_aléatoire

Comparaison de loop et with_*

  • Le site with_ s'appuient sur les mots-clés Plugins de recherche - même items est une consultation.
  • Le site loop est équivalent à with_list, et constitue le meilleur choix pour les boucles simples.
  • Le site loop n'acceptera pas une chaîne de caractères en entrée, cf. Assurer l'entrée de liste pour la boucle : utiliser la requête plutôt que le lookup..
  • D'une manière générale, toute utilisation de with_* couverte par Migration de with_X vers loop peut être mis à jour pour utiliser loop.
  • Soyez prudent lorsque vous modifiez with_items pour loopcar with_items a effectué un aplatissement implicite à un seul niveau. Vous pouvez avoir besoin d'utiliser flatten(1) avec loop pour correspondre au résultat exact. Par exemple, pour obtenir le même résultat que :
with_items:-1-[2,3]-4

vous auriez besoin de :

loop:"{{ [1, [2,3] ,4] | flatten(1) }}"
  • Tout with_* qui nécessite l'utilisation de lookup à l'intérieur d'une boucle ne doit pas être convertie pour utiliser la fonction loop pour utiliser le mot-clé loop . Par exemple, au lieu de faire :
loop:"{{ lookup('fileglob', '*.txt', wantlist=True) }}"

il est plus propre de garder :

with_fileglob:'*.txt'

Boucles standard

Itération sur une liste simple

Les tâches répétées peuvent être écrites comme des boucles standard sur une simple liste de chaînes de caractères. Vous pouvez définir la liste directement dans la tâche :

-name: Add several users
  ansible.builtin.user:name:"{{ item }}"state: present
    groups:"wheel"loop:- testuser1
     - testuser2

Vous pouvez définir la liste dans un fichier de variables, ou dans la section 'vars' de votre jeu, puis faire référence au nom de la liste dans la tâche :

loop:"{{ somelist }}"

L'un ou l'autre de ces exemples serait l'équivalent de :

-name: Add user testuser1
  ansible.builtin.user:name:"testuser1"state: present
    groups:"wheel"-name: Add user testuser2
  ansible.builtin.user:name:"testuser2"state: present
    groups:"wheel"

Vous pouvez passer une liste directement en paramètre pour certains plugins. La plupart des modules de conditionnement, comme yum et apt ont cette capacité. Lorsqu'elle est disponible, il est préférable de passer la liste en paramètre plutôt que de boucler sur la tâche. Par exemple :

-name: Optimal yum
  ansible.builtin.yum:name:"{{  list_of_packages  }}"state: present

-name: Non-optimal yum, slower and may cause issues with interdependencies
  ansible.builtin.yum:name:"{{  item  }}"state: present
  loop:"{{  list_of_packages  }}"

Vérifiez le documentation du module pour voir si vous pouvez passer une liste au(x) paramètre(s) d'un module particulier.

Itération sur une liste de hachages

Si vous avez une liste de hachages, vous pouvez référencer des sous-clés dans une boucle. Par exemple :

-name: Add several users
  ansible.builtin.user:name:"{{ item.name }}"state: present
    groups:"{{ item.groups }}"loop:-{name:'testuser1',groups:'wheel'}-{name:'testuser2',groups:'root'}

Lorsque vous combinez conditionnelles avec une boucle, le when: est traitée séparément pour chaque élément. Voir Conditionnelles de base avec when pour des exemples.

Itération sur un dictionnaire

Pour boucler sur un dict, utilisez la fonction dict2items:

-name: Using dict2items
  ansible.builtin.debug:msg:"{{ item.key }} - {{ item.value }}"loop:"{{ tag_data | dict2items }}"vars:tag_data:Environment: dev
      Application: payment

Ici, nous itérons sur tag_data et on imprime la clé et la valeur de celle-ci.

Enregistrement de variables avec une boucle

Vous pouvez enregistrer la sortie d'une boucle comme une variable. Par exemple :

-name: Register loop output as a variable
  ansible.builtin.shell:"echo {{ item }}"loop:-"one"-"two"register: echo

Lorsque vous utilisez register avec une boucle, la structure de données placée dans la variable contiendra une valeur de results qui est une liste de toutes les réponses du module. Cela diffère de la structure de données retournée lors de l'utilisation de la fonction register sans boucle :

{"changed":true,"msg":"All items completed","results":[{"changed":true,"cmd":"echo "one" ","delta":"0:00:00.003110","end":"2013-12-19 12:00:05.187153","invocation":{"module_args":"echo "one"","module_name":"shell"},"item":"one","rc":0,"start":"2013-12-19 12:00:05.184043","stderr":"","stdout":"one"},{"changed":true,"cmd":"echo "two" ","delta":"0:00:00.002920","end":"2013-12-19 12:00:05.245502","invocation":{"module_args":"echo "two"","module_name":"shell"},"item":"two","rc":0,"start":"2013-12-19 12:00:05.242582","stderr":"","stdout":"two"}]}

Les boucles ultérieures sur la variable enregistrée pour inspecter les résultats peuvent se présenter comme suit :

-name: Fail if return code is not 0
  ansible.builtin.fail:msg:"The command ({{ item.cmd }}) did not have a 0 return code"when: item.rc != 0
  loop:"{{ echo.results }}"

Pendant l'itération, le résultat de l'élément actuel sera placé dans la variable :

-name: Place the result of the current item in the variable
  ansible.builtin.shell: echo "{{ item }}"
  loop:- one
    - two
  register: echo
  changed_when: echo.stdout != "one"

Boucles complexes

Itération sur des listes imbriquées

Vous pouvez utiliser des expressions Jinja2 pour itérer sur des listes complexes. Par exemple, une boucle peut combiner des listes imbriquées :

-name: Give users access to multiple databases
  community.mysql.mysql_user:name:"{{ item[0] }}"priv:"{{ item[1] }}.*:ALL"append_privs: yes
    password:"foo"loop:"{{ ['alice', 'bob'] |product(['clientdb', 'employeedb', 'providerdb'])|list }}"

Réessayer une tâche jusqu'à ce qu'une condition soit remplie

Nouveau dans la version 1.4.

Vous pouvez utiliser le until pour réessayer une tâche jusqu'à ce qu'une certaine condition soit remplie. Voici un exemple :

-name: Retry a task until a certain condition is met
  ansible.builtin.shell: /usr/bin/foo
  register: result
  until: result.stdout.find("all systems go") !=-1retries:5delay:10

Cette tâche s'exécute jusqu'à 5 fois avec un délai de 10 secondes entre chaque tentative. Si le résultat de toute tentative a "all systems go" dans son stdout, la tâche réussit. La valeur par défaut pour "retries" est 3 et "delay" est 5.

Pour voir les résultats des tentatives individuelles, exécutez la pièce avec . -vv.

Lorsque vous exécutez une tâche avec until et que vous enregistrez le résultat en tant que variable, la variable enregistrée comprendra une clé appelée "tentatives", qui enregistre le nombre de tentatives pour la tâche.

Note

Vous devez définir la variable until si vous voulez qu'une tâche fasse une nouvelle tentative. Si until n'est pas défini, la valeur du paramètre retries est forcée à 1.

Boucle sur l'inventaire

Pour boucler sur votre inventaire, ou seulement sur un sous-ensemble de celui-ci, vous pouvez utiliser une commande régulière. loopavec le ansible_play_batch ou groups variables :

-name: Show all the hosts in the inventory
  ansible.builtin.debug:msg:"{{ item }}"loop:"{{ groups['all'] }}"-name: Show all the hosts in the current play
  ansible.builtin.debug:msg:"{{ item }}"loop:"{{ ansible_play_batch }}"

Il existe également un plugin de recherche spécifique inventory_hostnames qui peut être utilisé comme ceci :

-name: Show all the hosts in the inventory
  ansible.builtin.debug:msg:"{{ item }}"loop:"{{ query('inventory_hostnames', 'all') }}"-name: Show all the hosts matching the pattern, ie all but the group www
  ansible.builtin.debug:msg:"{{ item }}"loop:"{{ query('inventory_hostnames', 'all:!www') }}"

Plus d'informations sur les modèles peuvent être trouvées dans Patrons : ciblage des hôtes et des groupes.

Assurer la saisie de la liste pour loop : utiliser query plutôt que lookup

Le site loop requiert une liste en entrée, mais le mot-clé lookup renvoie une chaîne de valeurs séparées par des virgules par défaut. Ansible 2.5 a introduit une nouvelle fonction Jinja2 nommée requête qui renvoie toujours une liste, offrant une interface plus simple et une sortie plus prévisible des plugins de consultation lors de l'utilisation du mot-clé loop mot-clé.

Vous pouvez forcer lookup à retourner une liste à loopen utilisant wantlist=Trueou vous pouvez utiliser query à la place.

Ces exemples font la même chose :

loop:"{{ query('inventory_hostnames', 'all') }}"loop:"{{ lookup('inventory_hostnames', 'all', wantlist=True) }}"

Ajout de contrôles aux boucles

Nouveau dans la version 2.1.

Le site loop_control vous permet de gérer vos boucles de manière utile.

Limiter la sortie de la boucle avec label

Nouveau dans la version 2.2.

Lorsque vous bouclez sur des structures de données complexes, la sortie console de votre tâche peut être énorme. Pour limiter la sortie affichée, utilisez l'option label avec loop_control:

-name: Create servers
  digital_ocean:name:"{{ item.name }}"state: present
  loop:-name: server1
      disks: 3gb
      ram: 15Gb
      network:nic01: 100Gb
        nic02: 10Gb
        ...loop_control:label:"{{ item.name }}"

La sortie de cette tâche affichera seulement la directive name pour chaque item au lieu du contenu entier de la ligne multiple {{ item }} multi-lignes.

Note

Ceci est pour rendre la sortie de la console plus lisible, pas pour protéger les données sensibles. S'il y a des données sensibles dans loop , mettez no_log: yes sur la tâche pour empêcher la divulgation.

Mise en pause à l'intérieur d'une boucle

Nouveau dans la version 2.2.

Pour contrôler le temps (en secondes) entre l'exécution de chaque élément d'une boucle de tâche, utilisez le paramètre pause avec la directive loop_control:

# main.yml-name: Create servers, pause 3s before creating next
  community.digitalocean.digital_ocean:name:"{{ item }}"state: present
  loop:- server1
    - server2
  loop_control:pause:3

Le suivi de la progression dans une boucle avec index_var

Nouveau dans la version 2.5.

Pour garder la trace de l'endroit où vous vous trouvez dans une boucle, utilisez la fonction index_var avec loop_control. Cette directive spécifie un nom de variable pour contenir l'indice de la boucle actuelle :

-name: Count our fruit
  ansible.builtin.debug:msg:"{{ item }} with index {{ my_idx }}"loop:- apple
    - banana
    - pear
  loop_control:index_var: my_idx

Note

index_var est indexée 0.

Définition des noms de variables internes et externes avec loop_var

Nouveau dans la version 2.1.

Vous pouvez imbriquer deux tâches de bouclage en utilisant include_tasks. Cependant, par défaut, Ansible définit la variable de boucle item pour chaque boucle. Cela signifie que la boucle intérieure, imbriquée, écrasera la valeur de item de la boucle extérieure. Vous pouvez spécifier le nom de la variable pour chaque boucle à l'aide de la fonction loop_var avec loop_control:

# main.yml-include_tasks: inner.yml
  loop:-1-2-3loop_control:loop_var: outer_item

# inner.yml-name: Print outer and inner items
  ansible.builtin.debug:msg:"outer item={{ outer_item }} inner item={{ item }}"loop:- a
    - b
    - c

Note

Si Ansible détecte que la boucle actuelle utilise une variable qui a déjà été définie, il lèvera une erreur pour faire échouer la tâche.

Variables de boucle étendues

Nouveau dans la version 2.8.

À partir de la version 2.8 d'Ansible, vous pouvez obtenir des informations sur les boucles étendues à l'aide de la fonction extended à la commande de boucle. Cette option exposera les informations suivantes .

Variable

Description

ansible_loop.allitems

La liste de tous les éléments de la boucle

ansible_loop.index

L'itération actuelle de la boucle. (1 indexé)

ansible_loop.index0

L'itération actuelle de la boucle. (0 indexé)

ansible_loop.revindex

Le nombre d'itérations à partir de la fin de la boucle (1 indexé).

ansible_loop.revindex0

Le nombre d'itérations à partir de la fin de la boucle (0 indexé).

ansible_loop.first

True si première itération

ansible_loop.last

True si dernière itération

ansible_loop.length

Le nombre d'éléments dans la boucle

ansible_loop.previtem

L'élément de l'itération précédente de la boucle. Indéfini lors de la première itération.

ansible_loop.nextitem

L'élément de l'itération suivante de la boucle. Indéfini lors de la dernière itération.

loop_control:extended: yes

Accès au nom de votre loop_var.

Nouveau dans la version 2.8.

Depuis Ansible 2.8, vous pouvez obtenir le nom de la valeur fournie à... loop_control.loop_var en utilisant le ansible_loop_var variable

Pour les auteurs de rôles, l'écriture de rôles qui autorisent les boucles, au lieu de dicter la valeur requise de la variable

. loop_var requise, vous pouvez recueillir la valeur via :

"{{ lookup('vars', ansible_loop_var) }}"

Migration de with_X à loop

Dans la plupart des cas, les boucles fonctionnent mieux avec l'option loop au lieu du mot-clé with_X de style boucles. Le site loop est généralement mieux exprimée en utilisant des filtres au lieu de l'utilisation plus complexe de query ou lookup.

Ces exemples montrent comment convertir de nombreux filtres with_ en boucles de style loop et en filtres.

avec_liste

with_list est directement remplacé par loop.

-name: with_list
  ansible.builtin.debug:msg:"{{ item }}"with_list:- one
    - two

-name: with_list -> loop
  ansible.builtin.debug:msg:"{{ item }}"loop:- one
    - two

avec_items

with_items est remplacé par loopet le flatten filtre.

-name: with_items
  ansible.builtin.debug:msg:"{{ item }}"with_items:"{{ items }}"-name: with_items -> loop
  ansible.builtin.debug:msg:"{{ item }}"loop:"{{ items|flatten(levels=1) }}"

avec_éléments_indexés

with_indexed_items est remplacé par loop , le flatten et le filtre loop_control.index_var.

-name: with_indexed_items
  ansible.builtin.debug:msg:"{{ item.0 }} - {{ item.1 }}"with_indexed_items:"{{ items }}"-name: with_indexed_items -> loop
  ansible.builtin.debug:msg:"{{ index }} - {{ item }}"loop:"{{ items|flatten(levels=1) }}"loop_control:index_var: index

avec_flatté

with_flattened est remplacé par loop et le flatten filtre.

-name: with_flattened
  ansible.builtin.debug:msg:"{{ item }}"with_flattened:"{{ items }}"-name: with_flattened -> loop
  ansible.builtin.debug:msg:"{{ item }}"loop:"{{ items|flatten }}"

avec_together

with_together est remplacé par loop et le zip filtre.

-name: with_together
  ansible.builtin.debug:msg:"{{ item.0 }} - {{ item.1 }}"with_together:-"{{ list_one }}"-"{{ list_two }}"-name: with_together -> loop
  ansible.builtin.debug:msg:"{{ item.0 }} - {{ item.1 }}"loop:"{{ list_one|zip(list_two)|list }}"

Un autre exemple avec des données complexes

- name: with_together -> loop
  ansible.builtin.debug:
    msg: "{{ item.0 }} - {{ item.1 }} - {{ item.2 }}"
  loop: "{{ data[0]|zip(*data[1:])|list }}"
  vars:
    data:
      - ['a', 'b', 'c']
      - ['d', 'e', 'f']
      - ['g', 'h', 'i']

avec_dict

with_dict peut être remplacé par loop et soit le dictsort ou dict2items filtres.

- name: with_dict
  ansible.builtin.debug:
    msg: "{{ item.key }} - {{ item.value }}"
  with_dict: "{{ dictionary }}"

- name: with_dict -> loop (option 1)
  ansible.builtin.debug:
    msg: "{{ item.key }} - {{ item.value }}"
  loop: "{{ dictionary|dict2items }}"

- name: with_dict -> loop (option 2)
  ansible.builtin.debug:
    msg: "{{ item.0 }} - {{ item.1 }}"
  loop: "{{ dictionary|dictsort }}"

avec_séquence

with_sequence est remplacé par loop et la séquence range et potentiellement la fonction format filtre.

- name: with_sequence
  ansible.builtin.debug:
    msg: "{{ item }}"
  with_sequence: start=0 end=4 stride=2 format=testuser%02x

- name: with_sequence -> loop
  ansible.builtin.debug:
    msg: "{{ 'testuser%02x' | format(item) }}"
  # range is exclusive of the end point
  loop: "{{ range(0, 4 + 1, 2)|list }}"

avec_subelements

with_subelements est remplacé par loopet le subelements filtre.

- name: with_subelements
  ansible.builtin.debug:
    msg: "{{ item.0.name }} - {{ item.1 }}"
  with_subelements:
    - "{{ users }}"
    - mysql.hosts

- name: with_subelements -> loop
  ansible.builtin.debug:
    msg: "{{ item.0.name }} - {{ item.1 }}"
  loop: "{{ users|subelements('mysql.hosts') }}"

avec_nested/avec_cartesian

with_nested et with_cartesian sont remplacés par loop et les product filtre.

- name: with_nested
  ansible.builtin.debug:
    msg: "{{ item.0 }} - {{ item.1 }}"
  with_nested:
    - "{{ list_one }}"
    - "{{ list_two }}"

- name: with_nested -> loop
  ansible.builtin.debug:
    msg: "{{ item.0 }} - {{ item.1 }}"
  loop: "{{ list_one|product(list_two)|list }}"

avec_choix_aléatoire

with_random_choice est remplacé par la simple utilisation du filtre random sans avoir besoin du filtre loop.

- name: with_random_choice
  ansible.builtin.debug:
    msg: "{{ item }}"
  with_random_choice: "{{ my_list }}"

- name: with_random_choice -> loop (No loop is needed here)
  ansible.builtin.debug:
    msg: "{{ my_list|random }}"
  tags: random

Voir aussi

Intro aux playbooks

Une introduction aux playbooks

Rôles

Organisation des playbooks par rôles

Conseils et astuces

Conseils et astuces pour les playbooks

Conditionnels

Les déclarations conditionnelles dans les livrets de jeu

Utilisation des variables

Tout sur les variables

Liste de diffusion des utilisateurs

Vous avez une question ? Passez par le groupe google !

irc.freenode.net

Canal de discussion IRC #ansible