Aller au contenu
English
English
Infrastructure as Code medium

Modules assert et fail Ansible : validation défensive

75 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 échoue → fail_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
- name: Vérifier les prérequis
ansible.builtin.assert:
that: ansible_distribution_major_version | int >= 9
fail_msg: "RHEL 9+ requis"
- name: Interrompre avec un message explicite
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: Collecter la liste des paquets installes
ansible.builtin.package_facts:
- name: Pre-requis - paquets installes
ansible.builtin.assert:
that:
- "'chrony' in ansible_facts.packages"
- "'firewalld' in ansible_facts.packages"
fail_msg: "chrony et firewalld doivent etre installes"
- 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 posé sur la commande laisse l'assertion produire le message d'erreur clair, avec la version réellement trouvée, 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).

Ces quatre symptômes se ressemblent à l'exécution, un play qui s'arrête, et ne se distinguent que par ce que la sortie affiche. Lire la colonne Cause avant de toucher au playbook évite la correction réflexe, qui consiste à neutraliser la validation au lieu de corriger la condition qu'elle exprime. Les deux premières lignes tiennent à un message absent ou à un comportement par défaut assumé, les deux suivantes à un when: oublié et à une sortie que quiet: true aurait allégée.

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

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

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

Une validation défensive ne se juge qu'à l'échec qu'elle sait produire. Le lab vous fait poser sur db1.lab un fail: qui écarte tout hôte inattendu, puis une batterie d'assert: sur la distribution, sa version majeure et la mémoire disponible, chacun avec un fail_msg: lisible. Les tests inspectent le fichier témoin écrit une fois les contrôles passés, ce qui prouve que les préconditions ont été franchies dans l'ordre plutôt que contournées.

  • Module service_facts : Le matériau des assertions de conformité : l'état running et enabled de chaque service.
  • argument_specs.yml : La validation défensive portée au niveau d'un rôle, avec types et valeurs autorisées.
  • Introduction TDD Molecule : Passer de la vérification écrite dans le playbook au test écrit avant le code.

Ce site vous est utile ?

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

Je maintiens ce site gratuitement, sans publicité, sans profilage et sans compte à créer. 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