Aller au contenu
Infrastructure as Code medium

Modules assert et fail Ansible : validation défensive

10 min de lecture

Logo Ansible

Deux modules complémentaires pour la programmation défensive en Ansible :

  • ansible.builtin.assert: valide une condition ; si elle est fausse, le play échoue avec un message clair. Pattern de précondition en début de play.
  • ansible.builtin.fail: échoue explicitement avec un message custom. Pattern de branche d’erreur dans une logique conditionnelle.

Différence sémantique : assert: exprime “ça doit être vrai” ; fail: exprime “j’arrête maintenant parce que telle condition est rencontrée”.

  • Valider des prérequis en début de play avec assert:.
  • Personnaliser les messages avec fail_msg: / success_msg:.
  • Échouer explicitement avec fail: + when: (branche d’erreur).
  • Combiner assert: avec tests Jinja2 (is defined, is integer).
  • Choisir entre assert:, fail:, et failed_when: selon le contexte.
  • Connaître les tests Jinja2 (cf. Lab 28).
- name: Valider que app_port est dans la plage non-privilegiee
ansible.builtin.assert:
that:
- app_port is defined
- app_port is integer
- app_port > 1024
- app_port < 65535
fail_msg: "app_port doit etre un entier entre 1024 et 65535 (recu : {{ app_port | default('absent') }})"
success_msg: "Validation OK : app_port = {{ app_port }}"

Comportement :

  • Si toutes les conditions sont vraies → success_msg affiché, play continue.
  • Si une condition échouefail_msg affiché, play failed.

that: accepte une liste de conditions = AND implicite.

- name: Detecter un environnement non supporte
ansible.builtin.fail:
msg: |
Environnement non supporte :
- OS : {{ ansible_distribution }}
- Version : {{ ansible_distribution_version }}
Ce playbook necessite RHEL/AlmaLinux 9+.
when: ansible_distribution not in ['AlmaLinux', 'RedHat', 'Rocky']
or ansible_distribution_major_version | int < 9

fail: est une tâche qui échoue toujours quand elle s’exécute. when: la conditionne.

# Equivalent fonctionnel
- ansible.builtin.assert:
that: ansible_distribution_major_version | int >= 9
fail_msg: "RHEL 9+ requis"
- ansible.builtin.fail:
msg: "RHEL 9+ requis"
when: ansible_distribution_major_version | int < 9

Préférer assert: quand la condition est positive (“ça doit être vrai”). Préférer fail: quand la logique est branche d’erreur explicite (“si l’OS est X, on ne supporte pas”).

- name: Deploy myapp
hosts: webservers
become: true
pre_tasks:
- name: Pre-requis - OS
ansible.builtin.assert:
that:
- ansible_distribution in ['AlmaLinux', 'RedHat', 'Rocky']
- ansible_distribution_major_version | int >= 9
fail_msg: "OS non supporte"
- name: Pre-requis - paquets installes
ansible.builtin.command: rpm -q chrony firewalld
register: pkgs_check
changed_when: false
failed_when: pkgs_check.rc != 0
- name: Pre-requis - port libre
ansible.builtin.wait_for:
port: 8080
state: stopped
timeout: 5
tasks:
# ... vraies taches de deploy

pre_tasks: est la section dédiée aux préconditions. Si une assert: failed dedans, les tasks: ne tournent pas. Pattern fail-fast propre.

- name: Valider la structure d une variable complexe
vars:
db_config:
host: db1.lab
port: 5432
pool_size: 10
ansible.builtin.assert:
that:
- db_config is defined
- db_config is mapping # est un dict
- db_config.host is defined
- db_config.host is string
- db_config.port is defined
- db_config.port is integer
- db_config.port > 0
- db_config.port < 65536
- db_config.pool_size is integer
- db_config.pool_size > 0
fail_msg: "Structure de db_config invalide"

Pattern schema validation à la main. Pour des cas complexes, on combine plusieurs tests (is defined, is mapping, is integer).

- name: Capturer le rc de openssl
ansible.builtin.command: openssl version
register: openssl_check
changed_when: false
failed_when: false # Capturer meme en cas d echec
- name: Valider que la version est >= 3
ansible.builtin.assert:
that:
- openssl_check.rc == 0
- openssl_check.stdout is search('OpenSSL 3\\.')
fail_msg: "OpenSSL 3+ requis (vu : {{ openssl_check.stdout | default('non installe') }})"

Pattern très courant pour valider une version de binaire avant utilisation. Le failed_when: false sur le command: permet à assert: de générer le message d’erreur clair au lieu de l’erreur brute du module.

Sur des dizaines d’assertions, le bruit dans la sortie est gênant. quiet: true affiche uniquement les fail_msg: (pas les success).

- name: 50 validations silencieuses
ansible.builtin.assert:
that:
- ansible_memtotal_mb >= 1024
fail_msg: "Memoire insuffisante"
quiet: true

Sortie console plus lisible sur les playbooks de conformité (CIS Benchmark, audit RGS).

SymptômeCauseFix
Message d’erreur cryptiquePas de fail_msg: personnaliséToujours fournir un fail_msg: clair
Play s’arrête sur la première erreurComportement par défautSi désiré, ajouter ignore_errors: true ou block/rescue
fail: toujours déclenchéOubli du when:fail: sans when: = toujours échoue
Pollution des logs sur 50+ assertsPas de quiet: trueActiver quiet: true
  • assert: that: = liste de conditions, AND implicite.
  • fail_msg: / success_msg: pour personnaliser les messages.
  • fail: = échec explicite, à combiner avec when:.
  • pre_tasks: est la section idiomatique pour les assert: de validation.
  • Préférer assert: pour les préconditions, fail: pour les branches d’erreur.
  • quiet: true pour éviter la pollution sur les playbooks d’audit.

Cette page a un lab d’accompagnement : labs/modules-diagnostic/assert-fail/ dans stephrobert/ansible-training.

Challenge — sur db1.lab :

  1. fail: si inventory_hostname != 'db1.lab'.
  2. assert: sur OS, version, mémoire.
  3. Marker de succès si validations OK.

Validation pytest+testinfra :

Fenêtre de terminal
ansible-playbook solution.yml
pytest -v labs/modules-diagnostic/assert-fail/challenge/tests/

3 tests vérifient le marker et son contenu après validation réussie.

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn