Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/v1.12/BUG FIXES-20250331-150802.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: BUG FIXES
body: for_each expressions in import blocks should not be able to reference the import target
time: 2025-03-31T15:08:02.156881+02:00
custom:
Issue: "36672"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should point to the PR, not the issue

36 changes: 36 additions & 0 deletions internal/terraform/context_plan_import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1890,3 +1890,39 @@ output "foo" {
t.Errorf("should have reported the cycle to contain the target resource, but got %s", got)
}
}

// https://github.com/hashicorp/terraform/issues/36672
func TestContext2Plan_importSelfReferenceInForEach(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `
resource "test_resource" "a" {
count = 2
}

import {
# the block references the same resource it is importing into
for_each = { for _, v in test_resource.a : v => v }
to = test_resource.a[each.key]
id = concat("importable-", each.key)
}
`,
})

p := testProvider("test")
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
diags := ctx.Validate(m, &ValidateOpts{})

// We're expecting exactly one diag, which is the self-reference error.
if len(diags) != 1 {
t.Fatalf("expected one diag, got %d: %s", len(diags), diags.ErrWithWarnings())
}

got, want := diags.Err().Error(), "Invalid for_each argument: The for_each expression cannot reference the resource being imported."
if cmp.Diff(want, got) != "" {
t.Fatalf("unexpected error\n%s", cmp.Diff(want, got))
}
}
5 changes: 5 additions & 0 deletions internal/terraform/node_resource_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,11 @@ func (n *NodeValidatableResource) validateImportTargets(ctx EvalContext) tfdiags
}

if imp.Config.ForEach != nil {
diags = diags.Append(validateImportForEachRef(n.Addr.Resource, imp.Config.ForEach))
if diags.HasErrors() {
return diags
}

forEachData, _, forEachDiags := newForEachEvaluator(imp.Config.ForEach, ctx, true).ImportValues()
diags = diags.Append(forEachDiags)
if forEachDiags.HasErrors() {
Expand Down
11 changes: 11 additions & 0 deletions internal/terraform/validate_selfref.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@ func validateImportSelfRef(addr addrs.Resource, expr hcl.Expression) tfdiags.Dia
})
}

func validateImportForEachRef(addr addrs.Resource, expr hcl.Expression) tfdiags.Diagnostics {
return validateSelfRefFromExprInner(addr, expr, func(ref *addrs.Reference) *hcl.Diagnostic {
return &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid for_each argument",
Detail: "The for_each expression cannot reference the resource being imported.",
Subject: ref.SourceRange.ToHCL().Ptr(),
}
})
}

// validateSelfRefFromExprInner is a helper function that takes an address and
// an expression and returns diagnostics for self-references in the expression.
//
Expand Down