Aller au contenu
English
English
Infrastructure as Code medium

Filtres Jinja2 avancés Ansible : regex_search, flatten, b64encode, password_hash

80 min de lecture

Logo Ansible

Au-delà des filtres essentiels (default, combine, selectattr, dict2items...) déjà couverts dans la sous-section Variables et facts, Ansible expose des filtres avancés pour les cas plus pointus. Cette page liste les filtres avancés que vous rencontrerez dans les templates de production : extraction par regex (regex_search, regex_findall), aplatissement de listes nested (flatten), encodages (b64encode, b64decode), hash de mot de passe (password_hash), parsing/sérialisation de YAML (from_yaml, to_nice_yaml), empreintes (hash).

C'est une page de référence : on revient ici quand on cherche un filtre précis pour un besoin spécifique.

  • regex_search et regex_findall pour extraire par regex
  • flatten pour aplatir une liste de listes
  • b64encode et b64decode pour Base64
  • password_hash('sha512') pour générer un hash compatible /etc/shadow
  • from_yaml, to_nice_yaml pour parser/sérialiser
  • hash('sha256') pour calculer une empreinte hex

regex_search('pattern') retourne la première occurrence (ou None) :

{{ 'web1.lab.example.com' | regex_search('^([a-z]+)') }} ← 'web'
{{ 'IP : 10.10.20.21' | regex_search('\\d+\\.\\d+\\.\\d+\\.\\d+') }} ← '10.10.20.21'
{{ 'rien à matcher' | regex_search('xyz') }} ← '' (chaîne vide)

regex_findall('pattern') retourne toutes les occurrences sous forme de liste :

{{ 'a-1, b-2, c-3' | regex_findall('([a-z])-(\\d)') }}
← [['a', '1'], ['b', '2'], ['c', '3']]

Pour ne capturer qu'un seul groupe :

{{ 'a-1 b-2 c-3' | regex_findall('([a-z])-\\d') }} ← ['a', 'b', 'c']

Cas pratique : extraire toutes les IPs d'un fichier de config :

{% set ips = config_text | regex_findall('\\d+\\.\\d+\\.\\d+\\.\\d+') %}
Adresses détectées : {{ ips | join(', ') }}
{{ [[1, 2], [3, [4, 5]]] | flatten }} ← [1, 2, 3, 4, 5]
{{ [[1, 2], [3, [4, 5]]] | flatten(levels=1) }} ← [1, 2, 3, [4, 5]]

Sans levels, récursif (aplatit tous les niveaux). Avec levels=1, un seul niveau.

Cas pratique : agréger les services de plusieurs groupes :

vars:
webservers_services: [nginx, php-fpm]
dbservers_services: [postgresql, redis]
caches_services: [memcached]
template_var: "{{ [webservers_services, dbservers_services, caches_services] | flatten | unique }}"
{{ 'admin:secret' | b64encode }} ← 'YWRtaW46c2VjcmV0'
{{ 'YWRtaW46c2VjcmV0' | b64decode }} ← 'admin:secret'

Cas pratique : générer un header Authorization: Basic ... :

- name: Appel API avec auth basic
ansible.builtin.uri:
url: https://api.example.com/data
headers:
Authorization: "Basic {{ (api_user + ':' + api_password) | b64encode }}"
{{ 'mySecretPass' | password_hash('sha512') }}
← '$6$xxx$xxxxxxxxxxx...' (hash crypt SHA-512)

Avec un salt explicite (idempotent) :

{{ 'mySecretPass' | password_hash('sha512', 'mysalt2024') }}

Sans salt explicite, Ansible génère un salt aléatoire à chaque appel, vous obtenez un nouveau hash à chaque run, ce qui casse l'idempotence du module user. Toujours poser un salt fixe en production :

- name: Définir le mot de passe d'un user
ansible.builtin.user:
name: alice
password: "{{ 'mySecretPass' | password_hash('sha512', 'salt-alice-2024') }}"

from_yaml parse une string YAML en objet Python :

{% set parsed = "key: value\nlist:\n - a\n - b" | from_yaml %}
{{ parsed.key }} ← 'value'
{{ parsed.list | length }} ← 2

to_nice_yaml(indent=2) sérialise un dict/liste en YAML lisible :

{{ my_dict | to_nice_yaml(indent=2) }}

Cas pratique : poser un fichier YAML à partir d'un dict Ansible :

- name: Poser un manifest Kubernetes
ansible.builtin.copy:
dest: /etc/k8s/deployment.yml
content: "{{ k8s_deployment | to_nice_yaml(indent=2) }}"
mode: "0644"
{{ 'foobar' | hash('sha256') }}
← 'c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2'

Algos supportés : md5, sha1, sha256, sha512. Cas pratique : générer un identifiant déterministe à partir d'une string :

- name: Générer un cache key
ansible.builtin.set_fact:
cache_key: "{{ (env + '-' + version) | hash('sha256') | truncate(16, true, '') }}"

Voici l'exemple validé sur le lab ecrire-code-filtres-jinja-avances :

vars:
fqdn: "web1.lab.example.com"
secret: "admin:secret"
nested: [[1, 2], [3, [4, 5]]]
to_hash: "foobar"
tasks:
- name: Poser le fichier de résultats
ansible.builtin.copy:
dest: /tmp/filtres-avances.txt
content: |
prefix={{ fqdn | regex_search('^([a-z]+)') }}
b64={{ secret | b64encode }}
flat={{ nested | flatten }}
sha256={{ to_hash | hash('sha256') }}

Sortie :

prefix=web
b64=YWRtaW46c2VjcmV0
flat=[1, 2, 3, 4, 5]
sha256=c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2
FiltreUsage
urlsplit('host')Décompose une URL (https://api.com/foo → 'api.com')
dict2items / items2dictConversion dict ↔ liste de paires
groupby('attr')Groupe une liste de dicts par valeur d'une clé
subelements('key')Itère sur des sous-éléments d'une liste de dicts
community.general.json_query('expr')JMESPath sur des structures complexes
ipaddr / ipv4 / ipv6Manipulations IP ('10.0.0.0/8' | ipaddr('host'))
random / shuffleAléatoire
truncate(N, end='...')Tronquer une string
wordcount / wordwrap(width)Compter / wrapper des mots
SymptômeCauseFix
regex_search retourne videRegex Python (re module), pas POSIXVérifier la syntaxe regex Python
password_hash change à chaque runPas de salt expliciteAjouter un salt fixe : password_hash('sha512', 'mon_salt')
flatten retourne des éléments imbriquésSans levels, c'est récursif, vérifier que c'est OKflatten(levels=1) pour 1 seul niveau
b64encode plante sur des bytesLe filtre attend une stringCaster d'abord en string
hash('md5') rejeté en mode FIPSMD5 désactivé en FIPSPasser à sha256 ou sha512

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  • regex_search = première occurrence ; regex_findall = toutes les occurrences.
  • flatten aplatit récursivement par défaut, ou un seul niveau avec levels=1.
  • b64encode / b64decode pour Base64 (auth headers, transport binaire).
  • password_hash('sha512', 'salt') : toujours un salt explicite pour l'idempotence.
  • from_yaml parse une string YAML, to_nice_yaml sérialise, utile pour les manifests Kubernetes.
  • hash('sha256') = empreinte déterministe de string (cache keys, identifiants).

Les filtres avancés se valident sur leur sortie exacte, à un caractère près. Le lab vous fait extraire une valeur avec regex_search, encoder en b64encode, hacher un mot de passe en password_hash('sha512') avec un salt fixe, interroger une structure JSON avec json_query et manipuler des CIDR. Le fichier marqueur produit est comparé au résultat attendu, donc un salt oublié se voit immédiatement.

  • Module template : l'endroit où ces filtres servent vraiment, au moment de générer le fichier sur la cible.
  • lineinfile vs template : le critère de choix entre générer un fichier entier et n'en modifier que deux lignes.
  • Quiz Écrire du code : trente questions pour vérifier que filtres, précédence et handlers sont réellement acquis.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens ce site gratuitement, sans publicité, sans profilage et sans compte à créer. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn