Imports vs. Includes¶
The core import_tasks vs. include_tasks comparison — with the decision tree — lives in Module Decision Trees. This page covers what that comparison implies once you're structuring a real project, plus the playbook-level equivalent, import_playbook.
The One-Line Version¶
import_*— resolved at parse time, before any host runs anything. Fully visible to--list-tasks/--list-tags/--tagsfiltering.include_*— resolved at run time, host by host. Necessary when what to include depends on a variable, fact, or loop — invisible to--list-tasksahead of time, because it genuinely can't be known yet.
import_playbook¶
The playbook-level equivalent of import_tasks, used to compose multiple playbook files into one entry point:
---
- import_playbook: preflight.yml
- import_playbook: web.yml
- import_playbook: database.yml
import_playbook is always static — there is no include_playbook counterpart. This is a deliberate asymmetry: playbook composition is expected to be a fixed, reviewable structure, not something decided at run time.
Interaction With Loops¶
# include_tasks CAN be looped — a genuinely dynamic use case
- name: Run environment-specific setup
ansible.builtin.include_tasks: "{{ item }}_setup.yml"
loop:
- network
- storage
- firewall
import_tasks cannot be looped this way — because it's resolved before the play runs, there's no item value available yet to build a filename from. This is the clearest practical case where include_tasks isn't just a style preference — it's the only option.
Interaction With Tags¶
With import_tasks, the networking tag is applied to every task inside network_setup.yml individually, at parse time — --tags networking runs exactly those tasks, and --list-tags shows them. With include_tasks, tag filtering is checked at the moment the include is reached; Ansible can't tell you in advance what's inside.
Decision, Restated for Project Structure¶
| Situation | Choice |
|---|---|
| Splitting a long, always-the-same task list into files for readability | import_tasks |
| Composing multiple playbook files into one entry point | import_playbook (the only option) |
Task file to include is decided by a variable, fact, or loop |
include_tasks |
Need --list-tasks/--tags to see everything ahead of a run |
import_tasks |
Common Mistakes¶
- Trying to
loop:animport_tasksand being confused whyitemisn't defined inside the imported file — loops requireinclude_tasks. - Assuming
include_playbookexists — it doesn't; playbook-level composition is always static viaimport_playbook. - Defaulting to
include_taskseverywhere out of habit, losing--list-tasksvisibility for structure that was actually static all along.
Interview Questions¶
- What's the difference between
import_tasksandinclude_tasks, in terms of when each is resolved? - Why can't
import_tasksbe used inside aloop? - Why is there no
include_playbook?
See Interview Prep: Core Concepts for the full leveled answer.
Next¶
Continue to Delegation and Become.