Aller au contenu
Infrastructure as Code medium

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

9 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]

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.

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.

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

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

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

Fenêtre de terminal
cd ~/Projets/ansible-training/labs/ecrire-code/failed-when-changed-when/
cat README.md # tuto pas à pas
cat challenge/README.md # consigne du challenge final
pytest -v challenge/tests/ # lancer les tests testinfra

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

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn