Looping Around the Block – Using Loops with Ansible Blocks
By William Phelps, Senior Technical Architect
It’s a real “task” to use Ansible sometimes.
In reality, everything in Ansible is a task. A task is the basic work unit within an Ansible playbook. A task is also the smallest unit of work within an Ansible script.
Each task calls an Ansible “module” to perform its intended function. Once a task is completed, the next task is called. That next task can be blissfully unaware of anything that happened in the previous task (or any previous tasks as well.) This is actually a very powerful feature at times, as different tasks can then have differing environment variables or a different user executing each task. For example, think of needing to execute one or more tasks as root, whereas the rest of the play is meant to be executed with lower privilege.
However, today’s topic initially looks at the need to execute a series of tasks as more or less a unit or “block”. Each task creates a result that subsequently needs to be passed to one or more following tasks. If a task fails, either the play stops with an error, or it needs to enter some kind of error handler.
In general programming terms, this approach is called a try/catch/finally approach. Ansible uses the terminology block/rescue/always to refer to the same thing. Here is a schematic example:
Obviously this snippet would do very little, but merely illustrates the basic premise of an Ansible block.
So now let’s further consider a use case where there this type of block needs to be executed repeatedly. For an example, there exists a list of file names in a folder. That list of files (or some attribute of each file) must be evaluated in the context of the block, and some series of actions must occur for each file.
Normally, such looping is achieved in Ansible using the with_items or loop parameter within a task. For example, this snippet installs several packages using the yum module.
Depending on the Ansible module (in this case “yum”), the module may or may not support the with_items argument or the “loop” argument. “block” is one such module in Ansible. It also does not appear that Ansible will ever add such support.
The approach to get around this limitation lies in a module called include_tasks. This module can work inside of a block, and also can accept loop arguments. For example:
All of the operations that need to looped then reside in the subtasks.yml file. A simple example could be as shown:
Once the loop is finished, the next task in the main playbook script is executed.
Don’t be thrown for a loop. This post is intended as more of a conceptual teaching post for Ansible rather than a working example. Please contact us if there is a need for working code for a real-world scenario.