Livres De Jeu Ansible

1. Introduction

Un livre de jeux (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’inventaire (hosts:), et de manière optionnelle des paramètres de connexion, des paramètres d’élévation de privilèges, des variables et différentes actions qui comportent des listes de tâches (tasks:, roles:, handlers:, etc.).

Un premier exemple de livre de jeu pourrait être celui-ci :

playbooks/demo/empty-playbook.yaml

---
- name: "PLAY 1"
  hosts: localhost
  tasks:
- name: "PLAY 2"
  hosts: localhost
  tasks:
- name: "PLAY 3"
  hosts: localhost
  tasks:

2. 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.

playbooks/demo/minimal-playbook.yaml

---
- hosts: localhost

Il est toujours conseillé de nommer ses objets (name:)

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

playbooks/demo/helloworld-playbook.yaml

---
- name: "helloworld play"
  hosts: localhost
  tasks:
    - name: "Hello World Task"
      debug:
        msg: "Hello World!"

Mais on trouve d’autres clés qu’il convient de définir dans le jeu selon les cas :

  • des paramètres de connexion
  • des variables, l’activation des facts
  • des listes d’action de type pre_tasks:, post_tasks:, handlers:, roles:, etc …
  • des paramètres d’élévation de privilèges

3. Variables

Il est de bonne pratique d’organiser ses procédures avec des variables. Les variables Ansible peuvent être déclarées à différents endroits : dans l’inventaire ou dans les group_vars/’ ou ‘host_vars/.

On peut aussi les définir sur la ligne de commande d’ansible-playbook -e comme argument supplémentaire et prépondérant, mais aussi dans le livre de 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:), voir https://stackoverflow.com/questions/53253879/ansible-vars-files-vs-include-vars
  • 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
  • définies à partir d’invites inter-actives (vars_prompt:)

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 :

playbooks/demo/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 sa valeur.

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

Ansible est capable de récupérer des métadonnées sous forme de variables et comporte aussi des variables “dynamiques” ansible_*

playbooks/demo/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 }},"
          - "hostname: {{ ansible_hostname }}"

Notons aussi que Ansible-Vault permet de protéger ses variables.

4. Appels à des tâches

playbooks/demo/tasks-playbook.yaml

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

Toutes ces actions attendent des listes de tâches

5. Structure d’une tâche

Les tâches peuvent être directement appelées de manière séquentielle dans le livre de jeux selon la syntaxe :

- name: "Task example"
  module:
    option1: foo
    option2: bar

6. Options sur les tâches

Par exemple dans un livre de jeu, playbooks/demo/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

Les tâches font appel à une seule action, c’est-à-dire un module et ses arguments :

  • setup : collecte les “facts” sur l’hôte
  • ping : qui fait un test de connectivité
  • debug : qui affiche des messages sur la sortie standard
  • command : qui exécute des commande système

Une tâche peut être contrôlée de différente manière :

  • 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.

Si toutes les tâches étaient placées dans un fichier tasks.yaml, on pourrait parfaitement l’appeler du livre de jeux :

playbooks/demo/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: tasks.yaml

Fichier playbooks/demo/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

7. Gestionnaire (Handler)

Un “handler” 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 :

playbooks/demo/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 !!!"