From d6e1d26e90439504a830b47987e2b899d7207c50 Mon Sep 17 00:00:00 2001 From: Samsondeen <40821565+dsa0x@users.noreply.github.com> Date: Tue, 1 Apr 2025 09:19:42 +0200 Subject: [PATCH] Import references should not be able to reference the import target (#36801) --- .changes/v1.12/BUG FIXES-20250331-150802.yaml | 5 +++ .../terraform/context_plan_import_test.go | 36 +++++++++++++++++++ internal/terraform/node_resource_validate.go | 5 +++ internal/terraform/validate_selfref.go | 11 ++++++ 4 files changed, 57 insertions(+) create mode 100644 .changes/v1.12/BUG FIXES-20250331-150802.yaml diff --git a/.changes/v1.12/BUG FIXES-20250331-150802.yaml b/.changes/v1.12/BUG FIXES-20250331-150802.yaml new file mode 100644 index 0000000000..f68f0c72e2 --- /dev/null +++ b/.changes/v1.12/BUG FIXES-20250331-150802.yaml @@ -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" diff --git a/internal/terraform/context_plan_import_test.go b/internal/terraform/context_plan_import_test.go index 782b19d3c4..3b23398b6e 100644 --- a/internal/terraform/context_plan_import_test.go +++ b/internal/terraform/context_plan_import_test.go @@ -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)) + } +} diff --git a/internal/terraform/node_resource_validate.go b/internal/terraform/node_resource_validate.go index bb9f8bc401..dee4094fcc 100644 --- a/internal/terraform/node_resource_validate.go +++ b/internal/terraform/node_resource_validate.go @@ -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() { diff --git a/internal/terraform/validate_selfref.go b/internal/terraform/validate_selfref.go index e8d19f7db7..fc22028929 100644 --- a/internal/terraform/validate_selfref.go +++ b/internal/terraform/validate_selfref.go @@ -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. //