
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.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »regex_searchetregex_findallpour extraire par regexflattenpour aplatir une liste de listesb64encodeetb64decodepour Base64password_hash('sha512')pour générer un hash compatible/etc/shadowfrom_yaml,to_nice_yamlpour parser/sérialiserhash('sha256')pour calculer une empreinte hex
Prérequis
Section intitulée « Prérequis »- Avoir lu Filtres Jinja2 essentiels ;
- Connaître la syntaxe Jinja2 (cf. Jinja2 — syntaxe de base).
regex_search et regex_findall
Section intitulée « regex_search et regex_findall »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(', ') }}flatten — aplatir une liste de listes
Section intitulée « flatten — aplatir une liste de listes »{{ [[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 }}"b64encode et b64decode
Section intitulée « b64encode et b64decode »{{ '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 }}"password_hash — hash compatible /etc/shadow
Section intitulée « password_hash — hash compatible /etc/shadow »{{ '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 et to_nice_yaml
Section intitulée « from_yaml et to_nice_yaml »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 }} ← 2to_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) }}"hash('sha256') — empreinte hex
Section intitulée « hash('sha256') — empreinte hex »{{ '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, '') }}"Cas pratique — combinaison de 4 filtres avancés (lab ecrire-code/filtres-jinja-avances)
Section intitulée « Cas pratique — combinaison de 4 filtres avancés (lab ecrire-code/filtres-jinja-avances) »Voici l’exemple validé sur le lab 27-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=webb64=YWRtaW46c2VjcmV0flat=[1, 2, 3, 4, 5]sha256=c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2Filtres bonus
Section intitulée « Filtres bonus »| Filtre | Usage |
|---|---|
urlsplit('host') | Décompose une URL (https://api.com/foo → 'api.com') |
dict2items / items2dict | Conversion 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 / ipv6 | Manipulations IP ('10.0.0.0/8' | ipaddr('host')) |
random / shuffle | Aléatoire |
truncate(N, end='...') | Tronquer une string |
wordcount / wordwrap(width) | Compter / wrapper des mots |
Pièges fréquents
Section intitulée « Pièges fréquents »| Symptôme | Cause | Fix |
|---|---|---|
regex_search retourne vide | Regex Python (re module), pas POSIX | Vérifier la syntaxe regex Python |
password_hash change à chaque run | Pas de salt explicite | Ajouter un salt fixe : password_hash('sha512', 'mon_salt') |
flatten retourne des éléments imbriqués | Sans levels, c’est récursif — vérifier que c’est OK | flatten(levels=1) pour 1 seul niveau |
b64encode plante sur des bytes | Le filtre attend une string | Caster d’abord en string |
hash('md5') rejeté en mode FIPS | MD5 désactivé en FIPS | Passer à sha256 ou sha512 |
À retenir
Section intitulée « À retenir »regex_search= première occurrence ;regex_findall= toutes les occurrences.flattenaplatit récursivement par défaut, ou un seul niveau aveclevels=1.b64encode/b64decodepour Base64 (auth headers, transport binaire).password_hash('sha512', 'salt'): toujours un salt explicite pour l’idempotence.from_yamlparse une string YAML,to_nice_yamlsérialise — utile pour les manifests Kubernetes.hash('sha256')= empreinte déterministe de string (cache keys, identifiants).
Pratiquer dans le lab
Section intitulée « Pratiquer dans le lab »Cette page a un lab d’accompagnement : labs/ecrire-code/filtres-jinja-avances/ dans
stephrobert/ansible-training. Il contient
un README.md guidé, et un challenge final auto-évalué : combiner
regex_search, b64encode, flatten et hash('sha256') dans un seul fichier
marqueur posé sur db1.lab.
cd ~/Projets/ansible-training/labs/ecrire-code/filtres-jinja-avances/cat README.mdpytest -v challenge/tests/