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!