Backport of validation: don't strip marks from variables during validation into v1.13 (#37604)

* backport of commit 27ea08b0c2

* validation: don't strip marks from variables during validation

---------

Co-authored-by: Liam Cervante <liam.cervante@hashicorp.com>
pull/37631/head
github-actions[bot] 5 months ago committed by GitHub
parent afab18e435
commit 54ceb05282
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
kind: BUG FIXES
body: 'variable validation: keep sensitive and ephemeral metadata when evaluating variable conditions.'
time: 2025-09-11T14:20:38.411183+02:00
custom:
Issue: "37595"

@ -6974,7 +6974,7 @@ func TestContext2Plan_variableCustomValidationsCrossRef(t *testing.T) {
}
}
func TestContext2Plan_variableCustomValidationsSensitive(t *testing.T) {
func TestContext2Plan_variableCustomValidationsChildSensitive(t *testing.T) {
m := testModule(t, "validate-variable-custom-validations-child-sensitive")
p := testProvider("test")
@ -6993,6 +6993,39 @@ func TestContext2Plan_variableCustomValidationsSensitive(t *testing.T) {
}
}
func TestContext2Plan_variableCustomValidationsSensitive(t *testing.T) {
m := testModule(t, "validate-variable-custom-validations-sensitive")
p := testProvider("test")
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
_, diags := ctx.Plan(m, states.NewState(), SimplePlanOpts(plans.NormalMode, InputValues{
"input": {
Value: cty.StringVal("short"),
},
}))
if len(diags) != 1 {
t.Fatal("wanted exactly one error")
}
if diff := cmp.Diff(diags[0].Description(), tfdiags.Description{
Summary: "Invalid value for variable",
Detail: "too short\n\nThis was checked by the validation rule at testdata/validate-variable-custom-validations-sensitive/validate-variable-custom-validations-sensitive.tf:4,3-13.",
}); len(diff) > 0 {
t.Error(diff)
}
vars := diags[0].FromExpr().EvalContext.Variables["var"].AsValueMap()
_, ms := vars["input"].Unmark()
if _, ok := ms[marks.Sensitive]; !ok {
t.Error("should have been marked as sensitive")
}
}
func TestContext2Plan_nullOutputNoOp(t *testing.T) {
// this should always plan a NoOp change for the output
m := testModuleInline(t, map[string]string{

@ -281,31 +281,33 @@ func evalVariableValidations(addr addrs.AbsInputVariableInstance, ctx EvalContex
// Although that behavior was accidental, it makes simple validation rules
// more useful and is protected by compatibility promises, and so we'll
// fake it here by overwriting the unknown value that scope.EvalContext
// will have inserted with a possibly-more-known value using the same
// strategy our special code used to use.
ourVal := ctx.NamedValues().GetInputVariableValue(addr)
if ourVal != cty.NilVal {
// (it would be weird for ourVal to be nil here, but we'll tolerate it
// because it was scope.EvalContext's responsibility to check for the
// absent final value, and even if it didn't we'll just get an
// evaluation error when evaluating the expressions below anyway.)
// Our goal here is to make sure that a reference to the variable
// we're checking will evaluate to ourVal, regardless of what else
// scope.EvalContext might have put in the variables table.
if hclCtx.Variables == nil {
hclCtx.Variables = make(map[string]cty.Value)
}
if varsVal, ok := hclCtx.Variables["var"]; ok {
// Unfortunately we need to unpack and repack the object here,
// because cty values are immutable.
attrs := varsVal.AsValueMap()
attrs[addr.Variable.Name] = ourVal
hclCtx.Variables["var"] = cty.ObjectVal(attrs)
} else {
hclCtx.Variables["var"] = cty.ObjectVal(map[string]cty.Value{
addr.Variable.Name: ourVal,
})
// will have inserted during validate walks with a possibly-more-known value
// using the same strategy our special code used to use.
if validateWalk {
ourVal := ctx.NamedValues().GetInputVariableValue(addr)
if ourVal != cty.NilVal {
// (it would be weird for ourVal to be nil here, but we'll tolerate it
// because it was scope.EvalContext's responsibility to check for the
// absent final value, and even if it didn't we'll just get an
// evaluation error when evaluating the expressions below anyway.)
// Our goal here is to make sure that a reference to the variable
// we're checking will evaluate to ourVal, regardless of what else
// scope.EvalContext might have put in the variables table.
if hclCtx.Variables == nil {
hclCtx.Variables = make(map[string]cty.Value)
}
if varsVal, ok := hclCtx.Variables["var"]; ok {
// Unfortunately we need to unpack and repack the object here,
// because cty values are immutable.
attrs := varsVal.AsValueMap()
attrs[addr.Variable.Name] = ourVal
hclCtx.Variables["var"] = cty.ObjectVal(attrs)
} else {
hclCtx.Variables["var"] = cty.ObjectVal(map[string]cty.Value{
addr.Variable.Name: ourVal,
})
}
}
}

@ -0,0 +1,13 @@
variable "input" {
type = string
validation {
condition = length(var.input) > 5
error_message = "too short"
}
sensitive = true
}
output "value" {
value = var.input
}
Loading…
Cancel
Save