
block / rescue / always est l’équivalent Ansible de try / catch / finally. Vous groupez des tâches dans un block:, vous fournissez un rescue: qui s’exécute uniquement si une tâche du block échoue, et un always: qui s’exécute toujours (succès ou échec). Cette page explique le pattern, l’ordre d’exécution exact, et les cas pratiques de production : nettoyage, rollback, notification d’échec.
Sans block/rescue/always, la seule façon de gérer une erreur est ignore_errors: true (anti-pattern) ou failed_when: (souvent insuffisant). block/rescue/always est la gestion d’erreur structurée d’Ansible.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- La syntaxe
block: / rescue: / always: - L’ordre d’exécution : block → (rescue si échec) → always
- L’usage de
when:sur un block (propage à toutes les tâches enfants) - Patterns de production : nettoyage, rollback, notification
- La différence avec
ignore_errors:(anti-pattern)
Prérequis
Section intitulée « Prérequis »- Avoir lu Conditions — when et Boucles — loop.
La syntaxe
Section intitulée « La syntaxe »- name: Démo block / rescue / always hosts: db1.lab become: true tasks: - block: - name: Tâche A ansible.builtin.command: /bin/true
- name: Tâche B (peut échouer) ansible.builtin.command: /bin/false
- name: Tâche C (skippée si B échoue) ansible.builtin.command: /bin/true
rescue: - name: Notifier l'erreur ansible.builtin.debug: msg: "Le block a échoué !"
always: - name: Cleanup ansible.builtin.command: /bin/trueL’ordre exact :
Block exécution : Tâche A → succès Tâche B → ÉCHEC ! Block interrompu, on saute le reste Rescue exécuté (Notifier l'erreur)Always exécuté (Cleanup) — toujoursSi toutes les tâches du block réussissent : rescue n’est pas exécuté, always l’est. Si une tâche du block échoue : rescue capture, always est exécuté quoi qu’il arrive.
L’ordre d’exécution complet
Section intitulée « L’ordre d’exécution complet »1. block: ← exécuté en premier - tâche 1 - tâche 2 (si elle échoue : passage direct au rescue) - tâche 3 (skippée si tâche 2 a échoué)2. rescue: ← UNIQUEMENT si une tâche du block a échoué - tâche cleanup d'erreur3. always: ← TOUJOURS exécuté (succès ou échec) - tâche finalisationSi une tâche du rescue elle-même échoue, le play plante (sauf si failed_when: ou un autre rescue parent).
Cas pratique — capture d’échec (lab ecrire-code/block-rescue-always)
Section intitulée « Cas pratique — capture d’échec (lab ecrire-code/block-rescue-always) »Voici l’exemple validé sur le lab 22-ecrire-code-block-rescue-always :
- name: Challenge block rescue always hosts: db1.lab become: true tasks: - block: - name: Tenter une commande qui echoue ansible.builtin.command: /bin/false changed_when: false
rescue: - name: Marqueur rescue ansible.builtin.copy: dest: /tmp/challenge-rescue.txt content: "rescue triggered (block failed)\n"
always: - name: Marqueur always ansible.builtin.copy: dest: /tmp/challenge-always.txt content: "always executed\n"PLAY RECAP :
db1.lab : ok=2 changed=2 rescued=1 failed=0rescued=1 = le block a été rescuté. failed=0 = malgré l’échec du /bin/false, le play a terminé avec succès (grâce au rescue).
Patterns de production
Section intitulée « Patterns de production »Pattern 1 — Rollback en cas d’échec
Section intitulée « Pattern 1 — Rollback en cas d’échec »- block: - name: Snapshotter la base ansible.builtin.command: btrfs subvolume snapshot /var/lib/pgsql /var/lib/pgsql/.snap
- name: Lancer la migration ansible.builtin.command: /usr/local/bin/migrate.sh
rescue: - name: Restaurer la base depuis le snapshot ansible.builtin.command: btrfs subvolume rollback /var/lib/pgsql/.snap
- name: Notifier l'équipe ansible.builtin.uri: url: https://slack.example.com/... method: POSTPattern 2 — Nettoyage de fichiers temporaires
Section intitulée « Pattern 2 — Nettoyage de fichiers temporaires »- block: - name: Créer un répertoire temporaire ansible.builtin.tempfile: state: directory register: tmpdir
- name: Travailler dedans ansible.builtin.command: build.sh args: chdir: "{{ tmpdir.path }}"
always: - name: Toujours nettoyer le tempdir ansible.builtin.file: path: "{{ tmpdir.path }}" state: absent when: tmpdir.path is definedalways: garantit que le tempdir est supprimé, même si le command: plante.
Pattern 3 — when: sur un block entier
Section intitulée « Pattern 3 — when: sur un block entier »- block: - name: Tâche RHEL/AlmaLinux 1 ansible.builtin.dnf: name: foo
- name: Tâche RHEL/AlmaLinux 2 ansible.builtin.systemd: name: foo state: started
when: ansible_os_family == "RedHat"Le when: est propagé à toutes les tâches du block. Plus DRY que de répéter le when: sur chacune.
block vs autres mécanismes d’erreur
Section intitulée « block vs autres mécanismes d’erreur »| Mécanisme | Usage |
|---|---|
block / rescue / always | Gestion d’erreur structurée (try/catch/finally) |
ignore_errors: true | Ignorer une erreur ponctuelle (à éviter) |
failed_when: <expr> | Redéfinir ce qui constitue un échec |
any_errors_fatal: true | Arrêter le play dès la première erreur sur n’importe quel hôte |
block/rescue/always est de loin le plus puissant. Si vous écrivez ignore_errors: true, demandez-vous d’abord si un block ne ferait pas mieux.
Variables et block
Section intitulée « Variables et block »vars:, become:, become_user:, tags:, when: peuvent être posés au niveau du block et propagés aux tâches du block :
- block: - name: Tâche en root ansible.builtin.dnf: name: foo
- name: Autre tâche en root ansible.builtin.systemd: name: foo become: true vars: pkg_version: "1.2"Pièges fréquents
Section intitulée « Pièges fréquents »| Symptôme | Cause | Fix |
|---|---|---|
rescue: exécuté même quand block réussit | Faux : rescue ne s’exécute QUE si block échoue | Vérifier le code, ce n’est pas le comportement réel |
always: non exécuté quand block échoue | Faux : always s’exécute toujours | Vérifier qu’il y a bien une section always: |
Une tâche qui échoue dans rescue: plante le play | Comportement attendu : si rescue échoue, le play plante | Ajouter un autre rescue parent ou ignore_errors: sur la tâche du rescue |
Variable du block non visible dans rescue: | Faux : vars: du block est visible dans rescue ET always | Vérifier la portée |
failed=0 dans le PLAY RECAP malgré une erreur | Le rescue a capturé — rescued=1 apparaît à la place | Lire correctement le PLAY RECAP |
À retenir
Section intitulée « À retenir »block / rescue / always=try / catch / finallyAnsible.block:: tâches à exécuter ;rescue:: tâches si échec ;always:: tâches toujours exécutées.when:sur un block s’applique à toutes les tâches du block.- Patterns courants : rollback sur échec, cleanup dans always, notification Slack/Teams dans rescue.
- Préférer
block/rescue/alwaysàignore_errors: truedans 99 % des cas.
Pratiquer dans le lab
Section intitulée « Pratiquer dans le lab »Cette page a un lab d’accompagnement : labs/ecrire-code/block-rescue-always/ dans
stephrobert/ansible-training. Il contient
un README.md guidé, un Makefile (make verify lance les tests), et un
challenge final auto-évalué : block + rescue + always sur une commande qui échoue volontairement (/bin/false).
Une fois le lab provisionné :
cd ~/Projets/ansible-training/labs/ecrire-code/block-rescue-always/
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).