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 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