
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] changed_when: trueSans 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, les trois codes retour
Section intitulée « Cas pratique, les trois codes retour »Voici l'exemple complet, qui distingue les trois issues possibles :
- 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" mode: "0644"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.
Mettre en pratique
Section intitulée « Mettre en pratique »Redéfinir le succès d'une tâche demande de voir le code retour réel. Le lab vous fait neutraliser le changed d'une commande de lecture seule, poser un changed_when calculé sur la sortie, et accepter certains codes retour comme succès avec failed_when. Le contrôle relit le PLAY RECAP pour vérifier que les compteurs changed et failed décrivent enfin ce qui s'est passé.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- any_errors_fatal : une fois l'échec correctement défini, décider ce qu'il déclenche à l'échelle du play.
- Tests Jinja2 : les tests qui rendent vos expressions
failed_when:lisibles et robustes aux variables absentes. - Quiz Écrire du code : 30 questions pour vérifier que la sémantique d'échec et de changement est acquise.