Dans un playbook, vous pouvez vouloir exécuter différentes tâches, ou avoir différents objectifs, en fonction de la valeur d'un fait (données sur le système distant), d'une variable, ou du résultat d'une tâche précédente. Vous pouvez vouloir que la valeur de certaines variables dépende de la valeur d'autres variables. Ou bien vous pouvez créer des groupes d'hôtes supplémentaires en fonction de la correspondance des hôtes avec d'autres critères. Vous pouvez faire toutes ces choses avec les conditionnelles.

Ansible utilise Jinja2 tests et filtres dans les conditionnels. Ansible prend en charge tous les tests et filtres standard, et en ajoute également quelques-uns qui sont uniques.

Note

Il existe de nombreuses options pour contrôler le flux d'exécution dans Ansible. Vous pouvez trouver plus d'exemples de conditionnels pris en charge à l'adresse suivante . https://jinja.palletsprojects.com/en/master/templates/#comparisons.

  • Conditionnels de base avec when

    • Conditionnels basés sur ansible_facts
    • Conditions basées sur les variables enregistrées
    • Conditionnels basés sur les variables
    • Utilisation des conditionnels dans les boucles
    • Chargement des faits personnalisés
    • Conditionnels avec réutilisation

      • Conditionnels avec importations
      • Conditionnels avec inclusions
      • Conditionnels avec rôles
    • Sélection de variables, de fichiers ou de modèles sur la base de faits.

      • Sélection de variables fichiers sur la base de faits
      • Sélection de fichiers et de modèles sur la base de faits
  • Faits couramment utilisés

    • ansible_facts[‘distribution’]
    • ansible_facts[‘distribution_major_version’]
    • ansible_facts[‘os_family’]

Conditionnels de base avec when

La déclaration conditionnelle la plus simple s'applique à une seule tâche. Créez la tâche, puis ajoutez un when qui applique un test. Le site when est une expression Jinja2 brute, sans double accolade (cf. group_by - Créer des groupes Ansible basés sur des faits.). Lorsque vous exécutez la tâche ou le playbook, Ansible évalue le test pour tous les hôtes. Sur tout hôte où le test passe (renvoie une valeur de True), Ansible exécute cette tâche. Par exemple, si vous installez mysql sur plusieurs machines, dont certaines ont SELinux activé, vous pouvez avoir une tâche pour configurer SELinux afin d'autoriser l'exécution de mysql. Vous voudriez que cette tâche ne s'exécute que sur les machines qui ont SELinux activé :

tasks:-name: Configure SELinux to start mysql on any port
    ansible.posix.seboolean:name: mysql_connect_any
      state:truepersistent: yes
    when: ansible_selinux.status == "enabled"
    # all variables can be used directly in conditionals without double curly braces

Conditionnels basés sur ansible_facts

Souvent, vous voulez exécuter ou ignorer une tâche en fonction de faits. Les faits sont des attributs des hôtes individuels, notamment l'adresse IP, le système d'exploitation, l'état d'un système de fichiers, et bien d'autres. Avec des conditionnels basés sur des faits :

  • Vous pouvez installer un certain paquet uniquement lorsque le système d'exploitation est une version particulière.
  • Vous pouvez sauter la configuration d'un pare-feu sur les hôtes avec des adresses IP internes.
  • Vous pouvez effectuer des tâches de nettoyage uniquement lorsqu'un système de fichiers devient plein.

Voir Faits couramment utilisés pour une liste de faits qui apparaissent fréquemment dans les déclarations conditionnelles. Tous les faits n'existent pas pour tous les hôtes. Par exemple, le fait 'lsb_major_release' utilisé dans un exemple ci-dessous n'existe que lorsque le paquet lsb_release est installé sur l'hôte cible. Pour voir quels faits sont disponibles sur vos systèmes, ajoutez une tâche de débogage à votre playbook :

-name: Show facts available on the system
  ansible.builtin.debug:var: ansible_facts

Voici un exemple de conditionnel basé sur un fait :

tasks:-name: Shut down Debian flavored systems
    ansible.builtin.command: /sbin/shutdown -t now
    when: ansible_facts['os_family'] == "Debian"

Si vous avez plusieurs conditions, vous pouvez les regrouper avec des parenthèses :

tasks:-name: Shut down CentOS 6 and Debian 7 systems
    ansible.builtin.command: /sbin/shutdown -t now
    when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or
          (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")

Vous pouvez utiliser opérateurs logiques pour combiner des conditions. Lorsque vous avez plusieurs conditions qui doivent toutes être vraies (c'est-à-dire une condition logique and), vous pouvez les spécifier sous forme de liste :

tasks:-name: Shut down CentOS 6 systems
    ansible.builtin.command: /sbin/shutdown -t now
    when:- ansible_facts['distribution'] == "CentOS"
      - ansible_facts['distribution_major_version'] == "6"

Si un fait ou une variable est une chaîne de caractères et que vous devez exécuter une comparaison mathématique sur celle-ci, utilisez un filtre pour vous assurer qu'Ansible lit la valeur comme un nombre entier :

tasks:-ansible.builtin.shell: echo "only on Red Hat 6, derivatives, and later"
    when: ansible_facts['os_family'] == "RedHat" and ansible_facts['lsb']['major_release']| int >= 6

Conditions basées sur les variables enregistrées

Souvent, dans un playbook, vous voulez exécuter ou sauter une tâche en fonction du résultat d'une tâche antérieure. Par exemple, vous pourriez vouloir configurer un service après qu'il ait été mis à niveau par une tâche antérieure. Pour créer une conditionnelle basée sur une variable enregistrée :

  1. Enregistrez le résultat de la tâche antérieure en tant que variable.
  2. Créez un test conditionnel basé sur la variable enregistrée.

Vous créez le nom de la variable enregistrée à l'aide de l'attribut register mot-clé. Une variable enregistrée contient toujours l'état de la tâche qui l'a créée ainsi que toute sortie que cette tâche a générée. Vous pouvez utiliser les variables enregistrées dans les modèles et les lignes d'action, ainsi que dans les conditionnels. when conditionnelles. Vous pouvez accéder au contenu de la chaîne de caractères de la variable enregistrée à l'aide de variable.stdout. Par exemple :

-name: Test play
  hosts: all

  tasks:-name: Register a variable
        ansible.builtin.shell: cat /etc/motd
        register: motd_contents

      -name: Use the variable in conditional statement
        ansible.builtin.shell: echo "motd contains the word hi"
        when: motd_contents.stdout.find('hi') !=-1

Vous pouvez utiliser les résultats enregistrés dans la boucle d'une tâche si la variable est une liste. Si la variable n'est pas une liste, vous pouvez la convertir en liste, avec soit . stdout_lines soit avec variable.stdout.split(). Vous pouvez également diviser les lignes par d'autres champs :

-name: Registered variable usage as a loop list
  hosts: all
  tasks:-name: Retrieve the list of home directories
      ansible.builtin.command: ls /home
      register: home_dirs

    -name: Add home dirs to the backup spooler
      ansible.builtin.file:path: /mnt/bkspool/{{ item }}src: /home/{{ item }}state: link
      loop:"{{ home_dirs.stdout_lines }}"# same as loop: "{{ home_dirs.stdout.split() }}"

Le contenu de la chaîne d'une variable enregistrée peut être vide. Si vous voulez exécuter une autre tâche uniquement sur les hôtes où le stdout de votre variable enregistrée est vide, vérifiez que le contenu de la chaîne de la variable enregistrée n'est pas vide :

-name: check registered variable for emptiness
  hosts: all

  tasks:-name: List contents of directory
        ansible.builtin.command: ls mydir
        register: contents

      -name: Check contents for emptiness
        ansible.builtin.debug:msg:"Directory is empty"when: contents.stdout == ""

Ansible enregistre toujours quelque chose dans une variable enregistrée pour chaque hôte, même sur les hôtes où une tâche échoue ou Ansible saute une tâche parce qu'une condition n'est pas remplie. Pour exécuter une tâche de suivi sur ces hôtes, interrogez la variable enregistrée sur is skipped (pas pour "undefined" ou "default"). Voir Enregistrement des variables pour plus d'informations. Voici des exemples de conditionnels basés sur le succès ou l'échec d'une tâche. N'oubliez pas d'ignorer les erreurs si vous voulez qu'Ansible continue à s'exécuter sur un hôte lorsqu'un échec se produit :

tasks:-name: Register a variable, ignore errors and continue
    ansible.builtin.command: /bin/false
    register: result
    ignore_errors:true-name: Run only if the task that registered the "result" variable fails
    ansible.builtin.command: /bin/something
    when: result is failed

  -name: Run only if the task that registered the "result" variable succeeds
    ansible.builtin.command: /bin/something_else
    when: result is succeeded

  -name: Run only if the task that registered the "result" variable is skipped
    ansible.builtin.command: /bin/still/something_else
    when: result is skipped

Note

Les anciennes versions d'Ansible utilisaient success et failmais succeeded et failed utilisent le temps correct. Toutes ces options sont maintenant valides.

Conditionnels basés sur des variables

Vous pouvez également créer des conditionnels basés sur des variables définies dans les playbooks ou l'inventaire. Comme les conditionnelles nécessitent une entrée booléenne (un test doit être évalué comme Vrai pour déclencher la condition), vous devez appliquer l'option | bool aux variables non booléennes, telles que les variables de type chaîne de caractères avec un contenu tel que 'yes', 'on', '1' ou 'true'. Vous pouvez définir des variables comme suit :

vars:epic:truemonumental:"yes"

Avec les variables ci-dessus, Ansible exécuterait l'une de ces tâches et sauterait l'autre :

tasks:-name: Run the command if "epic" or "monumental" is true
      ansible.builtin.shell: echo "This certainly is epic!"
      when: epic or monumental | bool

    -name: Run the command if "epic" is false
      ansible.builtin.shell: echo "This certainly isn't epic!"
      when: not epic

Si une variable requise n'a pas été définie, vous pouvez sauter ou échouer en utilisant la fonction de Jinja2. defined de Jinja2. Par exemple :

tasks:-name: Run the command if "foo" is defined
      ansible.builtin.shell: echo "I've got '{{ foo }}' and am not afraid to use it!"
      when: foo is defined

    -name: Fail if "bar" is undefined
      ansible.builtin.fail: msg="Bailing out. This play requires 'bar'"
      when: bar is undefined

Ceci est particulièrement utile en combinaison avec l'importation conditionnelle de fichiers vars (voir ci-dessous). Comme le montrent les exemples, il n'est pas nécessaire d'utiliser le paramètre {{ }} pour utiliser des variables à l'intérieur des conditionnelles, car celles-ci sont déjà implicites.

Utilisation des conditionnelles dans les boucles

Si vous combinez une

avec une instruction boucle, Ansible traite la condition séparément pour chaque élément. C'est à dessein, ainsi vous pouvez exécuter la tâche sur certains éléments de la boucle et la sauter sur d'autres éléments. Par exemple :

tasks:-name: Run with items greater than 5
      ansible.builtin.command: echo {{ item }}loop:[0,2,4,6,8,10]when: item > 5

Si vous avez besoin de sauter toute la tâche lorsque la variable de la boucle est indéfinie, utilisez le paramètre |default pour fournir un itérateur vide. Par exemple, lorsque vous bouclez sur une liste :

-name: Skip the whole task when a loop variable is undefined
  ansible.builtin.command: echo {{ item }}loop:"{{ mylist|default([]) }}"when: item > 5

Vous pouvez faire la même chose lorsque vous bouclez sur un dict :

-name: The same as above using a dict
  ansible.builtin.command: echo {{ item.key }}loop:"{{ query('dict', mydict|default({})) }}"when: item.value > 5

Chargement des faits personnalisés

Vous pouvez fournir vos propres faits, comme décrit dans la section Devriez-vous développer un module ?. Pour les exécuter, il suffit de faire un appel à votre propre module de collecte de faits personnalisés en haut de votre liste de tâches, et les variables qui y sont retournées seront accessibles aux tâches futures :

tasks:-name: Gather site specific fact data
      action: site_facts

    -name: Use a custom fact
      ansible.builtin.command: /usr/bin/thingy
      when: my_custom_fact_just_retrieved_from_the_remote_system == '1234'

Conditionnels avec réutilisation

Vous pouvez utiliser des conditionnels avec des fichiers de tâches, des playbooks ou des rôles réutilisables. Ansible exécute ces instructions conditionnelles différemment pour la réutilisation dynamique (includes) et pour la réutilisation statique (imports). Voir Réutilisation des artefacts Ansible pour plus d'informations sur la réutilisation dans Ansible.

Conditionnels avec les importations

Lorsque vous ajoutez une condition à une déclaration d'importation, Ansible applique la condition à toutes les tâches du fichier importé. Ce comportement est l'équivalent de Héritage des balises : ajout de balises à plusieurs tâches. Ansible applique la condition à chaque tâche, et évalue chaque tâche séparément. Par exemple, vous pourriez avoir un playbook appelé main.yml et un fichier de tâches appelé other_tasks.yml:

# all tasks within an imported file inherit the condition from the import statement# main.yml-import_tasks: other_tasks.yml # note "import"when: x is not defined

# other_tasks.yml-name: Set a variable
  ansible.builtin.set_fact:x: foo

-name: Print a variable
  ansible.builtin.debug:var: x

Ansible étend cela au moment de l'exécution à l'équivalent de :

-name: Set a variable if not defined
  ansible.builtin.set_fact:x: foo
  when: x is not defined
  # this task sets a value for x-name: Do the task if "x" is not defined
  ansible.builtin.debug:var: x
  when: x is not defined
  # Ansible skips this task, because x is now defined

Ainsi, si x est initialement indéfini, le paramètre debug sera sautée. Si ce n'est pas le comportement que vous souhaitez, utilisez une tâche include_* pour appliquer une condition uniquement à cette déclaration elle-même.

Vous pouvez appliquer des conditions à import_playbook ainsi qu'aux autres import_* . Lorsque vous utilisez cette approche, Ansible renvoie un message " ignoré " pour chaque tâche sur chaque hôte qui ne correspond pas aux critères, créant ainsi une sortie répétitive. Dans de nombreux cas, l'instruction module group_by peut être un moyen plus rationalisé d'accomplir le même objectif.e; voir Gérer les différences d'OS et de distro.

Conditionnels avec les includes

Lorsque vous utilisez une conditionnelle sur un include_* la condition n'est appliquée qu'à la tâche d'inclusion elle-même et pas à d'autres tâches dans le ou les fichiers inclus. Pour contraster avec l'exemple utilisé pour les conditionnelles sur les importations ci-dessus, regardez le même playbook et le même fichier de tâches, mais en utilisant un include au lieu d'un import :

# Includes let you re-use a file to define a variable when it is not already defined# main.yml-include_tasks: other_tasks.yml
  when: x is not defined

# other_tasks.yml-name: Set a variable
  ansible.builtin.set_fact:x: foo

-name: Print a variable
  ansible.builtin.debug:var: x

Ansible étend ceci au moment de l'exécution à l'équivalent de :

# main.yml-include_tasks: other_tasks.yml
  when: x is not defined
  # if condition is met, Ansible includes other_tasks.yml# other_tasks.yml-name: Set a variable
  ansible.builtin.set_fact:x: foo
  # no condition applied to this task, Ansible sets the value of x to foo-name: Print a variable
  ansible.builtin.debug:var: x
  # no condition applied to this task, Ansible prints the debug statement

En utilisant include_tasks au lieu de import_tasksles deux tâches de other_tasks.yml seront exécutées comme prévu. Pour plus d'informations sur les différences entre include v import voir Réutilisation des artefacts Ansible.

Conditionnels avec rôles

Il existe trois façons d'appliquer des conditions aux rôles :

  • Ajouter la même condition ou les mêmes conditions à toutes les tâches du rôle en plaçant vos when sous la rubrique roles sous le mot-clé roles . Voir l'exemple dans cette section.
  • Ajoutez la ou les mêmes conditions à toutes les tâches du rôle en plaçant votre mot-clé when sur une déclaration statique import_role dans votre playbook.
  • Ajoutez une ou plusieurs conditions à des tâches ou des blocs individuels au sein du rôle lui-même. C'est la seule approche qui vous permet de sélectionner ou de sauter certaines tâches à l'intérieur du rôle en fonction de votre... when déclaration. Pour sélectionner ou sauter des tâches à l'intérieur du rôle, vous devez avoir des conditions définies sur des tâches ou des blocs individuels, utiliser la déclaration when dynamique. include_role dans votre playbook, et ajouter la ou les conditions à l'include. Lorsque vous utilisez cette approche, Ansible applique la condition à l'include lui-même plus toutes les tâches du rôle qui ont également cette instruction when déclaration.

Lorsque vous incorporez un rôle dans votre playbook de manière statique avec l'instruction rolesmot-clé, Ansible ajoute les conditions que vous définissez à toutes les tâches du rôle. Par exemple :

-hosts: webservers
  roles:-role: debian_stock_config
       when: ansible_facts['os_family'] == 'Debian'

Sélection de variables, de fichiers ou de modèles en fonction de faits.

Parfois, les faits concernant un hôte déterminent les valeurs que vous voulez utiliser pour certaines variables ou même le fichier ou le modèle que vous voulez sélectionner pour cet hôte. Par exemple, les noms des paquets sont différents sur CentOS et sur Debian. Les fichiers de configuration pour les services communs sont également différents sur les différentes saveurs et versions du système d'exploitation. Pour charger un fichier de variables, des modèles ou d'autres fichiers différents en fonction d'un fait concernant les hôtes :

  1. nommez vos fichiers vars, vos modèles ou vos fichiers pour qu'ils correspondent au fait Ansible qui les différencie.
  2. sélectionnez le bon fichier vars, modèle ou fichier pour chaque hôte avec une variable basée sur ce fait Ansible.

Ansible sépare les variables des tâches, empêchant vos playbooks de se transformer en code arbitraire avec des conditionnels imbriqués. Cette approche donne lieu à des règles de configuration plus rationalisées et auditables, car il y a moins de points de décision à suivre.

Sélection des fichiers de variables en fonction des faits

Vous pouvez créer un playbook qui fonctionne sur plusieurs plateformes et versions de systèmes d'exploitation avec un minimum de syntaxe en plaçant vos valeurs de variables dans des fichiers vars et en les important de manière conditionnelle. Si vous voulez installer Apache sur certains serveurs CentOS et certains serveurs Debian, créez des fichiers de variables avec des clés et des valeurs YAML. Par exemple :

---# for vars/RedHat.ymlapache: httpd
somethingelse:42

Importez ensuite ces fichiers de variables en fonction des faits que vous recueillez sur les hôtes dans votre playbook :

----hosts: webservers
  remote_user: root
  vars_files:-"vars/common.yml"-["vars/{{ ansible_facts['os_family'] }}.yml","vars/os_defaults.yml"]tasks:-name: Make sure apache is started
    ansible.builtin.service:name:'{{ apache }}'state: started

Ansible rassemble les faits sur les hôtes du groupe webservers, puis interpole la variable "ansible_facts".[‘os_family’]" en une liste de noms de fichiers. Si vous avez des hôtes avec des systèmes d'exploitation Red Hat (CentOS, par exemple), Ansible recherche 'vars/RedHat.yml'. Si ce fichier n'existe pas, Ansible tente de charger 'vars/os_defaults.yml'. Pour les hôtes Debian, Ansible recherche d'abord 'vars/Debian.yml', avant de se rabattre sur 'vars/os_defaults.yml'. Si aucun fichier de la liste n'est trouvé, Ansible lève une erreur.

Sélection des fichiers et des modèles en fonction des faits

Vous pouvez utiliser la même approche lorsque différentes saveurs ou versions d'OS nécessitent différents fichiers ou modèles de configuration. Sélectionnez le fichier ou le modèle approprié en fonction des variables attribuées à chaque hôte. Cette approche est souvent beaucoup plus propre que de mettre beaucoup de conditionnels dans un seul modèle pour couvrir plusieurs versions d'OS ou de paquets.

Par exemple, vous pouvez mettre en template un fichier de configuration qui est très différent entre, disons, CentOS et Debian :

-name: Template a file
  ansible.builtin.template:src:"{{ item }}"dest: /etc/myapp/foo.conf
  loop:"{{ query('first_found', { 'files': myfiles, 'paths': mypaths}) }}"vars:myfiles:-"{{ ansible_facts['distribution'] }}.conf"-  default.conf
    mypaths:['search_location_one/somedir/','/opt/other_location/somedir/']

Faits couramment utilisés

Les faits Ansible suivants sont fréquemment utilisés dans les conditionnels.

faits_ansible[‘distribution’]

Valeurs possibles (échantillon, liste non exhaustive) :

Alpine
Altlinux
Amazon
Archlinux
ClearLinux
Coreos
CentOS
Debian
Fedora
Gentoo
Mandriva
NA
OpenWrt
OracleLinux
RedHat
Slackware
SLES
SMGL
SUSE
Ubuntu
VMwareESX

ansible_facts[‘distribution_major_version’]

La version majeure du système d'exploitation. Par exemple, la valeur est 16 pour Ubuntu 16.04.

ansible_facts[‘os_family’]

Valeurs possibles (échantillon, liste non exhaustive) :

AIX
Alpine
Altlinux
Archlinux
Darwin
Debian
FreeBSD
Gentoo
HP-UX
Mandrake
RedHat
SGML
Slackware
Solaris
Suse
Windows

Voir aussi

Travailler avec des playbooks

Une introduction aux playbooks

Rôles

Organisation des playbooks par rôles

Conseils et astuces

Conseils et astuces pour les playbooks

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