Aller au contenu
Infrastructure as Code medium

Block / rescue / always Ansible : try/catch/finally pour playbooks

11 min de lecture

Logo Ansible

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.

  • 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)
- 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/true

L’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) — toujours

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

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'erreur
3. always: ← TOUJOURS exécuté (succès ou échec)
- tâche finalisation

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

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

- 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: POST
- 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 defined

always: garantit que le tempdir est supprimé, même si le command: plante.

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

MécanismeUsage
block / rescue / alwaysGestion d’erreur structurée (try/catch/finally)
ignore_errors: trueIgnorer une erreur ponctuelle (à éviter)
failed_when: <expr>Redéfinir ce qui constitue un échec
any_errors_fatal: trueArrê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.

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"
SymptômeCauseFix
rescue: exécuté même quand block réussitFaux : rescue ne s’exécute QUE si block échoueVérifier le code, ce n’est pas le comportement réel
always: non exécuté quand block échoueFaux : always s’exécute toujoursVérifier qu’il y a bien une section always:
Une tâche qui échoue dans rescue: plante le playComportement attendu : si rescue échoue, le play planteAjouter 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 alwaysVérifier la portée
failed=0 dans le PLAY RECAP malgré une erreurLe rescue a capturé — rescued=1 apparaît à la placeLire correctement le PLAY RECAP
  • block / rescue / always = try / catch / finally Ansible.
  • 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: true dans 99 % des cas.

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

Fenêtre de terminal
cd ~/Projets/ansible-training/labs/ecrire-code/block-rescue-always/
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