mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Allow for_each arguments containing sensitive values if they aren't keys (#27247)
* Add test for existing behavior, when a value contains a marked value * Allow some marked values as for_each arguments Rather than disallow values that have any marks as for_each arguments, this makes the check more nuanced to disallow cases where the whole value is marked (a whole map, or any set). This allows cases where a user may pass a map that has marked values, but the keys are not sensitive
This commit is contained in:
parent
1970c14a53
commit
428d404d92
@ -22,7 +22,7 @@ func evaluateForEachExpression(expr hcl.Expression, ctx EvalContext) (forEach ma
|
||||
// forEachVal might be unknown, but if it is then there should already
|
||||
// be an error about it in diags, which we'll return below.
|
||||
|
||||
if forEachVal.IsNull() || !forEachVal.IsKnown() || forEachVal.LengthInt() == 0 {
|
||||
if forEachVal.IsNull() || !forEachVal.IsKnown() || markSafeLengthInt(forEachVal) == 0 {
|
||||
// we check length, because an empty set return a nil map
|
||||
return map[string]cty.Value{}, diags
|
||||
}
|
||||
@ -58,16 +58,20 @@ func evaluateForEachExpressionValue(expr hcl.Expression, ctx EvalContext, allowU
|
||||
|
||||
forEachVal, forEachDiags := expr.Value(hclCtx)
|
||||
diags = diags.Append(forEachDiags)
|
||||
if forEachVal.ContainsMarked() {
|
||||
|
||||
// If a whole map is marked, or a set contains marked values (which means the set is then marked)
|
||||
// give an error diagnostic as this value cannot be used in for_each
|
||||
if forEachVal.IsMarked() {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid for_each argument",
|
||||
Detail: "Sensitive variables, or values derived from sensitive variables, cannot be used as for_each arguments. If used, the sensitive value could be exposed as a resource instance key.",
|
||||
Detail: "Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments. If used, the sensitive value could be exposed as a resource instance key.",
|
||||
Subject: expr.Range().Ptr(),
|
||||
Expression: expr,
|
||||
EvalContext: hclCtx,
|
||||
})
|
||||
}
|
||||
|
||||
if diags.HasErrors() {
|
||||
return nullMap, diags
|
||||
}
|
||||
@ -109,7 +113,7 @@ func evaluateForEachExpressionValue(expr hcl.Expression, ctx EvalContext, allowU
|
||||
})
|
||||
return nullMap, diags
|
||||
|
||||
case forEachVal.LengthInt() == 0:
|
||||
case markSafeLengthInt(forEachVal) == 0:
|
||||
// If the map is empty ({}), return an empty map, because cty will
|
||||
// return nil when representing {} AsValueMap. This also covers an empty
|
||||
// set (toset([]))
|
||||
@ -168,3 +172,9 @@ func evaluateForEachExpressionValue(expr hcl.Expression, ctx EvalContext, allowU
|
||||
}
|
||||
|
||||
const errInvalidForEachUnknownDetail = `The "for_each" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the for_each depends on.`
|
||||
|
||||
// markSafeLengthInt allows calling LengthInt on marked values safely
|
||||
func markSafeLengthInt(val cty.Value) int {
|
||||
v, _ := val.UnmarkDeep()
|
||||
return v.LengthInt()
|
||||
}
|
||||
|
@ -52,6 +52,16 @@ func TestEvaluateForEachExpression_valid(t *testing.T) {
|
||||
"b": cty.UnknownVal(cty.Bool),
|
||||
},
|
||||
},
|
||||
"map containing sensitive values, but strings are literal": {
|
||||
hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{
|
||||
"a": cty.BoolVal(true).Mark("sensitive"),
|
||||
"b": cty.BoolVal(false),
|
||||
})),
|
||||
map[string]cty.Value{
|
||||
"a": cty.BoolVal(true).Mark("sensitive"),
|
||||
"b": cty.BoolVal(false),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
@ -110,6 +120,14 @@ func TestEvaluateForEachExpression_errors(t *testing.T) {
|
||||
"Invalid for_each argument",
|
||||
"depends on resource attributes that cannot be determined until apply",
|
||||
},
|
||||
"marked map": {
|
||||
hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{
|
||||
"a": cty.BoolVal(true),
|
||||
"b": cty.BoolVal(false),
|
||||
}).Mark("sensitive")),
|
||||
"Invalid for_each argument",
|
||||
"Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments. If used, the sensitive value could be exposed as a resource instance key.",
|
||||
},
|
||||
"set containing booleans": {
|
||||
hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.BoolVal(true)})),
|
||||
"Invalid for_each set argument",
|
||||
@ -130,6 +148,11 @@ func TestEvaluateForEachExpression_errors(t *testing.T) {
|
||||
"Invalid for_each argument",
|
||||
"depends on resource attributes that cannot be determined until apply",
|
||||
},
|
||||
"set containing marked values": {
|
||||
hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.StringVal("beep").Mark("sensitive"), cty.StringVal("boop")})),
|
||||
"Invalid for_each argument",
|
||||
"Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments. If used, the sensitive value could be exposed as a resource instance key.",
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
|
Loading…
Reference in New Issue
Block a user