Skip to content

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

basic_loop.yaml
- name: Basic Loop Demo
  hosts: servers
  tasks:
    - name: Print each fruit
      ansible.builtin.debug:
        msg: "Fruit: {{ item }}"
      loop:
        - apple
        - banana
        - cherry
ok: [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
        - cherry

The 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:, not with_items, in anything you’re writing now — the legacy form still works, but isn’t where new code should start.
Last updated on