diff --git a/internal/configs/named_values.go b/internal/configs/named_values.go index 286c507a11..43a624d9d8 100644 --- a/internal/configs/named_values.go +++ b/internal/configs/named_values.go @@ -180,8 +180,9 @@ func decodeVariableBlock(block *hcl.Block, override bool) (*Variable, hcl.Diagno case "validation": vv, moreDiags := decodeCheckRuleBlock(block, override) diags = append(diags, moreDiags...) - v.Validations = append(v.Validations, vv) + diags = append(diags, checkVariableValidationBlock(v.Name, vv)...) + v.Validations = append(v.Validations, vv) default: // The above cases should be exhaustive for all block types // defined in variableBlockSchema @@ -504,3 +505,29 @@ var outputBlockSchema = &hcl.BodySchema{ {Type: "postcondition"}, }, } + +func checkVariableValidationBlock(varName string, vv *CheckRule) hcl.Diagnostics { + var diags hcl.Diagnostics + + if vv.Condition != nil { + // The validation condition must include a reference to the variable itself + for _, traversal := range vv.Condition.Variables() { + ref, moreDiags := addrs.ParseRef(traversal) + if !moreDiags.HasErrors() { + if addr, ok := ref.Subject.(addrs.InputVariable); ok { + if addr.Name == varName { + return nil + } + } + } + } + + return diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid variable validation condition", + Detail: fmt.Sprintf("The condition for variable %q must refer to var.%s in order to test incoming values.", varName, varName), + Subject: vv.Condition.Range().Ptr(), + }) + } + return nil +} diff --git a/internal/configs/testdata/invalid-files/variable-validation-condition-noself.tf b/internal/configs/testdata/invalid-files/variable-validation-condition-noself.tf new file mode 100644 index 0000000000..3571a58ab1 --- /dev/null +++ b/internal/configs/testdata/invalid-files/variable-validation-condition-noself.tf @@ -0,0 +1,10 @@ +locals { + something = "else" +} + +variable "validation" { + validation { + condition = local.something == "else" + error_message = "Something else." + } +}