Subtopic•Includes vs Imports

Include Vs Import

In the previous module, we covered advanced error handling using blocks, rescue sections, and rollback patterns to make playbooks resilient. As your automation projects expand to cover complex setups, keeping all tasks in a single playbook file makes it difficult to read, maintain, and share.

To address this, Ansible provides two mechanisms to break playbooks into smaller, reusable files: Includes and Imports.

In this guide, we will cover the conceptual difference between includes and imports, how they alter the parsing of playbooks, and how they serve as the foundation for modular infrastructure code.

1. The Core Difference: Parse Time vs. Execution Time

While both includes and imports pull external files into your playbooks, they do so at different points in Ansible's execution life cycle:

  • Imports are Static: They are processed at parse time, before the playbook starts executing on remote hosts. Ansible pre-compiles the imported files into a single master task list.
  • Includes are Dynamic: They are processed at execution time, as the playbook runs. Ansible only evaluates and loads the included file when its position in the task list is reached.
Static Import (Parse Time)
Playbook parsed → Load external file → Compile unified task list → Execute tasks

Dynamic Include (Execution Time)
Playbook parsed → Execute task 1 → Reach include statement → Load file → Execute included tasks

This timing difference affects how variables are evaluated, how tags propagate, and how loops are executed.


2. Static Imports: Compiling Task Lists

When you use an import statement (like import_tasks or import_playbook), Ansible acts as if you copied and pasted the tasks from the external file directly into the main playbook before running:

# main.yml
---
- name: Deploy application
  hosts: webservers
  tasks:
    - name: Run system update tasks
      ansible.builtin.import_tasks: tasks/updates.yml

When Ansible reads main.yml:

  1. It immediately opens tasks/updates.yml.
  2. It replaces the import_tasks line with all the tasks defined inside updates.yml.
  3. It compiles the final list of tasks.
  4. It starts the playbook run.

Because this happens before execution:

  • You cannot use variables inside the imported filename (e.g., import_tasks: "tasks/{{ app_name }}.yml" is invalid).
  • Handlers, tags, and parameters applied to the import statement cascade to all tasks inside the file statically.

3. Dynamic Includes: Runtime Evaluations

When you use an include statement (like include_tasks or include_playbook), Ansible processes the task list dynamically as it executes:

# main.yml
---
- name: Deploy application
  hosts: webservers
  tasks:
    - name: Run dynamic configuration tasks
      ansible.builtin.include_tasks: "tasks/{{ app_name }}.yml"

When Ansible reads main.yml:

  1. It parses the playbook, treating the include_tasks statement as a single placeholder task.
  2. It starts executing tasks.
  3. When the execution flow reaches the include_tasks task, it evaluates the {{ app_name }} variable.
  4. It loads the corresponding file (e.g., tasks/nginx.yml) and inserts the tasks into the active queue.

Because this happens at runtime:

  • You can use variables inside the filename.
  • You can run includes inside loops, loading different files for different iterations.
  • Handlers, tags, and parameters do not cascade to the included tasks automatically; they only apply to the include task itself.

4. Operational Best Practices: Includes vs. Imports

  • Use Imports for Static Logic: Use imports when your task sequence is fixed and does not change based on environment variables or host facts.
  • Use Includes for Dynamic Routing: Use includes when you need to load different task files based on variables, system facts, or inventory groupings.
  • Keep file paths relative: Always store included and imported task files relative to the playbook or role directory to maintain project portability.

5. Include and Import Syntax Reference

Action TypeDirectivesProcessing PointVariable Filename Allowed
Static Importimport_tasks, import_playbookParse TimeNo
Dynamic Includeinclude_tasks, include_playbookRuntimeYes

Now that we have covered the conceptual differences between includes and imports, the next step is analyzing how these structures affect variable and tag evaluation in detail. In the next section, we will cover how static and dynamic behaviors alter playbook compilation and task tags.

←Previous ModuleError Handling