
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”.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- 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:, etfailed_when:selon le contexte.
Prérequis
Section intitulée « Prérequis »- Connaître les tests Jinja2 (cf. Lab 28).
assert: simple
Section intitulée « assert: simple »- 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_msgaffiché, play continue. - Si une condition échoue →
fail_msgaffiché, play failed.
that: accepte une liste de conditions = AND implicite.
fail: explicite avec when:
Section intitulée « fail: explicite avec when: »- 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 < 9fail: est une tâche qui échoue toujours quand elle s’exécute. when: la conditionne.
assert: vs fail: : quand préférer chaque
Section intitulée « assert: vs fail: : quand préférer chaque »# 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 < 9Pré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”).
Pattern précondition de play (pre_tasks)
Section intitulée « Pattern précondition de play (pre_tasks) »- 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 deploypre_tasks: est la section dédiée aux préconditions. Si une assert: failed dedans, les tasks: ne tournent pas. Pattern fail-fast propre.
Tests Jinja2 dans assert:
Section intitulée « Tests Jinja2 dans assert: »- 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).
Pattern command + register + assert
Section intitulée « Pattern command + register + assert »- 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.
quiet: true pour les asserts en cascade
Section intitulée « quiet: true pour les asserts en cascade »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: trueSortie console plus lisible sur les playbooks de conformité (CIS Benchmark, audit RGS).
Pièges courants
Section intitulée « Pièges courants »| Symptôme | Cause | Fix |
|---|---|---|
| Message d’erreur cryptique | Pas de fail_msg: personnalisé | Toujours fournir un fail_msg: clair |
| Play s’arrête sur la première erreur | Comportement par défaut | Si 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+ asserts | Pas de quiet: true | Activer quiet: true |
À retenir
Section intitulée « À retenir »assert: that:= liste de conditions, AND implicite.fail_msg:/success_msg:pour personnaliser les messages.fail:= échec explicite, à combiner avecwhen:.pre_tasks:est la section idiomatique pour lesassert:de validation.- Préférer
assert:pour les préconditions,fail:pour les branches d’erreur. quiet: truepour éviter la pollution sur les playbooks d’audit.
Pratiquer dans le lab
Section intitulée « Pratiquer dans le lab »Cette page a un lab d’accompagnement : labs/modules-diagnostic/assert-fail/ dans stephrobert/ansible-training.
Challenge — sur db1.lab :
fail:siinventory_hostname != 'db1.lab'.assert:sur OS, version, mémoire.- Marker de succès si validations OK.
Validation pytest+testinfra :
ansible-playbook solution.ymlpytest -v labs/modules-diagnostic/assert-fail/challenge/tests/3 tests vérifient le marker et son contenu après validation réussie.