Aller au contenu
English
Infrastructure as Code medium

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

85 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:
- name: Démontrer l'enchaînement block, rescue et always
block:
- name: Tâche A
ansible.builtin.command: /bin/true
changed_when: false
- name: Tâche B (peut échouer)
ansible.builtin.command: /bin/false
changed_when: false
- name: Tâche C (skippée si B échoue)
ansible.builtin.command: /bin/true
changed_when: false
rescue:
- name: Notifier l'erreur
ansible.builtin.debug:
msg: "Le block a échoué !"
always:
- name: Cleanup
ansible.builtin.command: /bin/true
changed_when: false

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

Voici l'exemple validé sur le lab ecrire-code-block-rescue-always :

- name: Challenge block rescue always
hosts: db1.lab
become: true
tasks:
- name: Déclencher volontairement le rescue
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"
mode: "0644"
always:
- name: Marqueur always
ansible.builtin.copy:
dest: /tmp/challenge-always.txt
content: "always executed\n"
mode: "0644"

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

- name: Migrer la base avec rollback en cas d'échec
block:
- name: Snapshotter la base
ansible.builtin.command: btrfs subvolume snapshot /var/lib/pgsql /var/lib/pgsql/.snap
changed_when: true
- name: Lancer la migration
ansible.builtin.command: /usr/local/bin/migrate.sh
changed_when: true
rescue:
- name: Restaurer la base depuis le snapshot
ansible.builtin.command: btrfs subvolume rollback /var/lib/pgsql/.snap
changed_when: true
- name: Notifier l'équipe
ansible.builtin.uri:
url: https://slack.example.com/...
method: POST
- name: Travailler dans un répertoire temporaire nettoyé quoi qu'il arrive
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 }}"
changed_when: true
always:
- name: Toujours nettoyer le tempdir
when: tmpdir.path is defined
ansible.builtin.file:
path: "{{ tmpdir.path }}"
state: absent

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

- name: Installer la pile applicative sur les distributions RHEL
when: ansible_os_family == "RedHat"
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

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 :

- name: Exécuter un groupe de tâches en root
become: true
vars:
pkg_version: "1.2"
block:
- name: Tâche en root
ansible.builtin.dnf:
name: foo
- name: Autre tâche en root
ansible.builtin.systemd:
name: foo
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

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

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

Un rollback ne se prouve qu'en provoquant l'échec. Le lab vous fait grouper des tâches dans un block, déclencher volontairement une erreur pour entrer dans rescue et y écrire la compensation, puis vérifier que always nettoie dans les deux cas. Vous y lisez ansible_failed_task pour identifier la tâche qui a cassé, information que le PLAY RECAP seul ne donne pas.

  • ignore_errors : la directive que block/rescue remplace presque toujours, avec ses rares cas légitimes.
  • any_errors_fatal : le cran au-dessus, quand une erreur sur un seul hôte doit arrêter tout le play.
  • Quiz Écrire du code : 30 questions pour situer votre niveau sur la gestion d'erreurs et le reste du module.

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