How to implement Ansible blocks in our code to group and manage tasks together?

There are some limitations of using YAML instead of a scripting language or a DSL, but the developers of Ansible thought about the issue, and they integrated some cool, more advanced features into the tool. We can use blocks collecting and handling tasks together Let’s investigate this feature a bit more!

Blocks are virtual collections of tasks grouped for handling them together. With blocks we can add conditional statements for multiple tasks in one place, or we can set the become_user for the group members. We can save several lines of code, thus we reduce the complexity of our code base. On the other hand we can handle errors of a block together.

To create a block we can use the block keyword.

---
- name: Test playbook for playing with Ansible
  hosts: all

  tasks:
    - name: The first task is not in a block
      ansible.builtin.debug:
        msg: "Starting the work with tasks"

    - name: The block can have a name too
      become: true
      become_user: root
      when: ansible_facts['distribution'] == 'Debian'
      block:
        - name: The first task in the block
          ansible.builtin.debug:
            msg: "Here is the first task in the block"
        - name: The second task in the block
          ansible.builtin.debug:
            msg: "Second task in the block"

The above block will only run if we run the playbook on a Debian Linux OS, and we force become for the listed tasks. The become user is always root for these tasks.

We can improve our error handling in our blocks. There are rescue and always keywords for this.

The rescue part will run when a task in the block ran on an error.

The always part (as its name suggests) runs every time.

---
- name: Test playbook for playing with Ansible
  hosts: all

  tasks:
    - name: The first task is not in a block
      ansible.builtin.debug:
        msg: "Starting the work with tasks"

    - name: The block can have a name too
      become: true
      become_user: root
      when: ansible_facts['distribution'] == 'Debian'
      block:
        - name: The first task in the block
          ansible.builtin.debug:
            msg: "Now here is the first task in the block"
        - name: The second task in the block
          ansible.builtin.debug:
            msg: "Now here is the second task in the block"
        - name: This is an error
          ansible.builtin.command: /bin/false
        - name: This won't be ran
          ansible.builtin.debug:
            msg: "Hello"
      rescue:
        - name: It runs after the error
          ansible.builtin.debug:
            msg: "Message after an error"

As we see we can implement some fine error handling with grouping tasks with blocks.

With the always part we can implement clean up processes if a task in the block fails.

---
- name: Test playbook for playing with Ansible
  hosts: all

  tasks:
    - name: The first task is not in a block
      ansible.builtin.debug:
        msg: "Starting the work with tasks"

    - name: The block can have a name too
      become: true
      become_user: root
      when: ansible_facts['distribution'] == 'Debian'
      block:
        - name: The first task in the block
          ansible.builtin.debug:
            msg: "Now here is the first task in the block"
        - name: The second task in the block
          ansible.builtin.debug:
            msg: "Now here is the second task in the block"
        - name: This is an error stops processing the block
          ansible.builtin.command: /bin/false
        - name: This won't be ran
          ansible.builtin.debug:
            msg: "Hello, there was an error!"
      rescue:
        - name: It runs after the error
          ansible.builtin.debug:
            msg: "Here a task can try to solve the error"
      always:
        - name: This part will always run dispite errors
          ansible.builtin.debug:
            msg: "This task will always run, we can clean up here"

The above code snippet uses a block with a rescue part and an always part as well.

The block will run first. If there is an error in the block, Ansible would stop executing the whole playbook, but we have a rescue part where we try to solve the problem. The rescue part only runs when there was an error in the block. In the always part we can clean up despite the rescue was successful or not. The always part runs regardless the results of the block and rescue.

If you have anything to share then please visit my Tom’s IT Cafe Discord Server!

Leave a comment