Ansible 101 - Include vs Import

14 minute read

I am asked quite often about the differences between using import or include in Ansible. For example import_role or include_role - what should you expect when using one or the other?

Ansible documentation does a great job in explaining a lot of this and I recommend at least starting there. However, I felt a need to write a few example Playbooks for my own and to give others another way to look at it.

Below are some example playbooks that are meant to give you a better understanding on the differences between import and include.

Example - Parsing

Basically import tasks will be parsed at the beginning when you run the playbook, but include tasks will only be parsed at the moment Ansible hits them. Let me show you with an example.

We create a simple Ansible Role with a couple tasks - one that sets a variable and another that has a typo and should fail when parsed. It should show a syntax error. Here is the main.yml task file from our role called example.

- set_fact:
    role_setfact_var: testvalue
- debugger:

Now we write the Playbook to run a simple debug task first and then we include the role.

---
- hosts: localhost
  gather_facts: no

  tasks:
    - debug:

    - include_role:
        name: example

Running the Playbook shows that the first task debug was successfull but then Ansible tried to perform the tasks inside the role and it hit our syntax error.

PLAY [localhost] **************************************************************************************

TASK [debug] ******************************************************************************************
ok: [localhost] => {
    "msg": "Hello world!"
}

TASK [include_role : example] *************************************************************************
ERROR! no action detected in task. This often indicates a misspelled module name, or incorrect module path.

The error appears to have been in '/Users/jwadleig/Projects/ansible-example-include-vs-import/roles/example/tasks/main.yml': line 3, column 3, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

    role_setfact_var: testvalue
- debugger:
  ^ here

  to retry, use: --limit @/Users/jwadleig/Projects/ansible-example-include-vs-import/test.retry

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

This all seems quite normal. Now let us instead use import_role and see the output again.

ERROR! no action detected in task. This often indicates a misspelled module name, or incorrect module path.

The error appears to have been in '/Users/jwadleig/Projects/ansible-example-include-vs-import/roles/example/tasks/main.yml': line 3, column 3, but may be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

    role_setfact_var: testvalue
- debugger:
  ^ here

Wait, why didn’t Ansible run our first task? The reason is because the import_role task is pre-processed at the time playbooks are parsed. So it found the syntax error much earlier and so it never was able to even run our first task with the debug module.

This is an example that really emphasizes the fact that import is parsed quite early. This is what is referred to as static.

Example - Using when: conditional clause

---
- hosts: localhost
  gather_facts: no

  tasks:
    - name: Conditional role
      include_role:
        name: myrole
      when: false

    - name: Apply condition to each task in role
      import_role:
        name: myrole
      when: false

Running this playbook results in the following output.

$ ansible-playbook test.yml

PLAY [localhost] **************************************************************************************

TASK [include_role : example] *************************************************************************
skipping: [localhost]

TASK [example : set_fact] *****************************************************************************
skipping: [localhost]

PLAY RECAP ********************************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=0

Notice the following things:

  • include_role task itself was skipped because the when: clause is applied to the include_role task
  • import_role task applied the when: clause to the task inside the role, so the output only showed the task inside the role that was skipped

In fact, Ansible documentation states:

Most keywords, loops and conditionals will only be applied to the imported tasks, not to the statement itself.

Example - Using tags

Another common question is how tags are affected when using import vs include.

Let’s write the following playbook that uses tags. With include_role the tags will be applied to the tasks within the included role.

---
- hosts: localhost
  gather_facts: no

  tasks:
    - name: Apply tags to tasks within included file
      include_role:
        name: example
      tags:
        - mytag

To apply the tag to all tasks inside the role, use the apply argument as shown here.

- name: Apply tags to tasks within included file
  include_role:
    name: install
    apply:
      tags:
        - install
  tags:
    - always

References