diff --git a/internal/terraform/context_validate_test.go b/internal/terraform/context_validate_test.go index a02d85cdd..1f0491f1a 100644 --- a/internal/terraform/context_validate_test.go +++ b/internal/terraform/context_validate_test.go @@ -2062,3 +2062,36 @@ output "out" { t.Fatal(diags.ErrWithWarnings()) } } + +func TestContext2Validate_nonNullableVariableDefaultValidation(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` + module "first" { + source = "./mod" + input = null + } + `, + + "mod/main.tf": ` + variable "input" { + type = string + default = "default" + nullable = false + + // Validation expressions should receive the default with nullable=false and + // a null input. + validation { + condition = var.input != null + error_message = "Input cannot be null!" + } + } + `, + }) + + ctx := testContext2(t, &ContextOpts{}) + + diags := ctx.Validate(m) + if diags.HasErrors() { + t.Fatal(diags.ErrWithWarnings()) + } +} diff --git a/internal/terraform/node_module_variable.go b/internal/terraform/node_module_variable.go index ae3450be5..c5e2294ea 100644 --- a/internal/terraform/node_module_variable.go +++ b/internal/terraform/node_module_variable.go @@ -149,10 +149,10 @@ func (n *nodeModuleVariable) Execute(ctx EvalContext, op walkOperation) (diags t switch op { case walkValidate: - val, err = n.evalModuleCallArgument(ctx, true) + val, err = n.evalModuleVariable(ctx, true) diags = diags.Append(err) default: - val, err = n.evalModuleCallArgument(ctx, false) + val, err = n.evalModuleVariable(ctx, false) diags = diags.Append(err) } if diags.HasErrors() { @@ -178,7 +178,7 @@ func (n *nodeModuleVariable) DotNode(name string, opts *dag.DotOpts) *dag.DotNod } } -// evalModuleCallArgument produces the value for a particular variable as will +// evalModuleVariable produces the value for a particular variable as will // be used by a child module instance. // // The result is written into a map, with its key set to the local name of the @@ -190,7 +190,7 @@ func (n *nodeModuleVariable) DotNode(name string, opts *dag.DotOpts) *dag.DotNod // validateOnly indicates that this evaluation is only for config // validation, and we will not have any expansion module instance // repetition data. -func (n *nodeModuleVariable) evalModuleCallArgument(ctx EvalContext, validateOnly bool) (cty.Value, error) { +func (n *nodeModuleVariable) evalModuleVariable(ctx EvalContext, validateOnly bool) (cty.Value, error) { var diags tfdiags.Diagnostics var givenVal cty.Value var errSourceRange tfdiags.SourceRange diff --git a/internal/terraform/transform_module_variable.go b/internal/terraform/transform_module_variable.go index b739bfd6e..a9fa02c4e 100644 --- a/internal/terraform/transform_module_variable.go +++ b/internal/terraform/transform_module_variable.go @@ -3,7 +3,6 @@ package terraform import ( "fmt" - "github.com/hashicorp/hcl/v2/hclsyntax" "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/tfdiags" "github.com/zclconf/go-cty/cty" @@ -94,13 +93,6 @@ func (t *ModuleVariableTransformer) transformSingle(g *Graph, parent, c *configs var expr hcl.Expression if attr := content.Attributes[v.Name]; attr != nil { expr = attr.Expr - } else { - // No expression provided for this variable, so we'll make a - // synthetic one using the variable's default value. - expr = &hclsyntax.LiteralValueExpr{ - Val: v.Default, - SrcRange: v.DeclRange, // This is not exact, but close enough - } } // Add a plannable node, as the variable may expand