Aller au contenu
English
English
Infrastructure as Code medium

failed_when et changed_when Ansible : redéfinir succès et changement

70 min de lecture

Logo Ansible

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.

  • failed_when: <expression> redéfinit ce qui constitue un échec
  • changed_when: <expression> redéfinit le statut changed
  • changed_when: false = ne jamais marquer changed (commandes de lecture)
  • Combinaison : un script qui retourne 0/1/2 mappé sur ok/changed/failed
  • Différence avec block/rescue (gestion d'erreur structurée)
- 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: true

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

- name: Lire la version (lecture pure, pas de changement)
ansible.builtin.command: /usr/bin/myapp --version
register: version_result
changed_when: false # ← jamais changed

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

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 == 1

PLAY RECAP attendu :

rc retournéStatut Ansible
0ok=1 changed=0
1ok=1 changed=1
2failed=1

Vous mappez la sémantique du script sur la sémantique Ansible, sans réécrire le script.

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.

- 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 != 0

check_mode: false permet à la lecture de fonctionner même quand le playbook tourne en --check (sinon command: est skippé).

- 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 == 0

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

- 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 GiB

Le test échoue uniquement si l'espace libre est < 1 GiB. Sinon, ok (sans changement).

SymptômeCauseFix
failed_when: rc != 0 ne fonctionne pasOubli du result.failed_when: result.rc != 0
changed_when: false ne s'applique pasLa tâche n'a pas de register:Pas grave : changed_when: false marche aussi sans register
failed_when: complexe avec multiple conditionsUne longue expression OR/AND illisiblePasser une liste : toutes les conditions doivent être vraies (AND implicite)
failed_when: masque une vraie erreurExpression trop laxisteTester avec --check et lire les sorties
changed_when: rc == 1 mais module ignoreLe module n'expose pas rc (c'est command/shell qui l'ont)Vérifier la doc du module
MécanismeQuand utiliser
failed_when: / changed_when:Redéfinir la sémantique d'une tâche
block / rescue / alwaysCapturer 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.

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  • failed_when: <expr> redéfinit ce qui constitue un échec (sur result.* capturé par register).
  • changed_when: <expr> redéfinit le statut changed ; changed_when: false = jamais changed (lectures pures).
  • Pattern type : failed_when: result.rc not in [0, 1] + changed_when: result.rc == 1 pour un script à 3 codes retour.
  • check_mode: false + changed_when: false = lecture qui marche en --check.
  • block/rescue/always est complémentaire : failed_when: redéfinit, block/rescue capture.

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

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

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens ce site gratuitement, sans publicité, sans profilage et sans compte à créer. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn