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