Skip to content

data block with for_each evaluating to empty collection does not register namespace, breaking downstream references #110

Description

Summary

When a data block has for_each that evaluates to an empty collection, the resulting namespace (data.<type>.<name>) is never registered in the evaluation context. Any downstream reference (e.g. for_each = data.<type>.<name> on another data block or a transform) throws an "unknown reference" error instead of degrading to an empty iteration.

This forces every consumer of a for_each-driven data namespace to wrap their reference in try(data.X.Y, {}), which is ergonomically poor and easy to forget.

Reproducer

# .mptf.hcl
data "module" "for_order" {}

# When the tf-dir has no `module "..." {}` calls, this evaluates 0 times.
data "module_source" "for_order" {
  for_each = data.module.for_order.result
  source   = each.value.source
}

transform "reorder_attributes" "module_full" {
  for_each             = data.module_source.for_order   # ❌ data.module_source.for_order unknown
  target_block_address = each.value.module_address
  ...
}

Running mapotf transform against a tf-dir with zero modules errors with unknown reference: data.module_source.for_order.

Current workaround

Wrap the consumer with try():

transform "reorder_attributes" "module_full" {
  for_each = try(data.module_source.for_order, {})
  ...
}

The Azure AVM governance pipeline ships this workaround in mapotf-configs/order_module_attrs.mptf.hcl at https://github.com/Azure/avm-terraform-governance/blob/main/mapotf-configs/order_module_attrs.mptf.hcl with a comment block explaining the quirk. Cost is roughly nil at runtime but it's a foot-gun for anyone authoring new for_each-driven chains.

Suggested fix

In the data-block plan loop, when for_each is non-null but the evaluated collection is empty:

  1. Still register the data.<type>.<name> namespace in the eval context.
  2. Bind it to an empty cty.ObjectVal{} (or equivalent empty collection matching the block's value type).

This mirrors Terraform's own behaviour where for_each = [] on a resource produces an empty map under <resource_type>.<name> rather than an unknown reference.

Estimated cost: ~5 lines in the data-block iteration path, plus one test.

Impact

  • Not a regression — pre-existing behaviour back to v0.1.0
  • Low severity (documented try() workaround is reliable)
  • High ergonomic win (eliminates a documented foot-gun for data.module / data.module_source chains)

Credit

Surfaced during real-world deployment in the Azure AVM Terraform governance pipeline alongside the v0.1.4 cleanup PR (Azure/avm-terraform-governance#472). Reported by the governance pipeline maintainer; verified in mapotf v0.1.4.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions