
Par défaut, Ansible considère qu'une tâche a échoué si le module retourne rc != 0, et qu'elle est changed selon une logique propre à chaque module. failed_when: et changed_when: permettent de redéfinir ces deux statuts à partir d'une expression Jinja2 sur la sortie capturée par register:. C'est l'outil principal pour rendre idempotent un command: ou shell: qui retourne un code custom, pattern indispensable quand on wrappe un script maison.
Cette page couvre les deux directives, leur combinaison, et les patterns courants de RHCE.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »failed_when: <expression>redéfinit ce qui constitue un échecchanged_when: <expression>redéfinit le statutchangedchanged_when: false= ne jamais marquer changed (commandes de lecture)- Combinaison : un script qui retourne
0/1/2mappé surok/changed/failed - Différence avec
block/rescue(gestion d'erreur structurée)
Prérequis
Section intitulée « Prérequis »- Avoir lu register et set_fact ;
- Avoir lu Conditions, when.
failed_when:, redéfinir l'échec
Section intitulée « failed_when:, redéfinir l'échec »- name: Commande qui retourne rc=1 (mais ce n'est pas un échec) ansible.builtin.command: /usr/local/bin/check.sh register: result failed_when: result.rc not in [0, 1]Sans failed_when:, rc=1 ferait planter le play. Avec, seuls les rc ≥ 2 sont considérés failed.
L'expression a accès à toutes les variables Ansible et à result.* (le module register'ed).
changed_when:, redéfinir changed
Section intitulée « changed_when:, redéfinir changed »- name: Lire la version (lecture pure, pas de changement) ansible.builtin.command: /usr/bin/myapp --version register: version_result changed_when: false # ← jamais changedchanged_when: false est la forme la plus utilisée, sur une commande de lecture, on ne veut pas polluer le PLAY RECAP avec des changed=N artificiels.
Combinaison, script à 3 codes retour
Section intitulée « Combinaison, script à 3 codes retour »Pattern classique : un script qui retourne :
0: tout va bien, rien à faire (ok)1: différence détectée et corrigée (changed)2+: erreur (failed)
- name: Lancer le script de check/repair ansible.builtin.command: /usr/local/bin/check-and-repair.sh register: result failed_when: result.rc not in [0, 1] changed_when: result.rc == 1PLAY RECAP attendu :
| rc retourné | Statut Ansible |
|---|---|
0 | ok=1 changed=0 |
1 | ok=1 changed=1 |
2 | failed=1 |
Vous mappez la sémantique du script sur la sémantique Ansible, sans réécrire le script.
Cas pratique, validé sur le lab (lab ecrire-code/failed-when-changed-when)
Section intitulée « Cas pratique, validé sur le lab (lab ecrire-code/failed-when-changed-when) »Voici l'exemple validé sur le lab 23-ecrire-code-failed-when-changed-when :
- name: Commande qui retourne rc 1 ansible.builtin.command: /bin/sh -c 'exit 1' register: result failed_when: result.rc not in [0, 1] changed_when: result.rc == 1
- name: Marqueur post tache (preuve que la 1ère n'a pas plante) ansible.builtin.copy: dest: /tmp/failed-when-result.txt content: "rc={{ result.rc }} ok=changed\n"PLAY RECAP : ok=2 changed=2 failed=0.
La 1ère tâche est changed (rc=1), pas failed, donc la 2ème tâche tourne et pose le marqueur.
Patterns courants
Section intitulée « Patterns courants »Pattern 1, Commande de lecture (idempotent)
Section intitulée « Pattern 1, Commande de lecture (idempotent) »- name: Lire l'état d'une feature ansible.builtin.command: /usr/bin/feature-cli status register: feature_status changed_when: false check_mode: false # exécuter même en --check failed_when: feature_status.rc != 0check_mode: false permet à la lecture de fonctionner même quand le playbook tourne en --check (sinon command: est skippé).
Pattern 2, Commande qui peut être déjà fait
Section intitulée « Pattern 2, Commande qui peut être déjà fait »- name: Initialiser la base si pas déjà fait ansible.builtin.command: pg_ctl initdb -D /var/lib/pgsql/data register: init_result failed_when: - init_result.rc != 0 - "'already exists' not in init_result.stderr" changed_when: init_result.rc == 0Si le pg_ctl initdb échoue avec 'already exists' dans stderr, c'est en fait un succès (déjà initialisé). On le détecte et on n'échoue pas.
Pattern 3, Vérifier une condition métier
Section intitulée « Pattern 3, Vérifier une condition métier »- name: Vérifier l'espace disque libre ansible.builtin.command: df -B1 --output=avail / register: disk_result changed_when: false failed_when: disk_result.stdout_lines[1] | int < 1073741824 # 1 GiBLe test échoue uniquement si l'espace libre est < 1 GiB. Sinon, ok (sans changement).
Pièges fréquents
Section intitulée « Pièges fréquents »| Symptôme | Cause | Fix |
|---|---|---|
failed_when: rc != 0 ne fonctionne pas | Oubli du result. | failed_when: result.rc != 0 |
changed_when: false ne s'applique pas | La tâche n'a pas de register: | Pas grave : changed_when: false marche aussi sans register |
failed_when: complexe avec multiple conditions | Une longue expression OR/AND illisible | Passer une liste : toutes les conditions doivent être vraies (AND implicite) |
failed_when: masque une vraie erreur | Expression trop laxiste | Tester avec --check et lire les sorties |
changed_when: rc == 1 mais module ignore | Le module n'expose pas rc (c'est command/shell qui l'ont) | Vérifier la doc du module |
Différence avec block/rescue
Section intitulée « Différence avec block/rescue »| Mécanisme | Quand utiliser |
|---|---|
failed_when: / changed_when: | Redéfinir la sémantique d'une tâche |
block / rescue / always | Capturer une erreur et réagir (cleanup, rollback, notification) |
Les deux sont complémentaires : on peut avoir un block qui contient une tâche avec failed_when:, et un rescue qui se déclenche si failed_when: retourne quand même true.
À retenir
Section intitulée « À retenir »failed_when: <expr>redéfinit ce qui constitue un échec (surresult.*capturé par register).changed_when: <expr>redéfinit le statutchanged;changed_when: false= jamais changed (lectures pures).- Pattern type :
failed_when: result.rc not in [0, 1]+changed_when: result.rc == 1pour un script à 3 codes retour. check_mode: false+changed_when: false= lecture qui marche en--check.block/rescue/alwaysest complémentaire :failed_when:redéfinit, block/rescue capture.
Pratiquer dans le lab
Section intitulée « Pratiquer dans le lab »Cette page a un lab d'accompagnement : labs/ecrire-code/failed-when-changed-when/ dans
stephrobert/ansible-training. Il contient
un README.md guidé, un Makefile (make verify lance les tests), et un
challenge final auto-évalué : redéfinir failed_when et changed_when sur une commande qui retourne rc=1.
Une fois le lab provisionné :
cd ~/Projets/ansible-training/labs/ecrire-code/failed-when-changed-when/
cat README.md # tuto pas à pascat challenge/README.md # consigne du challenge finalpytest -v challenge/tests/ # lancer les tests testinfraSi les tests passent, vous maîtrisez les concepts couverts dans ce guide. En
cas de blocage, docs/troubleshooting.md
à la racine du repo couvre les pièges fréquents (rate-limit SSH, clé absente,
collection manquante).