Azure / Azure/mapotf

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

Open
#110 0 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
Go
Stars
60
Forks
19
Avg merge
2h 2m
Merged PRs (30d)
4

Description

## Summary

When a `data` block has `for_each` that evaluates to an empty collection, the resulting namespace (`data..`) is never registered in the evaluation context. Any downstream reference (e.g. `for_each = data..` 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

```hcl
# .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()`:

```hcl
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..` 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 `.` 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](https://github.com/Azure/avm-terraform-governance/pull/472)). Reported by the governance pipeline maintainer; verified in mapotf v0.1.4.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.