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