When working with Ansible's include_role
directive, many administrators encounter unexpected behavior with tag inheritance. The core issue manifests when tags applied at the playbook level don't properly propagate to tasks within included roles.
In our example scenario, the following occurs:
- hosts: all
tasks:
- name: Install Icinga2 on Windows
include_role:
name: my.icinga2.role
apply:
tags:
- install-icinga2
Despite specifying install-icinga2
tag, tasks with other tags (like install-icinga2-stack
) still execute. This happens because:
- Tags from the
apply
block only filter which roles to include, not which tasks within roles - Once a role is included, all its tasks execute unless filtered by other conditions
The current implementation follows these rules:
1. Role-level tags filter whether to include the role
2. Task tags within included roles work independently
3. Conditional statements (when
) override tag filtering
Here are three approaches to achieve the desired behavior:
1. Explicit Tag Filtering
Modify the playbook to explicitly specify which tags to run:
ansible-playbook plays/icinga2-client-win.yml \
--tags install-icinga2 \
--skip-tags install-icinga2-stack
2. Restructure Role Tasks
Reorganize the role to use proper tag inheritance:
# tasks/main.yml
- include_tasks: common.yml
tags: always
- include_tasks: install.yml
tags: install-icinga2
- include_tasks: configure.yml
tags: install-icinga2-stack
3. Combine Tags with Conditionals
For more complex scenarios, combine tags with explicit conditionals:
- include_tasks: configure.yml
tags: install-icinga2-stack
when: "'install-icinga2-stack' in ansible_run_tags"
For tasks like ido-install.yml
where you want both tag and conditional control:
- include_tasks: ido-install.yml
when:
- icinga2_ido_enable == true
- "'install-icinga2-ido' in ansible_run_tags"
tags:
- install-icinga2-stack
- install-icinga2-ido
- Document all tags used in role metadata (
meta/main.yml
) - Use consistent tag naming conventions
- Test tag behavior with
--list-tasks
before execution - Consider using
block
with tags for logical grouping
For complex scenarios, you can create a custom filter plugin:
# filter_plugins/tag_utils.py
def filter_by_tags(tasks, include_tags, exclude_tags):
return [t for t in tasks if
(not t.tags or set(t.tags) & set(include_tags)) and
not (set(t.tags) & set(exclude_tags))]
class FilterModule(object):
def filters(self):
return {'filter_by_tags': filter_by_tags}
When working with Ansible's include_role
directive, many users encounter unexpected behavior with tag propagation. The core problem manifests when:
- Tags defined at the playbook level don't properly filter tasks within included roles
- Conditional statements (
when:
) override tag-based filtering - Tasks with multiple tags behave differently than expected
In your specific case, two distinct issues are occurring simultaneously:
# Playbook level tag application
- include_role:
name: my.icinga2.role
apply:
tags:
- install-icinga2
# Role tasks with multiple tags
- include_tasks: configure.yml
tags: ['install-icinga2-stack'] # Still gets executed
Ansible's tag filtering operates differently between playbook-level tags and role-internal tags. The apply
parameter in include_role
only affects tasks directly under the role's main.yml, not nested includes.
Here are three approaches to achieve proper tag filtering:
1. Explicit Tag Inheritance
- include_role:
name: my.icinga2.role
tasks_from: install.yml
apply:
tags: install-icinga2
2. Dynamic Tag Management
- name: Control task execution
include_tasks: "{{ item }}"
loop:
- vars.yml
- install.yml
- ido-install.yml
- configure.yml
tags:
- "{{ (item == 'ido-install.yml' and icinga2_ido_enable) | ternary('install-icinga2-ido','skip') }}"
- "{{ (item == 'configure.yml') | ternary('skip','install-icinga2') }}"
3. Role Restructuring Approach
Create separate entry points in your role:
# roles/my.icinga2.role/tasks/main.yml
- include_tasks: "{{ include_tasks_from | default('default.yml') }}"
tags: "{{ include_tags | default(omit) }}"
# Then call from playbook:
- include_role:
name: my.icinga2.role
vars:
include_tasks_from: install.yml
include_tags: install-icinga2
For the ido-install.yml
issue, combine conditionals and tags explicitly:
- include_tasks: ido-install.yml
when:
- icinga2_ido_enable | default(false)
- "'install-icinga2-ido' in ansible_run_tags or 'install-icinga2' in ansible_run_tags"
This ensures the task only runs when BOTH the condition is true AND the proper tag is specified.
- Use consistent tag naming conventions (e.g., prefix with role name)
- Avoid tag overlap between different functional areas
- Document tag usage in role's README or meta/main.yml
- Test tag filtering with
--list-tasks
before execution
ansible-playbook playbook.yml --list-tasks --tags install-icinga2