How to Properly Use Ansible include_tasks with Tags on Nested Tasks


2 views

When working with Ansible's include_tasks functionality, many developers encounter a common pain point: tags defined in nested task files don't automatically propagate to the parent include statement. This creates a frustrating situation where running playbooks with specific tags fails to execute the intended tasks.


# main.yml
- include_tasks: dev.yml
  when: ec2_tag_env == 'dev'

# dev.yml
- name: Pull the latest image
  docker_image:
    name: "{{ dev_image }}"
    source: pull
  tags:
    - container

Ansible processes tags at two distinct levels:

  1. During playbook parsing (where it evaluates include statements)
  2. During task execution (where it evaluates individual tasks)

The parser needs to know which files to process before it can see the tags inside them. This creates a chicken-and-egg problem for dynamically included tasks.

Option 1: Explicit Tag Propagation

The most straightforward approach is to duplicate tags on the include statement:


- include_tasks: dev.yml
  when: ec2_tag_env == 'dev'
  tags:
    - container
    - dev

Option 2: Using import_tasks Instead

import_tasks behaves differently than include_tasks because it's processed statically during playbook parsing:


- import_tasks: dev.yml
  when: ec2_tag_env == 'dev'
  # Tags aren't needed here because import_tasks brings them in

Option 3: Dynamic Tag Collection

For complex cases, you can use a pre-task to dynamically collect and apply tags:


- name: Set tags for included tasks
  set_fact:
    required_tags: "{{ (required_tags | default([])) + ['container'] }}"
  when: ec2_tag_env == 'dev'

- include_tasks: dev.yml
  when: ec2_tag_env == 'dev'
  tags: "{{ required_tags | default(omit) }}"
  • Use consistent tag naming conventions across your playbooks
  • Document all available tags in a central README
  • Consider using tag prefixes for different environments (dev_, prod_)
  • Automate tag validation in your CI/CD pipeline

For complex environments, you might want tags to behave differently based on conditions:


- name: Include environment tasks
  include_tasks: "{{ ec2_tag_env }}.yml"
  tags:
    - "{{ 'container' if ec2_tag_env == 'dev' else omit }}"
    - "{{ 'db' if ec2_tag_env == 'prod' else omit }}"

When working with Ansible's include_tasks, a common pain point emerges regarding tag propagation. The fundamental behavior is that:

- include_tasks: file.yml  # Parent task
  tags: base_tag           # Only these tags are evaluated initially

# file.yml contents:
- name: Sub-task
  command: do_something
  tags: nested_tag         # These won't be considered unless parent has them

Ansible's tag evaluation works in two phases:

  1. First pass determines which top-level tasks to include
  2. Second pass evaluates subtasks only if their parent was selected

Option 1: Tag Inheritance Pattern

Create a standardized approach for tag inheritance:

# main.yml
- include_tasks: dev.yml
  when: ec2_tag_env == 'dev'
  tags: always,container,deploy  # All possible subtask tags

Option 2: Dynamic Includes

Use variables to control inclusion:

# playbook.yml
vars:
  include_container_tasks: "{{ 'container' in ansible_run_tags }}"

# main.yml
- include_tasks: dev.yml
  when: ec2_tag_env == 'dev' and (include_container_tasks or not ansible_run_tags)

Option 3: Tagged Blocks

Restructure using block-level tags:

# dev.yml
- block:
    - name: Pull the latest image
      docker_image:
        name: "{{ dev_image }}"
        source: pull
  tags:
    - container

For complex playbooks, consider:

  • Maintaining a central tag registry (e.g., group_vars/tags.yml)
  • Using CI/CD to validate tag consistency
  • Documenting tag requirements for included files
# Example registry
common_tags:
  container_ops: [container, docker, image]
  db_ops: [database, mysql, migration]

Create a filter to auto-collect tags:

# filter_plugins/tag_utils.py
def collect_tags(file_content):
    # Parse YAML and extract all unique tags
    ...

# main.yml
- include_tasks: dev.yml
  tags: "{{ dev.yml | collect_tags }}"