Aller au contenu
Infrastructure as Code medium

Filtres Jinja2 essentiels Ansible : default, regex_replace, combine, selectattr

15 min de lecture

Logo Ansible

Les filtres Jinja2 transforment une valeur via la syntaxe {{ valeur | filtre }}, c'est le levier principal pour générer un fichier de config, fusionner deux sources, filtrer une liste ou nettoyer une string. Ansible expose les filtres standard Jinja2 + ses propres filtres custom (combine, selectattr étendu, dict2items...). Cette page liste les 10 filtres essentiels de la RHCE EX294 avec un exemple concret pour chacun.

C'est une page de référence : on revient la consulter quand on a besoin de transformer une variable et qu'on cherche le filtre adapté.

  • Le filtre default pour les variables optionnelles
  • regex_replace pour transformer une string par regex
  • to_json, to_yaml, to_nice_yaml pour la sérialisation
  • dict2items et items2dict pour boucler sur des dicts
  • combine pour fusionner deux dictionnaires
  • selectattr + map(attribute='name') pour filtrer une liste de dicts
  • unique, union, difference, intersect pour les opérations d'ensemble
  • length, min, max, sum pour les agrégats
  • upper, lower, title, trim pour les transformations de strings
  • Avoir lu les pages précédentes de la sous-section Variables et facts ;
  • Connaître la syntaxe Jinja2 de base ({{ }}, {% %}).
{{ undefined_var | default('fallback') }}

Si la variable est absente ou None, retourne 'fallback'. Plusieurs variantes :

{{ var | default('fallback', true) }} ← fallback aussi si var est falsy ('', 0, [], {})
{{ var | default(omit) }} ← omet le paramètre si var absente (Ansible spécifique)

omit est ultra-utile pour conditionner un argument de module :

- name: Créer un user (sans password si var absente)
ansible.builtin.user:
name: "{{ user_name }}"
password: "{{ user_password | default(omit) }}"
{{ 'foo-bar-baz' | regex_replace('-', '_') }}

Sortie : foo_bar_baz. Avec groupes de capture :

{{ 'web1.lab' | regex_replace('^(\w+)(\d+)\.(\w+)$', '\\1_\\2_\\3') }}

Sortie : web_1_lab (les \1, \2, \3 sont les groupes capturés).

Sérialisation d'une variable structurée :

{{ my_dict | to_json }} ← JSON compact
{{ my_dict | to_nice_json(indent=2) }} ← JSON indenté
{{ my_dict | to_yaml }} ← YAML
{{ my_dict | to_nice_yaml(indent=2) }} ← YAML indenté

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

- name: Poser un config JSON
ansible.builtin.copy:
dest: /etc/app/config.json
content: "{{ app_config | to_nice_json(indent=2) }}"

dict2items convertit un dict en liste de {key, value} (utile pour boucler) :

vars:
ports:
nginx: 80
redis: 6379
tasks:
- name: Itérer sur le dict
ansible.builtin.debug:
msg: "{{ item.key }} : {{ item.value }}"
loop: "{{ ports | dict2items }}"

items2dict fait l'inverse, utile quand vous avez une liste de paires que vous voulez transformer en dict :

{{ [{key: 'a', value: 1}, {key: 'b', value: 2}] | items2dict }}

Sortie : {'a': 1, 'b': 2}.

{{ {a: 1, b: 2} | combine({b: 99, c: 3}) }}

Sortie : {'a': 1, 'b': 99, 'c': 3}. Le second dict gagne sur le premier en cas de conflit.

Pour une fusion récursive (nested) :

{{ defaults | combine(overrides, recursive=true) }}

Pattern classique : merger des defaults avec un overrides de l'environnement.

{{ services | selectattr('env', 'equalto', 'prod') | list }}

Garde uniquement les éléments dont la clé env vaut 'prod'. Tests disponibles : equalto, defined, undefined, match (regex), search (regex), in, gt, lt.

{{ users | selectattr('age', 'gt', 18) | list }} ← users majeurs
{{ services | rejectattr('disabled') | list }} ← services non disabled
{{ services | map(attribute='name') | list }}

Sortie : liste des noms des services. Combiné avec selectattr :

{{ services | selectattr('env', 'equalto', 'prod') | map(attribute='name') | list }}

Liste des noms des services prod.

Opérations d'ensemble :

{{ ['a', 'b', 'a', 'c'] | unique }} ← ['a', 'b', 'c']
{{ ['a', 'b'] | union(['b', 'c']) }} ← ['a', 'b', 'c']
{{ ['a', 'b', 'c'] | difference(['b']) }} ← ['a', 'c']
{{ ['a', 'b'] | intersect(['b', 'c']) }} ← ['b']

Ne fonctionnent que sur des listes. Pour un dict, passez par keys() ou values().

Agrégats :

{{ groups['webservers'] | length }} ← nombre de webservers
{{ [10, 20, 5] | min }} ← 5
{{ [10, 20, 5] | max }} ← 20
{{ [10, 20, 5] | sum }} ← 35

Transformations de strings :

{{ 'hello' | upper }} ← HELLO
{{ 'HELLO' | lower }} ← hello
{{ 'hello world' | title }} ← Hello World
{{ ' hello ' | trim }} ← 'hello'
{{ 'foo-bar' | replace('-', '_') }} ← foo_bar

Cas pratique, combinaison de 6 filtres (lab ecrire-code/filtres-jinja-essentiels)

Section intitulée « Cas pratique, combinaison de 6 filtres (lab ecrire-code/filtres-jinja-essentiels) »

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

vars:
raw_input: " HELLO World "
pkgs_a: ["nginx", "redis", "postgres"]
pkgs_b: ["redis", "memcached"]
services:
- { name: api, port: 8080, env: prod }
- { name: web, port: 80, env: staging }
- { name: cache, port: 6379, env: prod }
base_config: { app: api, port: 80 }
tls_overrides: { port: 443, tls: true }
tasks:
- name: Poser un fichier qui combine 6 filtres
ansible.builtin.copy:
dest: /tmp/filtres-result.txt
content: |
trimmed={{ raw_input | trim | lower }}
union={{ (pkgs_a + pkgs_b) | unique | sort | join(',') }}
prod_services={{ services | selectattr('env', 'equalto', 'prod') | map(attribute='name') | sort | join(',') }}
merged={{ (base_config | combine(tls_overrides)).items() | map('join', '=') | sort | join(' ') }}
default_value={{ undefined_var | default('fallback-OK') }}
yaml_safe={{ {'hello': 'world'} | to_yaml | trim }}
mode: "0644"

Contenu posé sur db1.lab :

trimmed=hello world
union=memcached,nginx,postgres,redis
prod_services=api,cache
merged=app=api port=443 tls=True
default_value=fallback-OK
yaml_safe="hello: world"

6 filtres en action : trim | lower, unique | sort | join, selectattr | map | sort | join, combine + items + map | sort | join, default, to_yaml | trim.

FiltreUsage
flatten(levels=N)Aplatit une liste de listes
randomÉlément aléatoire d'une liste
shuffleMélange une liste
dict_diff(other)Différence entre deux dicts
boolConvertit en booléen ('yes' | booltrue)
int, float, stringConversions de type
b64encode / b64decodeBase64
hash('sha256')Hashage
password_hash('sha512')Hash compatible /etc/shadow
regex_findall(pattern)Toutes les occurrences regex
community.general.json_query(...)JMESPath sur des structures complexes
SymptômeCauseFix
'list' object is not callableOubli des parenthèses sur lengthvar | length (pas var | length())
default retourne quand même la variableLa var existe mais est vide ('')Ajouter le true : var | default('x', true)
combine ne fusionne pas en profondeurMode non récursif par défautAjouter recursive=true
selectattr retourne toutMauvais test : utiliser 'equalto', pas '=='selectattr('env', 'equalto', 'prod')
to_yaml produit du !!set ou des balises bizarresVariable contient des objets non standardsPréférer to_nice_yaml ou caster en dict standard
  • default('x') = fallback ; default('x', true) = aussi pour valeur vide ; default(omit) = omet le paramètre.
  • regex_replace('pattern', '\\1') = transformation regex avec groupes de capture.
  • combine(dict, recursive=true) = fusion de dicts (récursive si demandé).
  • selectattr('attr', 'equalto', 'value') + map(attribute='name') = pattern central pour filtrer/extraire.
  • dict2items = transforme dict en liste de {key, value} pour boucler.
  • unique | sort | join(',') = chaîne classique pour produire une string déterministe.

Cette page a un lab d'accompagnement : labs/ecrire-code/filtres-jinja-essentiels/ dans stephrobert/ansible-training. Il contient un README.md guidé, un Makefile (make verify lance les tests), et un challenge final auto-évalué : appliquer 6 filtres dans un fichier marqueur : trim/lower, unique, selectattr, combine, default.

Une fois le lab provisionné :

Fenêtre de terminal
cd ~/Projets/ansible-training/labs/ecrire-code/filtres-jinja-essentiels/
cat README.md # tuto pas à pas
cat challenge/README.md # consigne du challenge final
pytest -v challenge/tests/ # lancer les tests testinfra

Si les tests passent, vous maîtrisez les concepts couverts dans ce guide. En cas de blocage, docs/troubleshooting.md à la racine du repo couvre les pièges fréquents (rate-limit SSH, clé absente, collection manquante).

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracking. 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