Loop Basics
Every task you’ve written in this course so far has done one thing, once. loop: runs the same task repeatedly, once per item in a list — the difference between writing three nearly-identical tasks by hand and writing one task that handles all three.
Why Loop At All?
Without a loop, doing the same thing to three different values means either three separate, nearly-duplicated tasks, or one task hardcoded to a single value you’d have to edit by hand every time it needs to change. Neither scales — a loop is a task written once, applied to however many values you give it, with nothing to duplicate or hand-edit as that list grows.
Basic loop: With A List
- name: Basic Loop Demo
hosts: servers
tasks:
- name: Print each fruit
ansible.builtin.debug:
msg: "Fruit: {{ item }}"
loop:
- apple
- banana
- cherryok: [ubuntu] => (item=apple) => {
"msg": "Fruit: apple"
}
ok: [ubuntu] => (item=banana) => {
"msg": "Fruit: banana"
}
ok: [ubuntu] => (item=cherry) => {
"msg": "Fruit: cherry"
}One task, written once — three separate executions, one per list item, each reported individually.
The item Variable
item is automatically set to the current value on each pass through the loop — the loop equivalent of how a registered variable holds a different value per host. Inside the task, {{ item }} always refers to whichever value is currently being processed.
A Gotcha: Task Name is Not Repeated In a Loop
Task Name is print once and only one which is not repeated in a loop. So don’t try to use {{ item }} in task name. This is because item is not defined when it prints the name.
- name: "Print fruit: {{ item }}"
ansible.builtin.debug:
msg: "Fruit: {{ item }}"
loop:
- apple
- banana
- cherryThe task still runs. But you will see:
TASK [Print fruit: << error 1 - 'item' is undefined >>]A Brief Note On with_items
You’ll see with_items: in older playbooks — it’s the original, legacy way to write a loop, predating loop:. It still works, but loop: is the current, recommended form, and it’s what this entire section uses. The full with_X family (with_items, with_dict, and several others) gets its own dedicated chapter later in this section, once you’ve seen enough of what loop: can do to properly appreciate the comparison — including exactly how to migrate an old with_X loop to its loop: equivalent.
Best Practices
- Use
loop:, notwith_items, in anything you’re writing now — the legacy form still works, but isn’t where new code should start.