How to Use Ansible Blocks

Blocks are a powerful feature in Ansible that allows you to group tasks together and apply common attributes, such as when conditions or become directives, to all tasks within the block.

This can make your Ansible playbooks more readable and maintainable.

With blocks, you can also gracefully handle errors, escalate privileges for multiple tasks at once, and organize tasks hierarchically with nested blocks.

In this tutorial, we'll explore how to use Ansible blocks with practical examples.

Basic Syntax of Ansible Blocks

The basic syntax for an Ansible block is straightforward. Here’s an example:

- name: Example block
  block:
    - name: Task 1
      ansible.builtin.command: echo "This is task 1"

    - name: Task 2
      ansible.builtin.command: echo "This is task 2"

In this example, both tasks are grouped within a block. You can apply common attributes to the entire block, such as when, become, or rescue.

Using Blocks with Conditional Execution

Imagine you want to execute a group of tasks only when a certain condition is met. You can apply the condition to the entire block instead of each individual task.

Create a playbook with the following content.

- name: Conditional block example
  hosts: localhost
  vars:
    run_tasks: true
  tasks:
    - name: Execute tasks if condition is met
      block:
        - name: Task 1
          ansible.builtin.command: echo "This is task 1"

        - name: Task 2
          ansible.builtin.command: echo "This is task 2"
      when: run_tasks

In this playbook, we define a variable run_tasks and set it to true. We then use a block to group two tasks. The when condition is applied to the block, so both tasks will only run if run_tasks is true.

This approach simplifies the playbook by applying the condition once to the block instead of individually to each task.

Run the above playbook.

ansible-playbook conditional_block_example.yml

This will run the block of tasks only if the condition run_tasks is true.

PLAY [Conditional block example] ***********************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [Execute tasks if condition is met] ***************************************
skipping: [localhost]

TASK [Task 1] ******************************************************************
ok: [localhost] => {
    "changed": false,
    "msg": "This is task 1"
}

TASK [Task 2] ******************************************************************
ok: [localhost] => {
    "changed": false,
    "msg": "This is task 2"
}

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

Using Blocks with Error Handling

You can use blocks to handle errors gracefully by adding rescue and always sections.

Let's create a playbook.

- name: Error handling block example
  hosts: localhost
  tasks:
    - name: Block with error handling
      block:
        - name: Task that might fail
          ansible.builtin.command: /bin/false

        - name: Task that won't be executed
          ansible.builtin.command: echo "This won't run"

      rescue:
        - name: Handle the error
          ansible.builtin.command: echo "An error occurred"

      always:
        - name: This always runs
          ansible.builtin.command: echo "This runs always"

In this playbook, the block section contains tasks that might fail. The rescue section contains tasks to run if any task in the block fails, and the always section contains tasks that will run regardless of success or failure.

This structure allows you to manage errors gracefully and ensure certain actions are always performed.

Now, run the above playbook.

ansible-playbook error_handling_block_example.yml

This will handle errors and ensure certain tasks always run.

PLAY [Error handling block example] ********************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [Block with error handling] ***********************************************
fatal: [localhost]: FAILED! => {"changed": false, "cmd": "/bin/false", "rc": 1}

TASK [Handle the error] ********************************************************
ok: [localhost] => {
    "changed": false,
    "msg": "An error occurred"
}

TASK [This always runs] ********************************************************
ok: [localhost] => {
    "changed": false,
    "msg": "This runs always"
}

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=1    ignored=0

Using Blocks with Privilege Escalation

You can use blocks to apply privilege escalation (become) to multiple tasks at once.

Create a playbook with the following content.

- name: Privilege escalation block example
  hosts: localhost
  tasks:
    - name: Block with privilege escalation
      block:
        - name: Task 1 as root
          ansible.builtin.command: echo "Task 1 as root"

        - name: Task 2 as root
          ansible.builtin.command: echo "Task 2 as root"
      become: yes

In this playbook, the become: yes directive is applied to the block, so both tasks will run with elevated privileges. This is useful when you need multiple tasks to run as a different user, reducing redundancy by applying the privilege escalation to the entire block.

Now, let's run this playbook.

ansible-playbook privilege_escalation_block_example.yml

This playbook executes the tasks in the block with root privileges.

PLAY [Privilege escalation block example] **************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [Block with privilege escalation] *****************************************
ok: [localhost] => {
    "changed": false,
    "msg": "Task 1 as root"
}

ok: [localhost] => {
    "changed": false,
    "msg": "Task 2 as root"
}

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Nested Blocks

You can nest blocks within other blocks to create complex task structures. This can help organize tasks into logical groups and apply specific attributes to different levels of the hierarchy.

Create a playbook and add the following content.

- name: Nested blocks example
  hosts: localhost
  tasks:
    - name: Outer block
      block:
        - name: Task in outer block
          ansible.builtin.command: echo "This is in the outer block"

        - name: Inner block
          block:
            - name: Task in inner block
              ansible.builtin.command: echo

In this playbook, the outer block contains another block within it. The outer block has a task that echoes a message indicating it is in the outer block. The inner block is nested within the outer block and contains a task that echoes a message indicating it is in the inner block.

Run the above playbook.

ansible-playbook nested_blocks_example.yml

You will get the following output.

PLAY [Nested blocks example] ***************************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [Outer block] *************************************************************
ok: [localhost] => {
    "changed": false,
    "msg": "This is in the outer block"
}

TASK [Inner block] *************************************************************
ok: [localhost] => {
    "changed": false,
    "msg": "This is in the inner block"
}

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Using Blocks with Handlers

You can use blocks to notify handlers, which can be useful for managing tasks that need to be executed conditionally based on previous task outcomes.

Create a playbook with the following content:

- name: Blocks with handlers example
  hosts: localhost
  tasks:
    - name: Block with handler notification
      block:
        - name: Task that changes something
          ansible.builtin.command: echo "This changes something"
          notify: Restart service

        - name: Another task
          ansible.builtin.command: echo "This is another task"
      notify: Restart service

  handlers:
    - name: Restart service
      ansible.builtin.command: echo "Service restarted"

In this example, the block contains two tasks, both of which notify the handler Restart service. The handler Restart service is defined to echo a message indicating the service has been restarted.

Now, run this playbook.

ansible-playbook blocks_with_handlers_example.yml

This playbook use blocks to notify handlers, ensuring that specific tasks run based on the outcomes of tasks within the block.

PLAY [Blocks with handlers example] ********************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [Block with handler notification] *****************************************
ok: [localhost] => {
    "changed": false,
    "msg": "This changes something"
}

ok: [localhost] => {
    "changed": false,
    "msg": "This is another task"
}

RUNNING HANDLER [Restart service] **********************************************
ok: [localhost] => {
    "changed": false,
    "msg": "Service restarted"
}

PLAY RECAP *********************************************************************
localhost                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Conclusion

In this article, you explored the basics of using Ansible blocks, including their syntax and practical applications. You saw how blocks can simplify conditional execution, error handling, privilege escalation, nested structures, and handler notifications in your playbooks. By using blocks, you can make your Ansible playbooks more organized and easier to maintain.

If you are new to Ansible and want to learn it from scratch, our Ansible tutorial series will be of great help. It's written for RHCE exam but it helps you the same whether you are preparing for the exam or not.

RHCE Ansible EX294 Exam Preparation Course
Learn all the essentials of Ansible with this hands-on tutorial series. It is ideal for RHCE Ansible EX294 exam preparation.
✍️
Author: Hitesh Jethwa has more than 15+ years of experience with Linux system administration and DevOps. He likes to explain complicated topics in easy to understand way.