A policy gate denies a Terraform plan over a resource generated three modules deep — how do you resolve it?
answer
- the address is not a file anyone has open
- leftmost segment is the editable call
- check inputs and a version bump first
- exempt the module version, not the plan
- run the rules at module release
basics
~20 sTranslate the address back to the top-level module call the developer can actually edit, then fix it where it is fixable: a module input, a newer pinned version, or an upstream change. Attach any exception to the module and version, not to one plan.
solid answer
~50 sFirst fix the message. An address like `module.platform.module.data.module.storage.aws_s3_bucket.raw` names a declaration in nobody's local file; the caller's editable surface is the `module "platform"` block in their root configuration, so the denial should name that call, the module source and version behind it, and the generated address as supporting detail. Then triage: does the module already expose an input that satisfies the rule, does a newer version fix it, or does the module itself need changing? Only the third case is genuinely blocked, and it is blocked on the platform team, not the developer. If you grant an exception, key it to the module and version rather than to this plan — otherwise you accumulate one waiver per caller for a single upstream defect and nobody ever fixes the module. Longer term, run the same rules over the module's own example plans at release, so violations surface to the author.
go deeper
Understand why the address in the denial does not match anything in your own files: modules are expanded into the plan, so the resource was declared in the module's repository, not yours.
Be able to read the module path left to right, identify the top-level call in the root configuration as the editable anchor, and check the obvious remediations — an unset module input, or a newer pinned version.
Demonstrate the triage and the exception granularity: escalate to the module owner, scope any waiver to the module and version so it covers every caller and retires on upgrade, and push the same rules into the module's release pipeline.
Own the trade you made by mandating shared modules: you concentrated remediation in one team, so the control is only sustainable if module review is real and fixed versions ship fast enough that blocked teams do not campaign for blanket exemptions.
### Why this failure feels different from every other denial Most gate failures are self-service: the rule names an attribute, the developer opens the file, changes it, pushes again. A violation inside a nested module breaks that loop in a specific way. The plan expands modules into resources, so the denial's address is fully qualified — `module.platform.module.data.module.storage.aws_s3_bucket.raw` — and describes a declaration living in a repository the developer may not even know exists. They search their own code for `aws_s3_bucket.raw` and find nothing. The gate is correct, the finding is real, and the feedback is useless as delivered. ### Step one: address translation The module path is a chain of call names, read left to right from the root. The first segment, `module.platform`, is the only one present in the developer's own configuration. That is the human-editable anchor, and the denial should lead with it: the call name, the source and version pinned on that call, and then the generated resource address as evidence. The gate already has everything needed to say this — the call is in the plan's configuration section, the resource is in the expanded output, and the address prefix joins them. Doing that join is the operator's job, not the developer's. The same translation decides *who* gets the notification. A denial anchored on a generated address routes to whoever ran the pipeline. A denial anchored on the module call routes to two parties: the caller, who chose to use this module at this version, and the module's owner, who wrote the resource. ### Step two: triage the three real cases **The module already supports the fix.** Many rules — a required tag, an encryption flag, a logging bucket — correspond to an input the module exposes and the caller left unset or set wrongly. The change is one argument in the caller's own module block. This is the outcome you want most denials to reduce to, and it is worth checking first because it is invisible from the address alone. **A newer module version fixes it.** The approved-source-and-pinned-version control pays for itself here: because the call names an exact version, you can compare it against the current release and tell the developer that bumping the pin resolves the finding. The remediation is again a one-line edit in their file. This is also why pinning to an immutable reference matters — without it you cannot say which revision produced the bad resource. **The module itself is wrong.** Now the caller is genuinely blocked on someone else's backlog. Escalate to the module owner with the specific rule and resource, and decide what happens to the caller in the meantime. This is the only case that needs a policy decision rather than an edit. ### Step three: if you must let it through, choose the right unit The instinct is to exempt the failing plan. Resist it. Ten teams calling the same module hit the same violation, so plan-scoped exemptions produce ten records for one defect, ten expiry dates, and no signal that they share a cause — and when the module is finally fixed, nothing retires them. Key the exception to the **module source and version**: it covers every caller at once, it expires naturally when callers move to a version that no longer needs it, and it makes the outstanding work legible as one item on the platform team's list rather than ten on ten teams' lists. It also puts the record where the accountability is. ### Step four: move the finding upstream The structural fix is to stop discovering module defects in callers' pipelines at all. Run the same rule set over the module's own plans — the examples or test configurations in the module repository — as part of its release process. Then a violation is found by the person who can fix it, before a version exists for anyone to pin. A module registry that publishes versions which fail the organisation's own policy is publishing known-bad artifacts and pushing the discovery cost onto every consumer. ### The judgment underneath All of this is the price of a paved road. The moment you require infrastructure to come through shared modules, you have moved a class of failures out of the reach of the people who trip over them. That is a good trade — one review instead of hundreds — but only if two things hold: the module review that justifies the approval is real, and a fixed version reaches the callers quickly. If module fixes take a quarter, your gate has converted a security finding into a delivery blocker for teams who cannot resolve it, and the pressure to blanket-exempt the module becomes irresistible. Operators of this kind of gate should watch the age of open module-scoped exceptions the way they watch the denial rate: it is the honest measure of whether the paved road is maintained.
- The developer says they cannot fix it and asks you to disable the rule for their pipeline. What do you say?That the rule stays and the exception moves. Disabling it for their pipeline hides every future violation of that rule in their estate, including ones they could fix. A time-boxed exception scoped to the offending module and version keeps the rule live everywhere else, covers their peers hitting the same wall, and expires when a fixed version exists.
- How would you decide whether to rule on the module call or on the resources it expands into?Rule on the resources for anything about what gets built — that is where the attributes are, and it works whoever declared them. Rule on the call for provenance: source and version exist only there. They answer different questions, so in practice you run both, and use the call to give the resource finding an address a human can act on.
- What signal tells you this pattern has become a systemic problem rather than a one-off?Module-scoped exceptions that keep getting extended, and several callers blocked on the same module version. Both say the module release pipeline is not running the organisation's rules, or that module fixes are slower than the teams consuming them can tolerate. The fix is upstream capacity, not more waivers.
The gate has rejected a part inside a pre-assembled component. Telling the assembly line the part number is useless; you tell them which component to reorder, and you tell the component's manufacturer about the part.
saying these in an interview costs you the question
- Reports only the generated address and calls it actionable
- Exempts the individual plan instead of the module version
- Disables the rule for the blocked team's pipeline
- Assumes the caller can edit the module they are calling
- Never checks whether a module input or newer version fixes it
- Leaves module violations to be found by consumers, not authors