
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é.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Le filtre
defaultpour les variables optionnelles regex_replacepour transformer une string par regexto_json,to_yaml,to_nice_yamlpour la sérialisationdict2itemsetitems2dictpour boucler sur des dictscombinepour fusionner deux dictionnairesselectattr+map(attribute='name')pour filtrer une liste de dictsunique,union,difference,intersectpour les opérations d'ensemblelength,min,max,sumpour les agrégatsupper,lower,title,trimpour les transformations de strings
Prérequis
Section intitulée « Prérequis »- Avoir lu les pages précédentes de la sous-section Variables et facts ;
- Connaître la syntaxe Jinja2 de base (
{{ }},{% %}).
Le top 10 RHCE
Section intitulée « Le top 10 RHCE »1. default, valeur de fallback
Section intitulée « 1. default, valeur de fallback »{{ 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) }}"2. regex_replace, transformation par regex
Section intitulée « 2. regex_replace, transformation par regex »{{ '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).
3. to_json / to_yaml / to_nice_yaml
Section intitulée « 3. to_json / to_yaml / to_nice_yaml »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) }}" mode: "0644"4. dict2items / items2dict
Section intitulée « 4. dict2items / items2dict »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}.
5. combine, fusion de dicts
Section intitulée « 5. combine, fusion de dicts »{{ {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.
6. selectattr, filtrer une liste de dicts
Section intitulée « 6. selectattr, filtrer une liste de dicts »{{ 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 disabled7. map(attribute='name'), extraire un champ
Section intitulée « 7. map(attribute='name'), extraire un champ »{{ 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.
8. unique, union, difference, intersect
Section intitulée « 8. unique, union, difference, intersect »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().
9. length, min, max, sum
Section intitulée « 9. length, min, max, sum »Agrégats :
{{ groups['webservers'] | length }} ← nombre de webservers{{ [10, 20, 5] | min }} ← 5{{ [10, 20, 5] | max }} ← 20{{ [10, 20, 5] | sum }} ← 3510. upper, lower, title, trim, replace
Section intitulée « 10. upper, lower, title, trim, replace »Transformations de strings :
{{ 'hello' | upper }} ← HELLO{{ 'HELLO' | lower }} ← hello{{ 'hello world' | title }} ← Hello World{{ ' hello ' | trim }} ← 'hello'{{ 'foo-bar' | replace('-', '_') }} ← foo_barCas pratique, combinaison de 6 filtres
Section intitulée « Cas pratique, combinaison de 6 filtres »Voici l'exemple validé sur le lab 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 worldunion=memcached,nginx,postgres,redisprod_services=api,cachemerged=app=api port=443 tls=Truedefault_value=fallback-OKyaml_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.
Filtres souvent utilisés mais avancés
Section intitulée « Filtres souvent utilisés mais avancés »| Filtre | Usage |
|---|---|
flatten(levels=N) | Aplatit une liste de listes |
random | Élément aléatoire d'une liste |
shuffle | Mélange une liste |
dict_diff(other) | Différence entre deux dicts |
bool | Convertit en booléen ('yes' | bool → true) |
int, float, string | Conversions de type |
b64encode / b64decode | Base64 |
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 |
Pièges fréquents
Section intitulée « Pièges fréquents »| Symptôme | Cause | Fix |
|---|---|---|
'list' object is not callable | Oubli des parenthèses sur length | var | length (pas var | length()) |
default retourne quand même la variable | La var existe mais est vide ('') | Ajouter le true : var | default('x', true) |
combine ne fusionne pas en profondeur | Mode non récursif par défaut | Ajouter recursive=true |
selectattr retourne tout | Mauvais test : utiliser 'equalto', pas '==' | selectattr('env', 'equalto', 'prod') |
to_yaml produit du !!set ou des balises bizarres | Variable contient des objets non standards | Préférer to_nice_yaml ou caster en dict standard |
À retenir
Section intitulée « À retenir »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.
Mettre en pratique
Section intitulée « Mettre en pratique »Ces filtres s'enchaînent, et c'est la chaîne complète qui résiste au premier essai. Le lab vous fait couvrir une variable absente avec default, filtrer une liste de dicts avec selectattr et map, puis fusionner deux dictionnaires avec combine. Le résultat attendu est une chaîne déterministe, ce qui oblige à terminer par unique | sort | join plutôt qu'à compter sur l'ordre d'origine.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Conditions avec when : c'est dans les conditions que ces filtres servent le plus souvent au quotidien.
- Filtres Jinja2 avancés :
regex_search,password_hashetto_nice_yamlprennent le relais quand les dix essentiels ne suffisent plus. - Tests Jinja2 : un test répond par vrai ou faux là où un filtre transforme, et la confusion entre les deux est fréquente.