Voici le douzième article de la formation Ansible. Dans mon précédent article, je vous ai présenté les handlers et la possibilité d’exécuter une tâche seulement en cas de changement. Aujourd’hui nous allons nous intéresser aux variables.
Ansible offre la possibilité d’utiliser des variables dans les tasks, à condition que celles-ci aient été définies quelque part auparavant. Sachez qu’il existe une bonne vingtaine de méthodes différentes pour faire ceci. Non, ne partez pas en courant. Nous allons prendre notre temps et découvrir tout cela en mettant un pied devant l’autre.
Atelier pratique
Placez-vous dans le répertoire du treizième atelier pratique :
$ cd ~/formation-ansible/atelier-13
Voici les quatre machines virtuelles de cet atelier :
Machine virtuelle | Adresse IP |
---|---|
ansible |
10.23.45.10 |
rocky |
10.23.45.20 |
debian |
10.23.45.30 |
suse |
10.23.45.40 |
Démarrez les VM :
$ vagrant up
Connectez-vous au Control Host :
$ vagrant ssh ansible
L’environnement de cet atelier est préconfiguré et prêt à l’emploi :
- Ansible est installé sur le Control Host.
- Le fichier
/etc/hosts
du Control Host est correctement renseigné. - L’authentification par clé SSH est établie sur les trois Target Hosts.
- Le répertoire du projet existe et contient une configuration de base et un inventaire.
- Direnv est installé et activé pour le projet.
- Le validateur de syntaxe
yamllint
est également disponible.
Rendez-vous dans le répertoire des playbooks :
$ cd ansible/projets/ema/playbooks/ direnv: loading ~/ansible/projets/ema/.envrc direnv: export +ANSIBLE_CONFIG
Play vars
Une des nombreuses possibilités pour définir des variables consiste à inclure une section vars
dans le play. Dans l’exemple ci-dessous, deux variables sont d’abord définies dans la section vars
. Dans un deuxième temps, elles sont affichées grâce au module debug
:
--- # vars1.yml
- hosts: localhost
gather_facts: false
vars:
color: blue
number: 42
tasks:
- debug:
msg: "Color: {{color}}, Number: {{number}}"
...
Jusqu’ici, rien de particulier à signaler :
$ ansible-playbook vars1.yml PLAY [localhost] ********************************************************************* TASK [debug] ************************************************************************* ok: [localhost] => { "msg": "Color: blue, Number: 42" } PLAY RECAP *************************************************************************** localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Notez au passage la syntaxe avec les doubles accolades {{...}}
qui nous permet d’accéder au contenu des variables.
Extra vars
Une autre possibilité pour définir des variables consiste à les fournir en argument en ligne de commande avec l’option -e
(ou --extra-vars
) :
$ ansible-playbook vars1.yml -e color=red
TASK [debug] *************************************************************************
ok: [localhost] => {
"msg": "Color: red, Number: 42"
}
L’option -e
pourra d’ailleurs être réitérée autant de fois que vous voulez :
$ ansible-playbook vars1.yml -e color=red -e number=99
TASK [debug] *************************************************************************
ok: [localhost] => {
"msg": "Color: red, Number: 99"
}
Précédences des variables
Cette première manipulation des variables nous permet de tirer une conclusion : lorsqu’on définit une seule et même variable à plusieurs endroits, une de ces définitions va l’emporter sur les autres. Concrètement, nous savons déjà qu’une variable extra vars est plus forte qu’une variable play vars.
Vous vous doutez bien que ces règles de précédence sont clairement définies. Jetez un œil dans la documentation officielle à la section Using Variables. Vous y trouverez le classement officiel :
Non, je ne vous demande pas d’apprendre ce classement par cœur. En revanche, rien ne vous empêche de comprendre son fonctionnement :
- Il comporte en tout et pour tout 22 types de variables.
- Repérez les play vars en position 12.
- Repérez les extra vars en position 22.
- Le classement va manifestement du plus faible au plus fort.
- Il en résulte qu’une variable extra vars l’emportera toujours sur toutes les autres.
set_fact
Dans certains cas de figure, il devient nécessaire de définir des variables au cours de l’exécution d’un play. Dans ce cas, set_fact
vous permet d’ajouter des variables à un play ou même de redéfinir des variables existantes :
--- # set_fact.yml
- hosts: localhost
gather_facts: false
tasks:
- name: Define variables
set_fact:
color: yellow
number: 13
- debug:
msg: "Color: {{color}}, Number: {{number}}"
...
Rien de bien surprenant ici non plus :
$ ansible-playbook set_fact.yml TASK [Define variables] ******************************************************************** ok: [localhost] TASK [debug] ******************************************************************************* ok: [localhost] => { "msg": "Color: yellow, Number: 13" }
- Dans la liste des précédences, les variables set_fact occupent la position 19. On va dire qu’elles sont plutôt « fortes ».
- Un fact est une variable spécifique à l’hôte. À partir du moment où elle a été définie, elle ne sera visible que pour l’hôte qui vient d’exécuter la tâche correspondante.
- Ne vous tracassez pas trop sur cette distinction pour l’instant. Nous aborderons les facts Ansible en temps et en heure, et les choses vont s’éclaircir.
group_vars
Pour gérer les différences entre les systèmes, il nous faut une méthode qui permet d’attribuer une valeur à une variable en fonction de la cible. Un exemple pratique va vous permettre de comprendre ce fonctionnement. Dans l’exemple suivant, on inclut tous les Target Hosts, mais les variables mycolor
et mynumber
ne sont pas définies :
--- # vars2.yml
- hosts: all
gather_facts: false
tasks:
- debug:
msg: "Color: {{mycolor}}, Number: {{mynumber}}"
...
Comme il faut s’y attendre, l’accès au contenu d’une variable non définie résulte en une erreur d’exécution conséquente, pour ne pas dire fatale :
$ ansible-playbook vars2.yml PLAY [all] ********************************************************************************* TASK [debug] ******************************************************************************* fatal: [rocky]: FAILED! => {"msg": "The task includes an option with an undefined variable. fatal: [debian]: FAILED! => {"msg": "The task includes an option with an undefined variable. fatal: [suse]: FAILED! => {"msg": "The task includes an option with an undefined variable. PLAY RECAP ********************************************************************************* debian : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0 rocky : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0 suse : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
En dehors des extra vars, est-ce qu’il existe une méthode pour définir des variables à l’extérieur d’un playbook ? La réponse est oui. En effet, Ansible va chercher ce genre d’information dans un répertoire group_vars
:
$ mkdir -v ~/ansible/projets/ema/group_vars mkdir: created directory '/home/vagrant/ansible/projets/ema/group_vars'
Le groupe all
contient la liste complète de tous nos Target Hosts. On va donc créer un fichier all.yml
correspondant à l’intérieur du répertoire group_vars
, avec des valeurs par défaut pour tous les systèmes cible :
--- # group_vars/all.yml
mycolor: white
mynumber: 97
...
À partir de là, notre playbook s’exécute correctement :
$ ansible-playbook vars2.yml PLAY [all] *************************************************************************** TASK [debug] ************************************************************************* ok: [rocky] => { "msg": "Color: white, Number: 97" } ok: [debian] => { "msg": "Color: white, Number: 97" } ok: [suse] => { "msg": "Color: white, Number: 97" } PLAY RECAP *************************************************************************** debian : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 rocky : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 suse : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
C’est déjà pas mal, mais nous n’avons toujours pas de paramétrage individuel. Qu’à cela ne tienne, jetez un œil dans l’inventaire de cet atelier, qui définit un groupe [redhat_hosts]
avec l’hôte rocky
comme seul et unique membre :
[testing] rocky debian suse [redhat_hosts] rocky
Admettons qu’on souhaite « colorier en rouge » tous nos systèmes Red Hat. Dans ce cas, on pourrait très bien créer un fichier group_vars/redhat_hosts.yml
en partant du principe qu’Ansible s’en servira pour la définition des variables du groupe correspondant :
--- # group_vars/redhat_hosts.yml
mycolor: red
...
Résultat des courses :
TASK [debug] *************************************************************************
ok: [rocky] => {
"msg": "Color: red, Number: 97"
}
ok: [debian] => {
"msg": "Color: white, Number: 97"
}
ok: [suse] => {
"msg": "Color: white, Number: 97"
}
Là aussi, jetez un œil à la précédence des variables :
group_vars/all.yml
occupe la quatrième place (pas terrible)group_vars/*.yml
occupe la sixième place (un chouïa plus fort)
host_vars
Ansible vous offre également la possibilité de paramétrer des cibles individuelles. Le fonctionnement est analogue aux group vars, au détail près qu’Ansible va chercher ses informations dans un répertoire host_vars
correspondant :
$ mkdir -v ~/ansible/projets/ema/host_vars mkdir: created directory '/home/vagrant/ansible/projets/ema/host_vars'
Admettons qu’on souhaite « colorier en vert » notre système SUSE. Dans ce cas, il suffit de créer un fichier host_vars/suse.yml
en partant du principe qu’Ansible s’en servira pour la définition des variables de l’hôte correspondant :
--- # host_vars/suse.yml
mycolor: green
...
Et voici le résultat :
TASK [debug] *************************************************************************
ok: [rocky] => {
"msg": "Color: red, Number: 97"
}
ok: [debian] => {
"msg": "Color: white, Number: 97"
}
ok: [suse] => {
"msg": "Color: green, Number: 97"
}
Vous vous doutez bien que les paramètres d’un hôte individuels doivent l’emporter sur les paramètres de groupe. Là aussi, jetez un œil à la précédence des variables : host_vars/*.yml
se trouve en neuvième position.
En mode interactif
Une option moins courante consiste à définir vos variables au début de l’exécution d’un play à l’aide d’une section vars_prompt
comme ceci :
--- # prompting.yml
- hosts: localhost
gather_facts: false
vars_prompt:
- name: var1
prompt: Please select a value for var1
default: 42
private: false
- name: var2
prompt: And now for var2 (secret)
private: true
tasks:
- debug:
msg: "var1 is {{var1}}, var2 is {{var2}}"
...
Voilà ce que ça donne :
$ ansible-playbook prompting.yml
Please select a value for var1 [42]: 99
And now for var2 (secret): ***********
PLAY [localhost] *************************************************************************
TASK [debug] *****************************************************************************
ok: [localhost] => {
"msg": "var1 is 99, var2 is yatahongaga"
}
Si vous lancez le playbook avec des extra vars, la saisie interactive ne s’affiche plus en raison de la précédence :
$ ansible-playbook prompting.yml -e var1=38 -e var2=zamooche
PLAY [localhost] *************************************************************************
TASK [debug] *****************************************************************************
ok: [localhost] => {
"msg": "var1 is 38, var2 is zamooche"
}
Structures complexes
Avec Ansible, les variables peuvent très bien contenir des structures complexes, comme on peut le voir dans l’exemple ci-dessous :
--- # vars-complex.yml
- hosts: suse
gather_facts: false
vars:
motd:
hello: Bonjour cher visiteur !
quote:
- Chat échaudé craint la charrue avant la peau de l'ours.
- Chassez le naturiste, il revient au bungalow.
lotto: [2, 8, 17, 33, 34, 42]
tasks:
- name: Upload /etc/motd
copy:
dest: /etc/motd
content: |
{{ motd.hello }}
Citation du jour: {{ motd.quote[1] }}
Les numéros du loto: {{ motd.lotto | join(', ') }}
...
Le fichier /etc/motd
contient le message du jour (Message Of The Day) censé s’afficher juste après la connexion au shell sur les systèmes Unix/Linux.
Exécutez le playbook et connectez-vous à l’hôte suse
:
$ ssh suse Last login: Sun Sep 22 07:58:19 2024 from 10.23.45.10 Bonjour cher visiteur ! Citation du jour: Chassez le naturiste, il revient au bungalow. Les numéros du loto: 2, 8, 17, 33, 34, 42
Tout le monde est là ?
Dans certains cas de figure, ce n’est pas une mauvaise idée de vérifier d’emblée si toutes les variables nécessaires à l’exécution d’un playbook sont correctement définies. Le module assert
va nous assister dans cette démarche :
--- # assert.yml
- hosts: debian
gather_facts: false
tasks:
- assert:
that:
- hostname is defined and hostname != ''
fail_msg: The hostname variable is undefined or empty.
- name: Set hostname
hostname:
name: "{{ hostname }}"
- debug:
msg: Let's check if we see this.
...
La condition en-dessous de that
doit être remplie, faute de quoi le play s’interrompt :
$ ansible-playbook assert.yml PLAY [debian] ************************************************************************ TASK [assert] ************************************************************************ fatal: [debian]: FAILED! => { "assertion": "hostname is defined and hostname != ''", "changed": false, "evaluated_to": false, "msg": "The hostname variable is undefined or empty." } PLAY RECAP *************************************************************************** debian : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
Le même play avec une variable hostname
définie :
$ ansible-playbook assert.yml -e hostname=sandbox PLAY [debian] ************************************************************************ TASK [assert] ************************************************************************ ok: [debian] => { "changed": false, "msg": "All assertions passed" } TASK [Set hostname] ****************************************************************** changed: [debian] TASK [debug] ************************************************************************* ok: [debian] => { "msg": "Let's check if we see this." } PLAY RECAP *************************************************************************** debian : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Quittez le Control Host :
$ exit
Supprimez toutes les VM :
$ vagrant destroy -f
Exercice
Placez-vous dans le répertoire du quatorzième atelier pratique :
$ cd ~/formation-ansible/atelier-14
Voici les quatre machines virtuelles de cet atelier :
Machine virtuelle | Adresse IP |
---|---|
control |
10.23.45.10 |
target01 |
10.23.45.20 |
target02 |
10.23.45.30 |
target03 |
10.23.45.40 |
Les VM tournent toutes sous Rocky Linux. Démarrez-les :
$ vagrant up
Connectez-vous au Control Host :
$ vagrant ssh control
L’environnement de cet atelier est préconfiguré et prêt à l’emploi :
- Ansible est installé sur le Control Host.
- Le fichier
/etc/hosts
du Control Host est correctement renseigné. - L’authentification par clé SSH est établie sur les trois Target Hosts.
- Le répertoire du projet existe et contient une configuration de base et un inventaire.
- Direnv est installé et activé pour le projet.
- Le validateur de syntaxe
yamllint
est également disponible.
Rendez-vous dans le répertoire du projet :
$ cd ansible/projets/ema/ direnv: loading ~/ansible/projets/ema/.envrc direnv: export +ANSIBLE_CONFIG $ ls -l total 8 -rw-r--r--. 1 vagrant vagrant 65 Sep 19 14:26 ansible.cfg -rw-r--r--. 1 vagrant vagrant 128 Sep 19 14:26 inventory drwxr-xr-x. 2 vagrant vagrant 6 Sep 19 14:26 playbooks
- Écrivez un playbook
myvars1.yml
qui affiche respectivement votre voiture et votre moto préférée en utilisant le moduledebug
et deux variablesmycar
etmybike
définies en tant que play vars. - En utilisant les extra vars, remplacez successivement l’une et l’autre marque – puis les deux à la fois – avant d’exécuter le play.
- Écrivez un playbook
myvars2.yml
qui fait essentiellement la même chose quemyvars1.yml
, mais en utilisant une tâche avecset_fact
pour définir les deux variables. - Là aussi, essayez de remplacer les deux variables en utilisant des extra vars avant l’exécution du play.
- Écrivez un playbook
myvars3.yml
qui affiche le contenu des deux variablesmycar
etmybike
mais sans les définir. Avant d’exécuter le playbook, définissezVW
etBMW
comme valeurs par défaut pourmycar
etmybike
pour tous les hôtes, en utilisant l’endroit approprié. - Effectuez le nécessaire pour remplacer
VW
etBMW
parMercedes
etHonda
sur l’hôtetarget02
. - Écrivez un playbook
display_user.yml
qui affiche un utilisateur et son mot de passe correspondant à l’aide des variablesuser
etpassword
. Ces deux variables devront être saisies de manière interactive pendant l’exécution du playbook. Les valeurs par défaut serontmicrolinux
pouruser
etyatahongaga
pourpassword
. Le mot de passe ne devra pas s’afficher pendant la saisie.
Quittez le Control Host :
$ exit
Supprimez toutes les VM :
$ vagrant destroy -f
Lire la suite : Variables enregistrées
La rédaction de cette documentation demande du temps et des quantités significatives de café espresso. Vous appréciez ce blog ? Offrez un café au rédacteur en cliquant sur la tasse.
0 commentaire