Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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: "36801"
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