Loop Control
Looping over dict2items output produces annotations like (item={'key': 'timeout', 'value': 30}) — readable enough for a small example, genuinely noisy once items get bigger or more numerous. loop_control is the toolset for exactly that, plus two more genuinely useful controls: knowing your position in a loop, and slowing it down.
label: Cleaner Output For Complex Items
loop_control.label replaces the full item dump in output with whatever you specify instead:
- name: "Apply settings"
ansible.builtin.debug:
msg: "Would set {{ item.key }} to {{ item.value }}"
loop: "{{ settings | dict2items }}"
loop_control:
label: "{{ item.key }}"ok: [ubuntu] => (item=timeout) => {
"msg": "Would set timeout to 30"
}Compare that (item=timeout) to the full (item={'key': 'timeout', 'value': 30}) — much easier to scan, especially once a loop’s items carry several fields.
There’s a second, sharper reason to use this beyond tidiness: if an item ever contains something sensitive — a password field in a user record, say — label is one of the ways how you keep that value out of the loop’s own output annotation entirely, showing only the field you explicitly choose.
index_var: Knowing Which Iteration You’re On
index_var names a variable holding the current iteration’s position (starting at 0):
- name: Dict Loop Demo
hosts: ubuntu
vars:
users:
- alice
- bob
tasks:
- name: "User Processing"
ansible.builtin.debug:
msg: "Processing user '{{ item }}' at #{{ user_index }}"
loop: "{{ users }}"
loop_control:
index_var: user_indexTASK [User Processing] *********************
ok: [ubuntu] => (item=alice) => {
"msg": "Processing user 'alice' at #0"
}
ok: [ubuntu] => (item=bob) => {
"msg": "Processing user 'bob' at #1"
}Useful for numbering output clearly, or for conditionally acting only on a specific position — when: user_index == 0 for “just the first item,” for instance.
pause: Slowing Down Between Iterations
pause inserts a delay, in seconds, between each iteration:
loop_control:
pause: 2Worth reaching for when a loop is doing something that shouldn’t be hammered too quickly — calling a rate-limited API, or deliberately spacing out a rolling action across several hosts, for instance. Not something to add by default; only when there’s a genuine reason to throttle.
Combining loop_control Options
All three compose freely:
- name: Dict Loop Demo
hosts: ubuntu
vars:
users:
- name: alice
age: 20
- name: bob
age: 30
tasks:
- name: "Processing users"
ansible.builtin.debug:
msg: "Working on user '{{ item.name }}', age={{ item.age }}"
loop: "{{ users }}"
loop_control:
label: "{{ item.name }}"
index_var: user_index
pause: 1Best Practices
- Use
labelany time a loop’s items are large or contain sensitive fields — cleaner output, and a genuine safeguard against leaking something into logs that shouldn’t be there. - Use
index_varwhen position matters — numbering, or targeting a specific iteration by its place in the list. - Only use
pausewhen there’s a real reason to throttle — a rate limit, a deliberate rollout pace — not as a default habit.