Livres de Jeu Ansible

Objectifs de certification

RHCE EX294 (RHEL8)

Si vous poursuivez des objectifs de certification, vous pourriez trouvez ici des bases qui vous aideront pour :

  • 2. Maîtrise des composants de base d’Ansible
    • 2.1. Inventaires
    • 2.2. Modules
    • 2.3. Variables
    • 2.4. Facts
    • 2.5. Jeux
    • 2.6. Playbooks
    • 2.8. Utiliser la documentation fournie pour trouver des informations spécifiques aux modules et commandes Ansible
  • 4. Configuration des noeuds gérés par Ansible
    • 4.1. Créer et distribuer des clés SSH aux noeuds gérés
  • 6. Création des jeux et des playbooks Ansible
    • 6.1. Utiliser des modules Ansible courants
    • 6.2. Utiliser des variables pour récupérer les résultats d’une commande exécutée
    • 6.3. Utiliser des conditions pour contrôler l’exécution des opérations
    • 6.4. Configurer la gestion des erreurs
    • 6.5. Créer des playbooks pour configurer des systèmes selon un état spécifique
  • 9. Utilisation des fonctions Ansible avancées
    • 9.3. Utilisation de variables et de facts Ansible

Références des sujets :

1. Comprendre le concept de livre de jeu

1.1. L’analogie d’un livre de jeu Ansible

Les livres de jeu (playbooks) sont écrits selon un langage d’automatisation simple et puissant. Les “playbooks” peuvent orchestrer avec précision plusieurs parties d’une topologie d’infrastructure, avec un contrôle très détaillé du nombre de machines à traiter à la fois.

L’approche de Ansible en matière d’orchestration est une approche simple et précise : le code d’automatisation devrait être pérenne et il devrait y avoir très peu de choses à retenir sur la syntaxe ou des fonctionnalités spéciales.

Les livres de jeu sont écrits en langage YAML, Ain’t Markup Language. YAML expose un minimum de syntaxe et propose un modèle de configuration ou de processus plutôt qu’un langage de script ou de programmation.

Chaque livre de jeu est composé de un ou plusieurs “jeux (plays)” énoncés dans une liste.

Un jeu est une analogie sportive qui définit un état ou un modèle et qui se “rejoue” de différentes manières à d’autres moments.

Le but d’un “jeu” est de faire correspondre un “groupe” contenant des “hôtes (hosts)” dans des “roles” bien définis représentés par des objets Ansible appelés des “tâches (tasks)”. Sur le plan fondamental, une “tâche” Ansible n’est rien de plus qu’un appel à un module Ansible accompagné de paramètres. Un module est un programme Ansible en vue de réaliser certaines actions sur les “hôtes”.

Un module est généré sur le contrôleur, il est copié, exécuté et effacé sur les noeuds (dans le cadre de l’automation des serveurs). Par contre , quand il s’agit de gérer des périphériques du réseau, comme des commutateurs (switches) ou des routeurs, un module est généré et exécuté localement sur le contrôleur pour agir sur la cible.

En composant un “livre de jeu” avec plusieurs “jeux” en une forme de liste portant sur des groupes d’inventaire, il est possible d’orchestrer un déploiement multi-machines, en exécutant certaines tâches dans un groupe de routeurs, certaines tâches dans un groupe de commutateurs, et encore d’autres commandes dans un autre groupe de serveurs …

1.2. Aperçu d’un livre de jeu Ansible

En résumé, un “livre de jeu” consiste à :

  • exécuter des tâches (tasks) pour un inventaire (inventory) d’hôtes (hosts) rassemblés en groupes,
  • utilisant certains modules,
  • utilisant ou peuplant certaines variables,
  • notamment, dans des modèles (templates) de fichiers.

Un “livre de jeu” organise des tâches. Mais il de bonne pratique d’utiliser des roles. Un “role” ajoute un niveau d’abstraction dans l’exécution d’un livre de jeu. Le concept de rôle fait l’objet d’un chapitre ultérieur.

1.3. Programme ansible-playbook

ansible-playbook est le programme qui permet de lancer des livres de jeu. Avec tous les paramètres de connexion et d’inventaire définis, il demande en paramètres le ou les livres de jeu à lancer.

Options courantes

De manière non-exhaustive, on manquera pas d’évoquer et de tester quelques options utiles et courantes :

OptionsDescription
-iPrécise le fichier ou le script dynamique d’inventaire.
-eAttend une variable définie sous la forme clé="valeur".
-tExécute uniquement les tâches en liste marquées d’un “tag”, une balise, une étiquette.
--skip-tagsEvite des “tags”
-lLimite une liste d’hôtes ou de groupes d’un inventaire sur lesquels le livre de jeu est appliqué.
-v, -vvvDéfinit le niveau de verbosité : utile au déboggage.

Options informatives

On trouvera des options informatives, c’est-à-dire des options qui n’agissent pas sur les cibles et qui informent du livre de jeu invoqué :

OptionsDescription
--syntax-checkVérifie la syntaxe du livre de jeu sans l’éxécuter.
--list-hostsFournit la liste des hôtes concernés par le livre de jeu sans l’éxécuter.
--list-tagsFournit la liste des balises (“tags”) dans le livre de jeu sans l’éxécuter.
--list-tasksFournit la liste des tâches qui devraient être exécutées par un livre de jeu.

Options d’exécution/déboggage

OptionsDescription
-C, --checkCette option essaie de prédire certains changement sans les exécuter.
-D, --diffAffiche les changements des (petits) fichiers et modèles. Utile avec l’option --check.
--step“One-step-at-a-time” : chaque tâche est confirmée avant d’être exécutée.

Options de connexion

OptionsDescription
-f FORKSDéfinit le nombre de processus parallèles.
-k, --ask-passDemande le mot de passe de connexion.
--key-file=PRIVATE_KEY_FILEPrécise la clé privée à utiliser.
-uPrécise le nom d’utilisateur pour la connexion.
-cPrécise le type de connexion à utiliser.

Options d’élévation de privilèges

OptionsDescription
-b, --becomeIndique l’usage de l’élévation de privilège (su, sudo, …).
-K, --ask-become-passActive la demande mot de passe avec l’élévation de privilège.

Tous les livres de jeu qui suivent dans les exemples de ces documents sont fonctionnels et peuvent être joués par le programme ansible-playbook.

1.4. Une liste de jeux

Un livre de jeu (playbook) est un fichier YAML constitué d’une liste de jeux. Chaque jeu comporte des sections qui définissent de manière obligatoire sa portée en désignant les hôtes ou les groupes d’un inventaire (hosts:). De manière optionnelle, mais probablement recommandée, on trouvera au niveau du jeu des paramètres de connexion, des paramètres d’élévation de privilèges, des variables définies et, aussi heureusement, différentes actions qui comportent des listes de tâches (tasks:, roles:, handlers:, etc.).

Un premier exemple de livre de jeu demo/empty-playbook.yaml pourrait être celui-ci :

---
#empty-playbook.yaml
#a playbook is a list of plays
- name: "PLAY 1"
  hosts: localhost
  tasks:
- name: "PLAY 2"
  hosts: localhost
  tasks:
- name: "PLAY 3"
  hosts: localhost
  tasks:

Ce livre de jeu porte sur l’hôte localhost soit dans notre cas sur le noeud de contrôle. localhost peut être sollicité par des tâches Ansible même s’il n’est pas précisé dans l’inventaire. Dans ce dernier cas, Ansible nous en informe avec des messages d’avertissement :

$ ansible-playbook empty-playbook.yaml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'

1.5. Structure d’un jeu

En effet, un jeu dispose toujours au minimum de la clé hosts: qui désigne un hôte ou un groupe de l’inventaire, comme par exemple dans le fichier minimal-playbook.yaml :

---
#minimal-playbook.yaml
#a playbook with one play
- hosts: localhost

Il est toujours conseillé voir fortement recommandé de dénommer ses objets jeu, tâche, rôle, etc … avec la directive name:.

Dans l’exemple suivant helloworld-playbook.yaml, on trouve un livre de jeu “Hello World” d’un seul jeu avec une seule tâche qui affiche le message “Hello World!” :

---
#helloworld-playbook.yaml
#one play, one task
- name: "Hello World **play**"
  hosts: localhost
  tasks:
    - name: "Hello World **task**"
      debug:
        msg: "Hello World!"

Mais on pourrait aussi trouver d’autres clés dans le niveau racine du jeu. Il conviendrait de les définir pour chaque jeu selon les cas :

  • des paramètres de connexion (connexion:),
  • des variables (voir plus loin), l’activation des facts (gather_facts: False),
  • des listes d’action de type pre_tasks:, post_tasks:, handlers:, roles:,
  • des paramètres d’élévation de privilèges (become: True, become_user:), etc …

Voici une image de synthèse pour ceux qui préfèrent :

Livre de jeu Hello World

1.6. Valeurs de retour

Ansible génère des valeurs de retour à la suite de l’exécution des tâches et des modules.

Valeurs de retour, Return Values

Valeurs de retourSignication de la valeurs de retour 
backup_file (fichier_de_sauvegarde)Pour les modules qui implémentent backup=noyes lors de la manipulation de fichiers, un chemin vers le fichier de sauvegarde créé.
changed (modifié)Un booléen indiquant si la tâche a dû effectuer des modifications. 
failed (échoué)Un booléen qui indique si la tâche a échoué ou non. 
invocationInformations sur la manière dont le module a été invoqué. 
msgUne chaîne avec un message générique relayé à l’utilisateur. 
rcCertains modules exécutent des utilitaires en ligne de commande ou sont conçus pour exécuter des commandes directement (raw, shell, commande, etc), ce champ contient le ‘code de retour’ de ces utilitaires. 
resultsSi cette clé existe, elle indique qu’une boucle était présente pour la tâche et qu’elle contient une liste du module normal’résultat’ par élément. 
skipped (évité)Un booléen qui indique si la tâche a été ignorée ou non. 
stderrCertains modules exécutent des utilitaires en ligne de commande ou sont conçus pour exécuter des commandes directement (raw, shell, commande, etc), ce champ contient la sortie d’erreur de ces utilitaires. 
stderr_linesLorsque stderr est retourné, nous fournissons aussi toujours ce champ qui est une liste de chaînes de caractères, un élément par ligne de l’original. 
stdoutCertains modules exécutent des utilitaires en ligne de commande ou sont conçus pour exécuter directement des commandes (raw, shell, commande, etc). Cette zone contient l’édition normale de ces utilitaires. 
stdout_linesLorsque stdout est retourné, Ansible fournit toujours une liste de chaînes de caractères, chacune contenant un élément par ligne de la sortie originale. 

2. Variables

2.1. Introduction

Il existe de nombreuses manière de contrôller le contenu des livres de jeu grâce à l’usage de variables. Il est de bonne pratique d’organiser ses procédures avec des variables.

Les variables Ansible peuvent être déclarées dans l’inventaire, dans les dossiers group_vars/ ou host_vars/ par exemple, pour toute valeur dépendantes des cibles elle-mêmes.

Les variables peuvent aussi être définies dans un jeu sous des forme diverses :

  • en valorisation directe dans la tâche ou dans le jeu ou encore dans le rôle (vars:),
  • en référence à un fichier (vars_files:),
  • en les “incluant” (include_vars:)1,
  • en appelant des variables d’environnement,
  • en important d’autres livres de jeu (import_playbook:),
  • par génération ou récolte dynamique (facter, gather_facts:),
  • définies par des tâches du livre de jeu (set_facts:),
  • définies à partir de la sortie standard d’une tâche (register:),
  • définies à partir d’invites interactives (vars_prompt:),
  • A partir des dossiers default/ ou variables/ d’un rôle.

On peut aussi les définir sur la ligne de commande d’ansible-playbook -e ponctuellement comme argument supplémentaire et prépondérant. On peut aussi désigner un fichier contenant des variables : --extra-vars "@some_file.json".

Les variables sont appelées en format Jinja2 sous la forme :

  • {{ var }}
  • {{ var["key1"]["key2"]}}
  • {{ var.key1.key2 }}
  • {{ varlist[0] }}

2.2. Appel aux variables

Les variables sont appelées en format Jinja2 sous la forme : {{ interface }} ou encore {{ ipv4.address }} quand on dispose d’une présentation de données comme par exemple dans le fichier demo/variables.yaml :

---
#variables.yaml
interface: eth0
ipv4:
  address: 192.168.1.1
ipv6:
  address:
    - fe80::1
    - fe80::11
    - fe80::111

Les guillemets sont nécessaires dans l’appel aux variables dans les paramètres car on appelle leur valeur.

Par contre dans les opérations logiques telles que les conditions cette mise entre guillemets n’est pas nécessaire.

Ansible est capable de récupérer des métadonnées sous forme de variables “dynamiques” ansible_*, aussi on peut aller chercher des variables d’autres hôtes de l’inventaire (hostvars, groups, group_names et inventory_hostname)

Enfin les modèles Jinja2 permet des traitement conditionnels, des boucles, des filtres …

Le livre de jeu suivant demo/variables-playbook.yaml démontre l’utilisation de variables dans le livre de jeu lui-même (vars:), venant d’un fichier (vars_files:), d’une interaction utilisateur (vars_prompt:) et de la collecte des “Facts” (gather_facts: True).

---
#variables-playbook.yaml
- name: "PLAY 1: variables playbook"
  hosts: localhost
  gather_facts: True
  vars:
    variable: a new value
  vars_files:
    - variables.yaml
  vars_prompt:
    name: response
    prompt: "What do to want to do?"
    private: no
  tasks:
    - name: "print variables"
      debug:
        msg:
          - "variable value: {{ variable }},"
          - "prompt value: {{ response }},"
          - "interface {{ interface }}: {{ ipv4.address }},"
          - "real hostname: {{ ansible_hostname }}"
          - "inventory hostname: {{ inventory_hostname }}"

On abordera ultérieurement dans le support la manipulation des variables. Notons aussi que Ansible-Vault permet de protéger ses variables. La protection des données sensibles avec ansible-vault est abordée dans un autre chapitre dédié.

3. Concept de tâches

3.1. Appels à des tâches

On trouvera dans le livre de jeu tasks-playbook.yaml suivant des actions :

  • pre_tasks:,
  • roles:,
  • tasks:,
  • post_tasks:,
  • handlers:,
  • include_tasks:.

qui peuvent contenir des listes de tâches.

---
#tasks-playbook.yaml
- name: "PLAY 1: tasks playbook"
  hosts: localhost
  connection: local
  pre_tasks:
  roles:
  tasks:
  post_tasks:
  handlers:
  include_tasks:

En toute logique, ce livre de jeu n’exécute aucune tâche.

3.2. Structure d’une tâche

Les tâches peuvent être directement appelées de manière séquentielle dans le livre de jeu selon la syntaxe formelle suivante (les modules présentés dans l’exemple n’existent pas) :

- name: "Task 1 example"
  module_x:
    argument1: foo
    argument2: bar
- name: "Task 2 example"
  module_y:
    argument1: foo
    argument2: bar

Les tâches font appel à une seule action, c’est-à-dire à un seul module et ses arguments, comme par exemple dans le livre de jeu playbook-with-simple-tasks.yaml :

---
#playbook-with-simple-tasks.yaml
- name: "PLAY 1: play with simple tasks"
  hosts: localhost
  connection: local
  gather_facts: False
  vars:
    test: True
  pre_tasks:
    - name: "PRE-TASK 1: collect facts"
      setup:
  tasks:
    - name: "TASK 1: check connectivity"
      ping:
    - name: "TASK 2: print hostname by var arg"
      debug:
        var: ansible_hostname
    - name: "TASK 3: print hostname by msg arg"
      debug:
        msg: "{{ ansible_hostname }}"
  post_tasks:
    - name: "POST-TASK 1: Succeed"
      command: /bin/true
    - name: "POST-TASK 2: Error"
      command: /bin/false

Dans ce dernier livre de jeu, on retrouve les modules suivants avec l’usage d’arguments :

  • setup : qui collecte les “facts” sur l’hôte.
  • ping : qui réalise un test de connectivité (selon la connexion définie).
  • debug : qui affiche des messages sur la sortie standard.
  • command : qui exécute des commandes système.

Il est parfaitement normal que ce livre de jeu se termine par un échec.

3.3. Idempotence des tâches

“Être idempotent permet à une tâche définie d’être exécutée une seule fois ou des centaines de fois sans créer un effet contraire sur le système cible, ne provoquant un changement à une seule reprise. En d’autres mots, si un changement est nécessaire pour obtenir le système dans un état désiré, alors le changement est réalisé ; par contre si le périphérique est déjà dans l’état désiré, aucun changement n’intervient. Ce comportement est différent des pratiques de scripts personnalisés et de copier/coller de lignes de commandes. Quand on exécute les mêmes commandes ou scripts sur un même système de manière répétée, le taux d’erreur est souvent élevé.”

Extrait de: Jason Edelman. « Network Automation with Ansible. », O’Reilly Media, 2016.

Attention, Ansible autorise l’idempotence, mais selon le module utilisé (les modules de commandes brutes), il faudra le manipuler pour atteindre cette exigence de conception.

3.4. Contrôle sur les tâches

Dans le livre de jeu suivant playbook-with-tasks.yaml, on trouve du contrôle sur les tâches :

---
#playbook-with-tasks.yaml
- name: "PLAY 1: playbook with tasks"
  hosts: localhost
  connection: local
  gather_facts: False
  vars:
    test: True
  pre_tasks:
    - name: "PRE-TASK 1: collect facts"
      setup:
  tasks:
    - name: "TASK 1: check connectivity"
      ping:
      register: output
    - name: "TASK 2: print variable output"
      debug:
        var: output
      when: test == True
    - name: "TASK 3: print hostname"
      debug:
        msg: "{{ ansible_hostname }} is {{ output.ping }}ing"
  post_tasks:
    - name: "POST-TASK 1: Error handeling and idempotency"
      command: /bin/false
      register: output
      ignore_errors: True
      changed_when: output.rc == 0

Une tâche peut être contrôlée de différentes manières, dans cet exemple :

  • La clé register: enregistre la sortie d’une tâche dans une variable.
  • La clé when: place des conditions à l’exécution de la tâche.
  • La clé ignore_errors n’interrompt pas l’exécution du livre de jeu en cas d’erreur.
  • La clé changed_when modifiera la valeur de sortie changed sous condition.

Ce livre de jeu exécute une tâche de collecte de facts avant toute autre, exécute en tâches le module ping, enregistre sa sortie dans une variable, affiche cette variable sous une condition de vérité et puis qui l’affiche dans un message, enfin, en toutes dernières tâches, il génère une erreur qui sera ignorée.

3.5. Inclusion de tâches

Si toutes les tâches étaient placées dans un fichier tasks.yaml, on pourrait parfaitement l’inclure dans le livre de jeu comme dans cet exemple playbook-with-tasks-included.yaml :

---
#playbook-with-tasks-included.yaml
- name: "PLAY 1: playbook with tasks included"
  hosts: localhost
  connection: local
  gather_facts: True
  vars:
    test: True
  tasks:
  - name: Include task list in play
    include_tasks: some-tasks.yaml

Ce dernier livre de jeu appelle le fichier de tâches some-tasks.yaml :

---
#some-tasks.yaml
- name: "PRE-TASK 1: check connectivity"
  ping:
  register: output
- name: "TASK 1: print variable output"
  debug:
    var: output
  when: test == True
- name: "TASK 2: print hostname"
  debug:
    msg: "{{ ansible_hostname }} is {{ output.ping }}ing"
  when: test == True
- name: "POST-TASK 1: Error handeling and idempotency"
  command: /bin/false
  register: output
  ignore_errors: True
  changed_when: output.rc == 0

Veuillez noter que la liste commence directement au début du fichier, contrairement au niveau des tâches dans le livre de jeu.

3.6. Gestionnaire (Handler)

Un “handler” (gestionnaire) est une tâche qui ne se sera réalisée qu’une seule fois à la suite d’un appel notify: associé à une tâche si la tâche rend une valeur de retour ‘changed’. Par exemple avec le livre de jeu playbook-with-handler.yaml

---
#playbook-with-handler.yaml
- name: "PLAY 1: playbook with handler"
  hosts: localhost
  connection: local
  gather_facts: False
  vars_prompt:
    - name: "response"
      prompt: "Do you want execute the task?\n1- Yes\n2- no\n"
      private: no
  tasks:
    - name: "TASK 1: always true"
      command: "true"
      notify: print state
      when: response == "1"
    - name: "TASK 1: always true"
      debug:
        msg: "Goodbye"
      when: response == "2"
  handlers:
    - name: "print state"
      debug:
        msg: "CHANGED !!!"

4. Jouer avec les variables et les Facts

Dans les exercices suivants nous allons utiliser les fonctionnalités :

  • vars: sur une tâche ou dans un jeu et vars_files: en premier niveau d’un jeu.
  • Le directive register: qui valorise une variable en cours de jeu avec la sortie générée par une tâche
  • Le module setfact qui permet de définir et de traiter des variables dans un jeu.

4.1. Créer un fichier de données YAML

Ansible pourra des fichiers contenant des variables quelles soit présentée en JSON, en XML, en CSV ou en YAML.

Ici, nous proposons de manipuler des données présentées en YAML.

Attention le nom de variables ne peuvent pas comporter d’espace, de tiret -, de point . ou commencer par un chiffre ! Il existe aussi une série de mots réservés.

Le fichier data.yml par exemple.

---
#data.yml
group: "omega"
users:
  - name: alfa
    password: testtest
  - name: beta
    password: testtest

4.2. Utiliser les données

---
#print-out-data.yml
- name: print out data.yml
  hosts: localhost
  gather_facts: False
  vars_files:
    - data.yml
  tasks:
  - debug:
      msg: "{{ item.name }} with weak password {{ item.password }}"
    loop: "{{ users }}"
  - debug:
      msg: "{{ group }}"

4.3. Variables internes

  • hostvars (par exemple hostvars[other.example.com]['ansible_facts']['distribution'] alors que le jeu s’applique à un hôte différent)
  • group_names (groupes contenant l’hôte courant sous forme de liste)
  • groups (tous les groupes et tous les hôtes de l’inventaire sous forme de liste), ou plus précisément groups['app_servers'].
  • inventory_hostname (l’hôte courant dans l’inventaire)
  • inventory_hostname_short (le premier élément de l’hôte dans l’inventaire, p.ex. www de www.example.com)
  • play_hosts (les noms d’hôtes dans la portée du jeu)
  • inventory_dir (l’emplacement de l’inventaire)
  • inventoty_file (le nom du fichier d’inventaire)

4.4. Facts

Les Facts sont des informations collectées par Ansible et que l’on peut appeller via des variables “magiques” ansible_*.

Par défaut, Ansible collecte les facts des cibles avant l’exécution des tâches du jeu. Ce comportement se controle sur le jeu avec la directive de jeu gather_facts:. On peut faire en sorte que la collecte de facts doivent toujours être paramétrée sur le jeu avec le paramètre gathering = explicit dans un fichier de configuration. Enfin le module setup permet de réaliser cette collecte à travers une tâche.

---
#print-out-operating-system.yml
- name: print out operating system
  hosts: localhost
  gather_facts: False
  tasks:
    - name: "facts"
      setup:
      register: output
    - name: "print output"
      debug:
        msg: "{{ output }}"
  • ansible_distribution
  • ansible_distribution_release
  • ansible_distribution_version
  • ansible_fqdn
  • ansible_hostname
  • ansible_os_family
  • ansible_pkg_mgr
  • ansible_default_ipv4.address
  • ansible_default_ipv6.address

4.5. Obtenir la version de sa distribution

---
#print-out-distribution.yml
- name: print out distribution
  hosts: localhost
  gather_facts: True
  tasks:
    - name: "print output"
      debug:
       msg: " {{ ansible_distribution }} {{ ansible_distribution_version }}"

4.6. Obtenir son adresse ip publique

---
#get-ipv4-public.yml
- name: "get ipv4 public address"
  hosts: localhost
  gather_facts: True
  tasks:
    - uri:
        url: https://ipinfo.io/ip
        return_content: yes
      register: ipinfo_content
    - set_fact:
        ip_address: "{{ ipinfo_content.content | replace('\n', '') }}"
    - set_fact:
        dns_name: "{{ ip_address }}.xip.io"
    - debug:
        msg: |
             "ip address : {{ ip_address }}
             dns name : {{ dns_name }}
             real hostname : {{ ansible_hostname}}
             inventory hostname : {{ inventory_hostname}}"

4.7. Sortie brute à traiter

---
- name: "print df -Th"
  hosts: localhost
  gather_facts: True
  tasks:
    - shell: df -Th
      register: df_output
    - set_fact:
        df: "{{ df_output }}"
    - debug:
        msg: "{{ df.stdout_lines | list }}"

Exercice : Sortie à traiter.

tests

4.8. Prompts

Prompts

Le module prompts permet valoriser des variables en interagissant avec l’utilisateur.

---
- hosts: localhost
  vars_prompt:
    - name: "name"
      prompt: "what is your name?"
      private: no
    - name: "favcolor"
      prompt: "what is your favorite color?"
      private: no
    - name: "my_password"
      prompt: "Enter password"
      private: yes
      encrypt: "sha512_crypt"
      confirm: yes
      salt_size: 7
  tasks:
    - debug:
        msg: |
             {{ name }} loves {{ favcolor }}
             His password is {{ my_password }}

4.9. Les conditions

Ansible Conditionals

when:

  • var == "Value", var >= 5, etc.
  • var, où var correspond à un boléen (yes, true, True, TRUE)
  • var is defined, var is not defined, ! var is defined
  • <condition1> and <condition2>
  • <condition1> or <condition2>
  • (<condition1> and <condition2>) or (<condition3> and <condition4>)
  tasks:
    - name: "Debian Action"
      debug:
        msg: "{{ ansible_hostname }} Debian host"
      when: ansible_facts['os_family'] == "Debian"
      # note that all variables can be directly in conditionals without double curly braces
  tasks:
    - name: "Action on CentOS 7 and Debian 9 systems"
      debug:
        msg: "{{ ansible_hostname }} CentOS 7 and Debian 9 systems"
      when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "7") or
            (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "9")
  tasks:
    - name: "shut down CentOS 7 systems"
      debug:
        msg: "{{ ansible_hostname }} CentOS 7"
      when:
        - ansible_facts['distribution'] == "CentOS"
        - ansible_facts['distribution_major_version'] == "7"
  tasks:
    - shell: echo "only on Red Hat 6, derivatives, and later"
      when: ansible_facts['os_family'] == "RedHat" and ansible_facts['lsb']['major_release']|int >= 6

Agir en fonction du résultat d’une commande :

---
#ansible-playbook register-result.yml -v
- hosts: localhost
  tasks:
    - command: /bin/false
      register: result
      ignore_errors: True

    - command: echo "print when result is failed"
      when: result is failed

    # In older versions of ansible use ``success``, now both are valid but succeeded uses the correct tense.
    - command: echo "print when result is succeed"
      when: result is succeeded

    - command: echo "print when result is skipped"
      when: result is skipped

    - command: echo "print when result is defined"
      when: result is defined

    - set_fact:
        result:

    - command: echo "print when result is not defined"
      when: ! result is defined

4.10. Filtres

{{ var | to_nice_json }}
{{ var | to_json }}
{{ var | from_json }}
{{ var | to_nice_yml }}
{{ var | to_yml }}
{{ var | from_yml }}
{{ result | failed }}
{{ result | changed }}
{{ result | success }}
{{ result | skipped }}
{{ var | manditory }}
{{ var | default(5) }}
{{ list1 | unique }}
{{ list1 | union(list2) }}
{{ list1 | intersect(list2) }}
{{ list1 | difference(list2) }}
{{ list1 | symmetric_difference(list2) }}
{{ path | basename }}
{{ path | dirname }}
{{ path | expanduser }}
{{ path | realpath }}
{{ var | b64decode }}
{{ var | b64encode }}
{{ filename | md5 }}
{{ var | bool }}
{{ var | int }}
{{ var | quote }}
{{ var | md5 }}
{{ var | fileglob }}
{{ var | match }}
{{ var | search }}
{{ var | regex }}

Voir aussi default jinja2 filters et Jinja2 Filters in Ansible.

4.11. Lookups

{{  lookup('file', '/etc/foo.txt')  }}
{{  lookup('password', '/tmp/passwordfile chars=ascii')  }}
{{  lookup('env','HOME')  }}
{{  lookup('pipe','date')  }}
{{  lookup('redis_kv', 'redis://localhost:6379,somekey')  }}
{{  lookup('dnstxt', 'example.com')  }}
{{  lookup('template', './some_template.j2')  }}

4.12. Boucles

Ansible Loops

Note : la boucle with_item est dépréciée au profit de la boucle loop.

On peut appeler une variable valorisée en liste dans l’exécution d’un module :

---
#my-fruits.yml
- name: "loop illustration"
  hosts: localhost
  vars:
    fruits:
      - apple
      - orange
      - pineapple
  tasks:
    - name: "Print my Fruits"
      debug:
        msg: "{{ item }}"
      loop: "{{ fruits }}"

Using register with a loop

---
#my-fruits-v2.yml
- name: "loop with register"
  hosts: localhost
  vars:
    fruits:
      - apple
      - orange
      - pineapple
  tasks:
    - name: "loop + register"
      shell: "echo {{ item }}"
      loop: "{{ fruits }}"
      register: echo
    - name: "print result"
      debug:
        msg: "{{ echo }}"

Mieux :

---
#my-fruits-v2.1.yml
- name: "loop with register"
  hosts: localhost
  vars:
    fruits:
      - apple
      - orange
      - pineapple
  tasks:
    - name: "loop register"
      shell: "echo {{ item }}"
      loop: "{{ fruits }}"
      register: echo
    - name: "print result"
      debug:
        msg: "{{ item.stdout_lines }}"
      loop: "{{ echo.results }}"

4.13. Boucle Standard

---
#other-my-fruits.yml
- name: "other loop illustration"
  hosts: localhost
  vars:
    fruits:
      - name: apple
        color: green
      - name: orange
        color: orange
      - name: pineapple
        color: yellow
  tasks:
    - name: "Print my Fruits"
      debug:
        msg: "This {{ item.name }} is {{ item.color }}"
      loop: "{{ fruits }}"

boucle Nested

---
#nested-my-fruits.yml
- name: "nested loop illustration"
  hosts: localhost
  vars:
    fruits:
    - id: 0
      name: apple
    - id: 1
      name: orange
    - id: 2
      name: pineapple
    colors:
    - id: 0
      name: green
    - id: 1
      name: orange
    - id: 2
      name: yellow
  tasks:
    - name: "Print my Fruits"
      debug:
        msg: "This {{ item.0.name }} can have the {{ item.1.name }} color"
      loop: "{{ fruits|product(colors)|list }}"

Boucle Over hashes

---
#print-phone-records.yml
- name: "over hashes loop illustration"
  hosts: localhost
  vars:
    users:
      alice:
        name: Alice Appleworth
        telephone: 123-456-7890
      bob:
        name: Bob Bananarama
        telephone: 987-654-3210
  tasks:
    - name: "Print phone records"
      debug:
        msg: "User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"
      loop: "{{ users|dict2items }}"

Random choice

Pas besoin de boucle ici !

---
#random-my-fruits.yml
- name: "loop with register"
  hosts: localhost
  vars:
    fruits:
      - apple
      - orange
      - pineapple
      - raspberry
  tasks:
    - name: "Print a random Fruit"
      debug:
        msg: "The chosen fruit is {{ fruits|random }}"

Boucle Do-Until

---
#do-until.yml
- name: "Do-Until illustration"
  hosts: localhost
  tasks:
    - name: "do the task until all systems go is viewed"
      shell: "for x in {1..4}; do sleep 3 ; echo false ; done ; echo all systems go "
      register: result
      until: result.stdout.find("all systems go") != -1
      retries: 5
      delay: 10

Laisser un commentaire