Ansible Curriculum
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.
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:
When Ansible reads main.yml:
- It immediately opens
tasks/updates.yml. - It replaces the
import_tasksline with all the tasks defined insideupdates.yml. - It compiles the final list of tasks.
- 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:
When Ansible reads main.yml:
- It parses the playbook, treating the
include_tasksstatement as a single placeholder task. - It starts executing tasks.
- When the execution flow reaches the
include_taskstask, it evaluates the{{ app_name }}variable. - 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
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.