
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- 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 < 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: 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 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.
Mettre en pratique
Section intitulée « Mettre en pratique »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.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- 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.