Ansible import vs include – Loops

Ansible has 2 methods that allows you to scale your playbooks by moving tasks to other files. Those methods are import and include. On the surface, the use of these is very similar if your playbook and tasks are simple. In-fact here is an example of using import and include interchangeably.

import_a_task.yaml

---
- name: import a task
  hosts: localhost
  gather_facts: false
  tasks:
    - name: import a task
      import_tasks: subtask.yaml

include_a_task.yaml

---
- name: include a task
  hosts: localhost
  gather_facts: false
  tasks:
    - name: include a task
      include_tasks: subtask.yaml

subtask.yaml

---
- name: Debug some stuff
  debug:
    msg: "This is a subtask"

The output of the two even looks very similar.

ansible-playbook import_a_task.yaml

PLAY [import a task] ***************************************************************************************************************************************************************************************************
TASK [debug] ***********************************************************************************************************************************************************************************************************ok: [localhost] => {
    "msg": "This is a subtask"
}

PLAY RECAP *************************************************************************************************************************************************************************************************************localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

You’ll notice the output is similar but the include task contains a few extra lines that show which task was included and ran.

ansible-playbook include_a_task.yaml

PLAY [include a task] **************************************************************************************************************************************************************************************************
TASK [include a task] **************************************************************************************************************************************************************************************************included: /mnt/c/Users/justin/lab-ansible/subtask.yaml for localhost

TASK [debug] ***********************************************************************************************************************************************************************************************************ok: [localhost] => {
    "msg": "This is a subtask"
}

PLAY RECAP *************************************************************************************************************************************************************************************************************localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

The difference between import and include become apparent when we look at using loops, tags, and conditionals. Let’s first look at how each behaves when we use loops. If we change our playbooks to the following we can see the behavior of each when there is a loop applied to the outermost task.

import_a_task.yaml

---
- name: import a task
  hosts: localhost
  gather_facts: false
  tasks:
    - name: import a task
      import_tasks: subtask.yaml
      loop:
        - 123
        - 456
        - 789

include_a_task.yaml

---
- name: include a task
  hosts: localhost
  gather_facts: false
  tasks:
    - name: include a task
      include_tasks: subtask.yaml
      loop:
        - 123
        - 456
        - 789

subtask.yaml

---
- name: Debug some stuff
  debug:
    msg: "{{ item }}"

Upon running the import_a_task.yaml and include_a_task.yaml we can see that the import_a_task.yaml fails to execute all together but the include_a_task.yaml includes and executes the subtask one time for each of the items in the list. The import_a_task even tells us that we cannot use loops on ‘import_tasks’ statements and that we should instead use ‘include_tasks’.

ansible-playbook import_a_task.yaml 

ERROR! You cannot use loops on 'import_tasks' statements. You should use 'include_tasks' instead.

The error appears to be in '/mnt/c/Users/justin/lab-ansible/import_a_task.yaml': line 6, column 7, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

  tasks:
    - name: import a task
      ^ here
ansible-playbook include_a_task.yaml

PLAY [include a task] **************************************************************************************************************************************************************************************************
TASK [include a task] **************************************************************************************************************************************************************************************************included: /mnt/c/Users/justin/lab-ansible/subtask.yaml for localhost => (item=123)
included: /mnt/c/Users/justin/lab-ansible/subtask.yaml for localhost => (item=456)
included: /mnt/c/Users/justin/lab-ansible/subtask.yaml for localhost => (item=789)

TASK [debug] ***********************************************************************************************************************************************************************************************************ok: [localhost] => {
    "msg": 123
}

TASK [debug] ***********************************************************************************************************************************************************************************************************ok: [localhost] => {
    "msg": 456
}

TASK [debug] ***********************************************************************************************************************************************************************************************************ok: [localhost] => {
    "msg": 789
}

PLAY RECAP *************************************************************************************************************************************************************************************************************localhost                  : ok=6    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

We can, use loops in the subtasks when using the import_tasks. This, however, can get cluncky if there are a large number of subtasks you wish to loop over the same list. Using the import_tasks function your playbooks would look something like this:

import_a_task.yaml

---
- name: import a task
  hosts: localhost
  gather_facts: false
  tasks:
    - name: import a task
      import_tasks: subtask.yaml

subtask.yaml

---
- name: debug 1
  debug:
    msg: "Loop 1: {{ item }}"
  loop:
    - 123
    - 456
    - 789

- name: debug 2
  debug:
    msg: "Loop 2: {{ item }}"
  loop:
    - 123
    - 456
    - 789

When using include_tasks functionality you only have to define the loop one time in the outermost task.

include_a_task.yaml

---
- name: include a task
  hosts: localhost
  gather_facts: false
  tasks:
    - name: include a task
      include_tasks: subtask.yaml
      loop:
        - 123
        - 456
        - 789

subtask.yaml

---
- name: debug 1
  debug:
    msg: "Loop 1: {{ item }}"

- name: debug 2
  debug:
    msg: "Loop 2: {{ item }}"

Another item worth noting is that when using the import_tasks – each subtask executes its entire loop before going to the next subtask. However, when using include_tasks – each item in the loop is run against all subtasks one-by-one before moving onto the next item in the loop. The difference can be seen in the output.

ansible-playbook import_a_task.yaml

ansible-playbook import_a_task.yaml

PLAY [import a task] ***************************************************************************************************************************************************************************************************
TASK [debug 1] *********************************************************************************************************************************************************************************************************ok: [localhost] => (item=123) => {
    "msg": "Loop 1: 123"
}
ok: [localhost] => (item=456) => {
    "msg": "Loop 1: 456"
}
ok: [localhost] => (item=789) => {
    "msg": "Loop 1: 789"
}

TASK [debug 2] *********************************************************************************************************************************************************************************************************ok: [localhost] => (item=123) => {
    "msg": "Loop 2: 123"
}
ok: [localhost] => (item=456) => {
    "msg": "Loop 2: 456"
}
ok: [localhost] => (item=789) => {
    "msg": "Loop 2: 789"
}

PLAY RECAP *************************************************************************************************************************************************************************************************************localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   \

ansible-playbook include_a_task.yaml

ansible-playbook include_a_task.yaml

PLAY [include a task] **************************************************************************************************************************************************************************************************
TASK [include a task] **************************************************************************************************************************************************************************************************included: /mnt/c/Users/justin/lab-ansible/subtask.yaml for localhost => (item=123)
included: /mnt/c/Users/justin/lab-ansible/subtask.yaml for localhost => (item=456)
included: /mnt/c/Users/justin/lab-ansible/subtask.yaml for localhost => (item=789)

TASK [debug 1] *********************************************************************************************************************************************************************************************************ok: [localhost] => {
    "msg": "Loop 1: 123"
}

TASK [debug 2] *********************************************************************************************************************************************************************************************************ok: [localhost] => {
    "msg": "Loop 2: 123"
}

TASK [debug 1] *********************************************************************************************************************************************************************************************************ok: [localhost] => {
    "msg": "Loop 1: 456"
}

TASK [debug 2] *********************************************************************************************************************************************************************************************************ok: [localhost] => {
    "msg": "Loop 2: 456"
}

TASK [debug 1] *********************************************************************************************************************************************************************************************************ok: [localhost] => {
    "msg": "Loop 1: 789"
}

TASK [debug 2] *********************************************************************************************************************************************************************************************************ok: [localhost] => {
    "msg": "Loop 2: 789"
}

PLAY RECAP *************************************************************************************************************************************************************************************************************localhost                  : ok=9    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

That covers the basics of loops and when and how they can be used in each circumstance. Next we can look at tags.

One thought on “Ansible import vs include – Loops”

Leave a comment