Aller au contenu

Développer et utiliser les lookup Ansible

logo

Les lookups Ansible permet de récupérer des données qui sont stockées sur le noeud ou est exécuté le playbook. Par exemple il est possible de lire le contenu d’un fichier, de générer un mot de passe aléatoire, etc.

Les lookup Ansible disponibles

Il existe beaucoup de lookup disponible via la communauté ansible.

Nous allons nous intéresser à quelques lookup dont ceux qui sont intégrés par défaut à Ansible et à ceux de la communauté Général. Vous pouvez retrouver la liste de tous les lookups installés sur votre noeud via la commande suivante :

Terminal window
ansible-doc -l -t lookup
ansible.utils.get_path Retrieve the value in a v...
ansible.utils.index_of Find the indices of items...
ansible.utils.to_paths Flatten a complex object ...
ansible.utils.validate Validate data with provid...
...

Lire des variables d’environnement

On utilise ici le lookup env:

---
- name: Load env
gather_facts: no
hosts: all
vars:
my_env: "{{ lookup('env', 'HOME') }}"
tasks:
- name: show the env
debug:
msg: "{{ my_env }}"

Pour lancer ce playbook (et tous les autres exemples) il suffit de taper la commande suivante :

Terminal window
ansible-playbook -i localhost, -c local playbook.yml

On ne le lance que sur localhost via une connexion locale.

Ce qui donne :

ok: [localhost] => {
"msg": "/home/stephane.r"
}

Lire le retour d’une commande

Il est possible de récupérer la sortie d’une commande avec le lookup pipe :

---
- name: Load stdout of a command
gather_facts: no
hosts: all
vars:
my_env: "{{ lookup('pipe', 'date') }}"
tasks:
- name: show date
debug:
msg: "{{ my_env }}"

Ce qui donne :

ok: [localhost] => {
"msg": "jeu. 1 avr. 2021 11:01:45"
}

Lire le contenu d’un fichier

Pour lire le contenu d’un fichier, vous devez utiliser le lookup file. Rappel le fichier est stocké sur le noeud où est lancé le playbook. Donc pour notre test créer un fichier test.txt ou se trouve votre playbook :

---
- name: Read a file
gather_facts: no
hosts: all
vars:
my_file: "{{ lookup('file', 'test.txt') }}"
tasks:
- name: show test file content
debug:
msg: "{{ my_file }}"

Ce qui donne :

ok: [localhost] => {
"msg": "coucou je suis un fichier :)"
}

Générer un mot de passe

Cette fois , nousallons utiliser le lookup Ansible password.

---
- name: Generate a password
gather_facts: no
hosts: all
vars:
my_password: "{{ lookup('password', 'password.secret') }}"
tasks:
- name: display password
debug:
msg: "{{ my_password }}"

Ce qui donne :

ok: [localhost] => {
"msg": "wbf4C-byFvUB3fqci6lT"
}

Vous devriez retrouver le fichier password.secret dans le répertoire ou vous exécuter le playbook. Si vous ne voulez pas le stocker il suffit d’indiquer /dev/null.

Il est possible de formater le mot de passe en utilisant les paramètres suivant accolés au nom de fichier :

  • chars : vous pouvez indiquer les attributs du module string de python : ascii_letters, digits, hexdigits, punctuation ou votre propre liste de caractères.
  • length: la taille de votre mot de passe
  • encrypt: la méthode d’encryptage parmi : passlib.hash; md5_crypt, bcrypt, sha256_crypt, sha512_crypt. Par défaut, il s’agit du format plain_text sinon le fichier contiendra également le salt pour l’idem potence.

Exemple, on génère un mot de passe de 64 caractères intégrant des chiffres, des lettres et les caractères .-! :

---
- name: Generate a password
gather_facts: no
hosts: all
vars:
my_password: "{{ lookup('password', '/dev/null length=64 chars=ascii_letters,digits,._-! encrypt=md5_crypt') }}"
tasks:
- name: display password
debug:
msg: "{{ my_password }}"

Ce qui donne :

ok: [localhost] => {
"msg": "hWkD3GN1B9ZvgLWj1!NbYRR6MowYdO5mc5OlPMgHwEx5ezRphqVOcT6bXXul-!aV"
}

Lire une entrée DNS

Nous allons utiliser ici le lookup dig qui est très complet, mais il faut installer le module python dnspython:

Terminal window
pip3 install dnspython --user
---
- name: Generate a password
gather_facts: no
hosts: all
tasks:
- name: use in a loop
debug:
msg: "{{ lookup('dig', 'gmail.com./MX', wantlist=True) }}"

Ce qui donne :

ok: [localhost] => {
"msg": [
"5 gmail-smtp-in.l.google.com.",
"40 alt4.gmail-smtp-in.l.google.com.",
"10 alt1.gmail-smtp-in.l.google.com.",
"30 alt3.gmail-smtp-in.l.google.com.",
"20 alt2.gmail-smtp-in.l.google.com."
]
}

Ce lookup utilise deux paramètres :

  • flat: 0 ou 1 qui indique si l’on souhaite un export plat ou un dictionnaire
  • qtype: le type de requête Un exemple les utilisant :
---
- name: Generate a password
gather_facts: no
hosts: all
tasks:
- name: use in a loop
debug:
msg: "{{ lookup('dig', '_xmpp-server._tcp.gmail.com./SRV', 'flat=0') }}"

Ce qui donne:

ok: [localhost] => {
"msg": "10 alt1.gmail-smtp-in.l.google.com.,40 alt4.gmail-smtp-in.l.google.com.,20 alt2.gmail-smtp-in.l.google.com.,30 alt3.gmail-smtp-in.l.google.com.,5 gmail-smtp-in.l.google.com."
}

Les autres filtres

Comme dis plus haut il existe un tas de lookup disponible dont voici la liste complète.

Avant de vous lancer dans le développement de votre propre lookup vérifier qu’il n’existe pas.

Développer un lookup Ansible

Nous allons écrire notre propre plugin lookup qui retournera la version d’Ansible.

Dans le répertoire créer le fichier ansible_version.py avec ce contenu :

from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.errors import AnsibleError
from ansible.plugins.lookup import LookupBase
from ansible.release import __version__ as ansible_version
class LookupModule(LookupBase):
def run(self, terms, variables=None, **kwargs):
return [ansible_version]

Maintenant appelons le depuis un playbook:

---
- name: Get ansible version
gather_facts: no
hosts: all
tasks:
- name: display ansible version
debug:
msg: "{{ lookup('ansible_version') }}"

Ce qui donne :

ok: [localhost] => {
"msg": "2.10.6 "
}

Quelques explications

On retrouve une classe LookupModule qui hérite de la classe LookupBase. Cette classe doit implémenter la méthode run.

def run(self, terms, variables=None, **kwargs):

La variable contient les arguments passés au module lookup sous la forme d’un tableau.

Comme pour le plugin filter il est possible de lever des exceptions:

try:
import dnspython
except ImportError:
raise AnsibleError("the lookup diog cannot be run without dnspython installed")

Je suis sûr que vous avez déjà plein d’idées :)

Pour tester vos développements sur différentes versions d’Ansible et Python, je vous propose de lire ce billet.

Si vous voulez plus de tutorial Ansible je vous renvoie sur le billet de l’introduction à ansible