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