Loops
Sequence
You can run a loop with a traditional start and end sequence without the use of a list by using the with_sequence command with the start and end of the loop defined using = assignment.
- name: Run sequence
debug:
msg: ""
with_sequence: start=0 end=10
Loop Vars
You can rename the default name of items in an iterated list which is item by default using the loop_control and loop_var properties. In the example below the name item is changed to my_loop_var instead.
- name: Rename loop
debug:
msg: ""
with_items: [1, 2, 3, 4, 5]
loop_control:
loop_var: my_loop_var
Ignore Loop Cases
You can ignore cases in a loop using the failed_when command. This works with a range of conditional operators, such as >, == etc. and will ignore any cases in iteration that meet the failed_when condition.
- name: Create list
set_fact:
my_list: [1, 2, 3, 4, 5]
# Prints every item apart from 2
- name: Ignore 2
debug:
msg: ""
failed_when: item == 2
The output for this will be:
TASK [debug] ***************************************************************************************************************************************************************************
ok: [127.0.0.1] => {
"msg": "my_var"
}
TASK [debug] **************************************
ok: [127.0.0.1] => {
"msg": "1"
}
TASK [debug] **************************************
failed: [127.0.0.1] => {
"msg": "2"
}
TASK [debug] **************************************
ok: [127.0.0.1] => {
"msg": "3"
}
... etc.
fatal: [127.0.0.1]: FAILED! => {"msg": "All items completed"}
If you don’t want the entire playbook to fail once the loop completes you must append ignore_errors to these loop case command.
- name: Ignore 2
debug:
msg: ""
failed_when: item == 2
ignore_errors: yes
Nested Loops
You can take the cartesian product of two sets of lists (by nesting them) using the with_nested command. You can specify which component of the nested element you want to access by appending a . to the item variable. This specification is zero indexed. So items in the first list are indexed at .0 in the second list at .1 and so on.
- name: Nested list
debug:
msg: " : "
with_nested:
- [1, 2, 3]
- ["a", "b", "c"]
# => "1 : a", "1 : b", "1 : c", "2 : a", "2 : b" etc...
You can create a nested loop using the Jinja filter product to generate a cartesian product of two lists in conjunction with the loop command, however this could just as easily be used in other places to generate an n-dimensional list. Importantly, the first list is passed via a | pipe into the product filter which takes a comma separated set of spread arguments of other lists to combine it with, from 1 to N.
- name: create list
debug:
msg: " : "
loop: ""
vars:
x: [1, 2, 3]
y: ["a", "b", "c"]
z: ["*", "&", "^"
Traversing Lists inside Objects
You can traverse lists nested as properties inside objects (or dicts) by using the with_subelements command. This takes a list of objects as its first input (which must be using handlebars format) and then the name of the key to traverse through which should be a list. The loop will then output the original object and each element in the nested collection. The collection elements are available at the item.1 position and the original object at item.0.
- name: Define objects
set_fact:
objects:
- { key_1: 0, key_2: ['a', 'b'] }
- { key_1: 1, key_2: ['y', 'z'] }
- name: Iterate through property collections
debug:
msg: ""
with_subelements:
- "" # name of the collection of objects
- key_2 # name of the key which is a collection inside the individual objects
# => a, b, y, z
Looping over a set of tasks
You can loop over a set of multiple tasks with a collection by placing the tasks you want to loop over in a separate file and using an include on them with the loop command. The items of the collection will be available inside the included file as `` and each item from the collection will be subbed into the included on each iteration of the loop. The outer run task would be:
- name: Create list
set_fact:
my_list:
- a
- b
- c
- name: Run set of tasks
include: tasks.yml
loop: ""
And the corresponding included task would be:
# tasks.yml
- name: Do something with the list items
some_command: ""
- name: Do something else with list items
another_command: ""
- debug:
msg: "List item is "
You can replace the loop command with the with_items command and the functionality will be identical.
You can also alias the names of items that you pass into the included playbook so that you don’t have to use item by including an assignment after the name of file that is to be included with the item property from the list assigned to a new name.
- name: Create list
set_fact:
my_list:
- a
- b
- c
- name: Run set of tasks
# assignment to a new name here
include: tasks.yml letter=
loop: ""
The corresponding include file (truncated for ease of reading):
# tasks.yml
- debug:
msg: "List item is "
Looping over tightly coupled tasks using retries
You can loop over a group of tightly coupled tasks that may fail using retries delay by recursively calling an include on a task using the block and failure commands. The example below is taken from Jeff Martin’s blog. You can also define the max_retries, retry_delay and retry_count as facts outside of original recursive include.
- name: Group of tasks that are tightly coupled
vars:
max_retries: "5"
retry_delay: "10"
block:
- name: Increment the retry count
set_fact:
retry_count: "0"
- name: Some task
setting_up:
do_something: prerequisite action
- name: Some other task
setting_up:
do_something: prerequisite action
- name: Some task that might fail
failing_task:
some: setting
rescue:
- fail:
msg: Maximum retries of grouped tasks reached
when: retry_count | int == max_retries | int
- debug:
msg: "Task Group failed, let's give it another shot"
- name: Sleep between retries
wait_for:
timeout: "" # seconds
delegate_to: localhost
become: false
- include_tasks: coupled_task_group.yml