Ansible Playbook Task Import: From Basics to Advanced

Ansible Playbook Task Import

When using Playbooks, there may be some frequently used Plays. In this case, you can utilize the import method in Playbooks to reuse the same Play. The imported tasks serve as a task template (without the <span>hosts</span> and <span>tasks</span> fields, as shown in the example below).

There are two modes for Playbook task import:

  • <span>ansible.builtin.import_tasks</span>: Static import of <span>tasks</span>, imported before the Playbook execution
  • <span>ansible.builtin.include_tasks</span>: Dynamic import of <span>tasks</span>, imported when the Playbook reaches a specific task
  • <span>ansible.builtin.import_role</span>: Static import of <span>role</span>, imported before the Playbook execution
  • <span>ansible.builtin.include_role</span>: Dynamic import of <span>role</span>, imported when the Playbook reaches a specific task
  • <span>ansible.builtin.import_playbook</span>: Static import of <span>Playbook</span>, imported before the Playbook execution

The main distinction here is between static and dynamic. Whether it is <span>tasks</span> or <span>role</span> does not make much difference; we will only use <span>ansible.builtin.import_tasks</span> and <span>ansible.builtin.include_tasks</span> as examples.

Creating Importable Templates

Below is a reusable Play template, saved as <span>/tmp/other.yml</span>:

[root@study ansible]# cat /tmp/other.yml
- name: set
  set_fact:
    x: foo
- name: print
  debug:
    var: x

Static Import

Below is a Playbook for static import:

[root@study ansible]# cat /tmp/import.yml
- name: test
  hosts: localhost
  tasks:
  - import_tasks: /tmp/other.yml
    when: x is not defined

To explain briefly, <span>import_tasks</span> will import the tasks from <span>/tmp/other.yml</span> before executing the tasks, thus the actual Playbook executed is as follows:

- name: test
  hosts: localhost
  gather_facts: false
  tasks:
  - name: set
    set_fact:
      x: foo
    when: x is not defined
  - name: print
    debug:
      var: x
    when: x is not defined

Each imported Play will have a <span>when</span> condition added. The first Play will set the variable <span>x</span>, and since the variable <span>x</span> now exists, the second Play will be skipped. The execution result is as follows:

[root@study ansible]# ansible-playbook /tmp/import.yml

PLAY [test] *****************************************************************************************************

TASK [set] ******************************************************************************************************
ok: [localhost]

TASK [print] ****************************************************************************************************
skipping: [localhost]

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

Dynamic Import

Below is a Playbook for dynamic import:

[root@study ansible]# cat /tmp/include.yml
- name: test
  hosts: localhost
  gather_facts: false
  tasks:
  - include_tasks: /tmp/other.yml
    when: x is not defined

This will execute the <span>include_tasks</span> Play only when the Playbook reaches that task. Although there is a condition, it only checks whether the task should be imported (import the Play if the variable does not exist, skip if it does). Therefore, all imported Plays will be executed, and the execution result is as follows:

[root@study ansible]# ansible-playbook /tmp/include.yml

PLAY [test] *****************************************************************************************************

TASK [include_tasks] ********************************************************************************************
included: /tmp/other.yml for localhost

TASK [set] ******************************************************************************************************
ok: [localhost]

TASK [print] ****************************************************************************************************
ok: [localhost] =&gt; {
    "x": "foo"
}

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

Differences Between Static and Dynamic Import

https://docs.ansible.org.cn/ansible/latest/playbook_guide/playbooks_reuse.html#comparing-includes-and-imports-dynamic-and-static-reuse

Here is a direct quote from the official website’s table:

Include_* Import_*
Reuse Type Dynamic Static
When Processed At runtime, when encountered Pre-processed during Playbook parsing
Task or Play All includes are tasks <span>import_playbook</span> cannot be a task
Task Options Only applied to the included task itself Applied to all sub-tasks in the import
From Loop Calls Executed once for each loop item Cannot be used in loops
Using <span>--list-tags</span> Tags in includes are not listed All tags are displayed with <span>--list-tags</span>
Using <span>--list-tasks</span> Tasks in includes are not listed All tasks are displayed with <span>--list-tasks</span>
Notify Handlers Cannot trigger handlers in includes Can trigger handlers from imports
Using <span>--start-at-task</span> Cannot start from a task in includes Can start from an imported task
Using Inventory Variables Can use <span>include_*: {{ inventory_var }}</span> Cannot use <span>import_*: {{ inventory_var }}</span>
For Playbooks No <span>include_playbook</span> Can import entire playbooks
For Variable Files Can include variable files Use <span>vars_files:</span> to import variables

For <span>notify</span>, <span>include_tasks</span> only supports overall triggering, while <span>import_tasks</span> supports triggering individual Plays from imports.

Referencing an example from the official website:

https://docs.ansible.org.cn/ansible/latest/playbook_guide/playbooks_reuse.html#re-using-tasks-as-handlers

Imported Play template:

# restarts.yml
- name: Restart apache
  ansible.builtin.service:
    name: apache
    state: restarted

- name: Restart mysql
  ansible.builtin.service:
    name: mysql
    state: restarted

Dynamic import of <span>notify</span>:

- name: Trigger an included (dynamic) handler
  hosts: localhost
  handlers:
    - name: Restart services
      include_tasks: restarts.yml
  tasks:
    - command: "true"
      notify: Restart services

Static import of <span>notify</span>:

- name: Trigger an imported (static) handler
  hosts: localhost
  handlers:
    - name: Restart services
      import_tasks: restarts.yml
  tasks:
    - command: "true"
      notify: Restart apache
    - command: "true"
      notify: Restart mysql

Leave a Comment