Production Postmortem: Tag-Based Task Execution Failure in Ansible Molecule Scenario
Summary
Root Cause
- Molecule applies
--tags/--skip-tagsglobally to both role tasks and its internal action playbooks (create.yml/destroy.yml). - The role’s
always-tagged task (Make preparations) runs indiscriminately due to Ansible’s built-inalwaystag behavior. - Tag inheritance wasn’t isolated to the role. Action playbooks lacking explicit tagging inherited global CLI/molecule.yml tags, conflicting with scenario intentions.
Why This Happens in Real Systems
- Action Playbook Coupling: Internal Molecule playbooks execute within the same Ansible context as role tasks, inheriting tag flags.
alwaysTag Ambiguity**: Ansible’s undocumentedalwaystag behavior (runs unless explicitly skipped) contradicts scenario-specific filtering.- Imperfect Isolation: Multi-scenario repositories often share action playbooks/vars, propagating tag conflicts across environments.
- Tool Assumptions: Molecule treats tags as global runtime args, not scoped to role execution.
Real-World Impact
- Unsafe State Changes: Non-
removetasks (e.g., configuration/installation) executed during destructive operations, risking data loss or misconfiguration. - Scenario Reliability: Delete scenarios became non-deterministic, preventing safe infrastructure teardown.
- Debugging Overhead: Engineers spent hours verifying task execution logs due to unexpected tag behavior.
Example or Code
Attempted to create a moleculed `delete` scenario for an Ansible role that should exclusively run tasks tagged `remove`. Despite configuring scenario actions and CLI tag flags, Molecule executed unwanted tasks because tag filters applied globally to all playbooks (including Molecule's action playbooks), causing unintended task inclusion.
yaml
Role task (tags create conflicts)
-
name: Make preparations
tags: [always] # Runs in EVERY scenario unless explicitly skipped -
name: Remove
tags: [never, remove] # Intended to run ONLY in delete scenario
yaml
Non-working molecule.yml approach
provisioner:
name: ansible
playbooks:
create: ../shared/create.yml # Lacks tags → Inherits global –tags=remove
options:
tags: remove # Applied to ALL playbooks!
How Senior Engineers Fix It
1. Explicit Skip-Tags
-
Use
--skip-tags alwaysin CLI fordeletescenario:
bash
molecule converge -s delete — –skip-tags always -
Ensures
always-tagged tasks don’t force-execute during removal.
2. Isolate Action Playbooks
-
Modify
create.yml/destroy.ymlwith scenario-specific tags:
yamldestroy.yml
-
name: Destroy infrastructure
tags: [“never”, “destroy”]
hosts: all
tasks: […] -
Call with
--tags=destroy,removeduringmolecule destroy.
3. Decouple Role Tasks
- Split
main.ymlinto dedicated files:
yaml
tasks/
main.yml: # Default scenario - include_tasks: setup.yml
- include_tasks: configure.yml
- include_tasks: remove.yml
when: scenario == “delete”
remove.yml:
- name: Remove
tags: [remove] # Remove ‘never’ tag
4. Conditional Logic
Replace tags with Molecule variables:
yaml
- name: Remove
when: scenario == “delete” # Explicit scenario guard
Why Juniors Miss It
- Assumed Tag Locality: Expected
--tagsto apply only to the role, not Molecule’s internal playbooks. - Overlooked Implicit Tags: Unaware that
alwaysruns unless skipped, even with custom tags. - Tool Abstraction: Didn’t inspect Molecule’s underlying Ansible command generation.
- Undocumented Nuances: Ansible’s tag precedence (
always> CLI tags) isn’t intuitive. - Scenario Coupling: Underestimated shared action playbook side effects across scenarios.