Skip to content

Commit

Permalink
Validate self-references within resource count and foreach arguments (#…
Browse files Browse the repository at this point in the history
…35047)

* Validate self-references within resource count and foreach arguments

* add nil check
  • Loading branch information
liamcervante authored Apr 23, 2024
1 parent d1f9d3b commit 5294d1f
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 2 deletions.
14 changes: 13 additions & 1 deletion internal/terraform/node_resource_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,25 @@ func (n *nodeExpandPlannableResource) ModifyCreateBeforeDestroy(v bool) error {
}

func (n *nodeExpandPlannableResource) DynamicExpand(ctx EvalContext) (*Graph, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics

// First, make sure the count and the foreach don't refer to the same
// resource. The config maybe nil if we are generating configuration, or
// deleting a resource.
if n.Config != nil {
diags = diags.Append(validateSelfRefInExpr(n.Addr.Resource, n.Config.Count))
diags = diags.Append(validateSelfRefInExpr(n.Addr.Resource, n.Config.ForEach))
if diags.HasErrors() {
return nil, diags
}
}

// Expand the current module.
expander := ctx.InstanceExpander()
moduleInstances := expander.ExpandModule(n.Addr.Module)

// Expand the imports for this resource.
// TODO: Add support for unknown instances in import blocks.
var diags tfdiags.Diagnostics
imports, importDiags := n.expandResourceImports(ctx)
diags = diags.Append(importDiags)

Expand Down
31 changes: 31 additions & 0 deletions internal/terraform/validate_selfref.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,37 @@ func validateSelfRef(addr addrs.Referenceable, config hcl.Body, providerSchema p
return diags
}

// validateSelfRefInExpr checks to ensure that a specific expression does not
// reference the same block that it is contained within.
func validateSelfRefInExpr(addr addrs.Referenceable, expr hcl.Expression) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics

addrStrs := make([]string, 0, 1)
addrStrs = append(addrStrs, addr.String())
switch tAddr := addr.(type) {
case addrs.ResourceInstance:
// A resource instance may not refer to its containing resource either.
addrStrs = append(addrStrs, tAddr.ContainingResource().String())
}

refs, _ := langrefs.ReferencesInExpr(addrs.ParseRef, expr)
for _, ref := range refs {

for _, addrStr := range addrStrs {
if ref.Subject.String() == addrStr {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Self-referential block",
Detail: fmt.Sprintf("Configuration for %s may not refer to itself.", addrStr),
Subject: ref.SourceRange.ToHCL().Ptr(),
})
}
}
}

return diags
}

// Legacy provisioner configurations may refer to single instances using the
// resource address. We need to filter these out from the reported references
// to prevent cycles.
Expand Down
16 changes: 15 additions & 1 deletion internal/terraform/validate_selfref_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import (

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hcltest"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/zclconf/go-cty/cty"

"github.com/hashicorp/terraform/internal/addrs"
)

func TestValidateSelfRef(t *testing.T) {
Expand Down Expand Up @@ -98,6 +99,8 @@ func TestValidateSelfRef(t *testing.T) {
},
}

// First test the expression within the context of the configuration
// body.
diags := validateSelfRef(test.Addr, body, ps)
if diags.HasErrors() != test.Err {
if test.Err {
Expand All @@ -106,6 +109,17 @@ func TestValidateSelfRef(t *testing.T) {
t.Errorf("unexpected error\n\n%s", diags.Err())
}
}

// We can use the same expressions to test the expression
// validation.
diags = validateSelfRefInExpr(test.Addr, test.Expr)
if diags.HasErrors() != test.Err {
if test.Err {
t.Errorf("unexpected success; want error")
} else {
t.Errorf("unexpected error\n\n%s", diags.Err())
}
}
})
}
}

0 comments on commit 5294d1f

Please sign in to comment.