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.