Ansible roles / importrole / includerole Practical Reference¶
About This Article¶
A practical reference for engineers who struggle with choosing between Ansible's roles:, import_role, and include_role.
What You'll Learn:
- Use flowcharts to instantly decide which approach to use
- Find the right method from reverse lookup tables based on "what you want to do"
- Identify causes and solutions from troubleshooting tables when issues occur
How to Use This Document¶
- Need a quick decision? → Check the Decision Flowchart and Reverse Lookup Table
- Want to understand why? → Read the detailed sections
- Troubleshooting? → Use the Troubleshooting Table
Decision Flowchart¶
Just listing roles as play structure?
│
├─ Yes ───────────────────────────→ roles:
│
└─ No → Need to insert tasks in between?
│
├─ No ───────────────────→ roles:
│
└─ Yes → Need conditionals/loops/variable role names?
│
├─ No ─────────→ import_role
│ (static - tags apply to role tasks)
│
└─ Yes ────────→ include_role
(dynamic - supports conditionals/loops)
│
└─ Want tags to apply to role tasks?
├─ Yes → Use with apply:
└─ No → Use as-is
Reverse Lookup Table (Goal → Method)¶
| Goal | Method | Notes |
|---|---|---|
| Define play structure | roles: | First choice. Auto dependency resolution |
| Insert tasks in between | import_role | Explicit positioning |
| Apply tags to all role tasks | import_role | include_role requires apply: |
| Conditional role selection | include_role | Use with when: |
| Loop over roles | include_role | Use with loop: |
| Variable role name | include_role | name: "{{ var }}" |
| Execute specific role file only | tasks_from: | Limit entry points |
| Expose role vars to subsequent tasks | include_role + public: true | Explicit required |
| Execute same role multiple times | allow_duplicates: true (in role) | Set in meta/main.yml |
Troubleshooting Table (Symptom → Solution)¶
| Symptom | Cause | Solution |
|---|---|---|
| Tags don't affect role tasks | include_role tags don't inherit to role | Use apply: or switch to import_role |
| Second invocation of same role skipped | Duplicate prevention (default) | Add allow_duplicates: true in role |
| Variables affect preceding tasks | import_role exposes vars at parse time | Explicit public + isolate shared vars |
| Role doesn't work in handlers | Role calls in handlers not supported | Limit handlers to single functions, use role calls in tasks |
| Wrong handler triggered due to name collision | Handlers have Play-wide scope | Use role_name : handler_name format in notify |
Characteristics of the Three Approaches¶
Static vs Dynamic¶
| Type | Processing Timing | Characteristics |
|---|---|---|
Static (roles:, import_role) | Parse time | Execution plan determined early. Tags easily apply to role tasks |
Dynamic (include_role) | Runtime | Strong support for conditionals/loops. Tags require apply: |
Overview of Each Approach¶
| Method | Use Case | Characteristics |
|---|---|---|
roles: | Play structure | Static. Auto dependency resolution. Runs before tasks |
import_role | Insert tasks + apply tags | Static. Can be positioned in tasks list |
include_role | Conditionals/loops/variable names | Dynamic. Flexible but tags need apply: |
Execution Order¶
Roles specified with roles: ←── Execute first
↓
tasks: task list (import_role/include_role inserted here)
roles:runs before tasksimport_role/include_roleexecute at their position in tasks list
How Tags Work (Critical Difference)¶
| Method | Tag Scope | Workaround |
|---|---|---|
roles: / import_role | Applied to all role tasks | Works as-is |
include_role | Applied only to include_role statement | Use apply: to pass to role |
include_role + apply example:
- name: deploy with tags
ansible.builtin.include_role:
name: app
apply:
tags: [deploy]
become: true
tags: [deploy] # ← Tag for include_role statement itself
Call Specific Files (tasks_from, etc.)¶
- ansible.builtin.include_role:
name: myrole
tasks_from: setup.yml # roles/myrole/tasks/setup.yml
handlers_from: custom.yml # roles/myrole/handlers/custom.yml
Team Standard: Limit entry points to a few (install/configure/verify). Split roles if they grow.
Variable Scope and public¶
| Method | Exposure Range with public |
|---|---|
include_role | Available from that task onwards |
import_role | Exposed at parse time (may affect preceding tasks) |
- ansible.builtin.include_role:
name: shared_vars
public: true # ← Explicit required
Team Standard: Always explicitly set public (true/false). Isolate shared variables in dedicated roles.
Duplicate Prevention and allow_duplicates¶
Principle: Same role executes only once per Play (including dependent roles)
To execute multiple times:
# roles/myrole/meta/main.yml
allow_duplicates: true
Pattern Collection¶
A. Play Structure (Default)¶
- hosts: all
roles:
- common
- hardening
- app
B. Insert Check in Between¶
- hosts: web
tasks:
- name: precheck
command: /usr/local/bin/precheck
- ansible.builtin.import_role:
name: app
tags: [deploy]
C. Conditional Branching¶
- hosts: all
tasks:
- ansible.builtin.include_role:
name: app_debian
when: ansible_facts['os_family'] == 'Debian'
- ansible.builtin.include_role:
name: app_rhel
when: ansible_facts['os_family'] == 'RedHat'
D. Loop¶
- hosts: all
tasks:
- ansible.builtin.include_role:
name: "{{ item }}"
loop:
- common
- app
E. include_role + apply¶
- hosts: web
tasks:
- ansible.builtin.include_role:
name: app
apply:
tags: [deploy]
become: true
tags: [deploy]
Team Standardization (Minimal Rules)¶
- Default to
roles: - Document exception conditions
- Insert tasks →
import_role - Conditionals/loops/variables →
include_role(withapply:to affect role tasks) - Always explicitly set
public(true/false) - Limit
tasks_fromentry points (split roles if they grow) - Use
allow_duplicatesin role for multiple executions - Prohibit role calls in handlers (design guideline)
References¶
Updated: 2025-12-29