Handle Failures with Blocks and Rescue

AnsibleBeginner
Practice Now

Introduction

Ansible blocks group related tasks. A rescue section runs when a task in the block fails, and an always section runs in either case.

In this lab, you will create a controlled failure, recover from it, then rerun the same playbook with the failure disabled to see the success path.

Create a Failure-Handling Project

In this step, you will create a small project with one local managed host and a reports directory for block, rescue, and always markers.

cd /home/labex/project
mkdir -p rhce-ansible/{inventory,playbooks,reports}
cd rhce-ansible
cat > ansible.cfg <<'EOF'
[defaults]
inventory = ./inventory/hosts
host_key_checking = False
retry_files_enabled = False
EOF
cat > inventory/hosts <<'EOF'
[managed]
host ansible_connection=local
EOF

The reports directory is where the playbook writes markers for each path. That makes block behavior visible without relying only on the terminal output.

Write a Block with Rescue and Always

In this step, you will write a playbook with a controlled failure. The block fails when simulate_failure is true, the rescue section writes a recovery marker, and the always section writes an audit marker. Before running commands in this step, return to the Ansible project directory. This keeps the commands stable even when LabEx runs each step separately.

cd /home/labex/project/rhce-ansible
cat > playbooks/block-rescue.yml <<'EOF'
---
- name: Practice block rescue and always
  hosts: managed
  gather_facts: false
  vars:
    simulate_failure: true
  tasks:
    - name: Remove old markers
      ansible.builtin.file:
        path: "/home/labex/project/rhce-ansible/reports/{{ item }}.txt"
        state: absent
      loop:
        - block-success
        - rescued
        - always

    - name: Protect a risky operation with rescue
      block:
        - name: Run a command that may fail
          ansible.builtin.command: /usr/bin/false
          when: simulate_failure | bool

        - name: Mark block success
          ansible.builtin.copy:
            dest: /home/labex/project/rhce-ansible/reports/block-success.txt
            content: |
              block completed
            mode: '0644'
      rescue:
        - name: Mark rescue path
          ansible.builtin.copy:
            dest: /home/labex/project/rhce-ansible/reports/rescued.txt
            content: |
              rescued controlled failure
            mode: '0644'
      always:
        - name: Mark always path
          ansible.builtin.copy:
            dest: /home/labex/project/rhce-ansible/reports/always.txt
            content: |
              always ran
            mode: '0644'
EOF

The risky command is /usr/bin/false, which always exits with failure. When simulate_failure is true, the block stops at that failing task, the rescue section writes rescued.txt, and the always section writes always.txt either way.

ansible-playbook playbooks/block-rescue.yml --syntax-check

The block, rescue, and always keywords must align at the same indentation level. A syntax check is a quick way to catch a misplaced section before running the controlled failure.

Run the Controlled Failure Path

In this step, you will run the playbook with its default failure enabled. The play recap should show a rescued task instead of a failed play. Before running commands in this step, return to the Ansible project directory. This keeps the commands stable even when LabEx runs each step separately.

cd /home/labex/project/rhce-ansible
ansible-playbook playbooks/block-rescue.yml | tee reports/failure-path.txt

This run is expected to show rescued=1 and failed=0 in the recap. That means Ansible saw a task fail inside the block, ran the rescue tasks, and did not treat the overall play as failed.

cat reports/rescued.txt reports/always.txt

The two marker files should say rescued controlled failure and always ran. You should not see block-success.txt in this path because the block did not reach the success marker task.

Run the Success Path with an Extra Variable

In this step, you will override simulate_failure at the command line. The block should complete normally, rescue should not run, and always should still run. Before running commands in this step, return to the Ansible project directory. This keeps the commands stable even when LabEx runs each step separately.

cd /home/labex/project/rhce-ansible
ansible-playbook playbooks/block-rescue.yml -e simulate_failure=false | tee reports/success-path.txt

The -e simulate_failure=false option overrides the playbook variable for this run only. Now the false command is skipped, the normal block reaches block-success.txt, and the rescue section should not run.

cat reports/block-success.txt reports/always.txt

The success path should show block completed and always ran. The playbook removes old markers at the start, so rescued.txt should no longer exist after this run.

Compare Failure and Success Evidence

In this step, you will compare the two saved playbook runs. Keeping the evidence small helps you troubleshoot which path ran without reading the entire output. Before running commands in this step, return to the Ansible project directory. This keeps the commands stable even when LabEx runs each step separately.

cd /home/labex/project/rhce-ansible
grep -E 'rescued=|failed=' reports/failure-path.txt reports/success-path.txt

The failure-path report should contain rescued=1, while the success-path report should contain rescued=0. Both should have failed=0, but for different reasons.

ls -1 reports

The report list should include both saved playbook outputs plus the markers from the most recent path. Keeping both outputs makes it easier to compare how rescue changes the recap.

Summary

You used block, rescue, and always sections to handle a controlled failure, then ran the same playbook through a normal success path and compared the saved evidence.