// evaluateForEachExpressionValue is like evaluateForEachExpression
// except that it returns a cty.Value map or set which can be unknown.
evaluateForEachExpressionValue is like evaluateForEachExpression
except that it returns a cty.Value map or set which can be unknown.
// evaluateForEachExpressionValue is like evaluateForEachExpression // except that it returns a cty . Value map or set which can be unknown .
func evaluateForEachExpressionValue(expr hcl.Expression, ctx EvalContext) (cty.Value, tfdiags.Diagnostics) {
	var diags tfdiags.Diagnostics
	nullMap := cty.NullVal(cty.Map(cty.DynamicPseudoType))
	if expr == nil {
		return nullMap, diags
	}
	forEachVal, forEachDiags := ctx.EvaluateExpr(expr, cty.DynamicPseudoType, nil)
	diags = diags.Append(forEachDiags)
	if forEachVal.IsMarked() {
		diags = diags.Append(&hcl.Diagnostic{
			Severity:   hcl.DiagError,
			Summary:    "Invalid for_each argument",
			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,
		})
	}
	if diags.HasErrors() {
		return nullMap, diags
	}
	ty := forEachVal.Type()
	switch {
	case forEachVal.IsNull():
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid for_each argument",
			Detail:   `The given "for_each" argument value is unsuitable: the given "for_each" argument value is null. A map, or set of strings is allowed.`,
			Subject:  expr.Range().Ptr(),
		})
		return nullMap, diags
	case !forEachVal.IsKnown():
		return cty.UnknownVal(cty.Map(cty.DynamicPseudoType)), diags
	case !(ty.IsMapType() || ty.IsSetType() || ty.IsObjectType()):
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid for_each argument",
			Detail:   fmt.Sprintf(`The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or set of strings, and you have provided a value of type %s.`, ty.FriendlyName()),
			Subject:  expr.Range().Ptr(),
		})
		return nullMap, diags
	case markSafeLengthInt(forEachVal) == 0:
		return forEachVal, diags
	}
	if ty.IsSetType() {
		if !forEachVal.IsWhollyKnown() {
			return cty.UnknownVal(ty), diags
		}
		if ty.ElementType() != cty.String {
			diags = diags.Append(&hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Invalid for_each set argument",
				Detail:   fmt.Sprintf(`The given "for_each" argument value is unsuitable: "for_each" supports maps and sets of strings, but you have provided a set containing type %s.`, forEachVal.Type().ElementType().FriendlyName()),
				Subject:  expr.Range().Ptr(),
			})
			return cty.NullVal(ty), diags
		}
		it := forEachVal.ElementIterator()
		for it.Next() {
			item, _ := it.Element()
			if item.IsNull() {
				diags = diags.Append(&hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  "Invalid for_each set argument",
					Detail:   fmt.Sprintf(`The given "for_each" argument value is unsuitable: "for_each" sets must not contain null values.`),
					Subject:  expr.Range().Ptr(),
				})
				return cty.NullVal(ty), diags
			}
		}
	}
	return forEachVal, nil
}
func evaluateForEachExpressionValue ( expr hcl . Expression , ctx EvalContext ) ( cty . Value , tfdiags . Diagnostics ) { var diags tfdiags . Diagnostics nullMap := cty . NullVal ( cty . Map ( cty . DynamicPseudoType ) ) if expr == nil { return nullMap , diags } forEachVal , forEachDiags := ctx . EvaluateExpr ( expr , cty . DynamicPseudoType , nil ) diags = diags . Append ( forEachDiags ) if forEachVal . IsMarked ( ) { diags = diags . Append ( & hcl . Diagnostic { Severity : hcl . DiagError , Summary : " " , Detail : " " , Subject : expr . Range ( ) . Ptr ( ) , Expression : expr , } ) } if diags . HasErrors ( ) { return nullMap , diags } ty := forEachVal . Type ( ) switch { case forEachVal . IsNull ( ) : diags = diags . Append ( & hcl . Diagnostic { Severity : hcl . DiagError , Summary : " " , Detail : `The given "for_each" argument value is unsuitable: the given "for_each" argument value is null. A map, or set of strings is allowed.` , Subject : expr . Range ( ) . Ptr ( ) , } ) return nullMap , diags case ! forEachVal . IsKnown ( ) : return cty . UnknownVal ( cty . Map ( cty . DynamicPseudoType ) ) , diags case ! ( ty . IsMapType ( ) || ty . IsSetType ( ) || ty . IsObjectType ( ) ) : diags = diags . Append ( & hcl . Diagnostic { Severity : hcl . DiagError , Summary : " " , Detail : fmt . Sprintf ( `The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or set of strings, and you have provided a value of type %s.` , ty . FriendlyName ( ) ) , Subject : expr . Range ( ) . Ptr ( ) , } ) return nullMap , diags case markSafeLengthInt ( forEachVal ) == 0 : return forEachVal , diags } if ty . IsSetType ( ) { if ! forEachVal . IsWhollyKnown ( ) { return cty . UnknownVal ( ty ) , diags } if ty . ElementType ( ) != cty . String { diags = diags . Append ( & hcl . Diagnostic { Severity : hcl . DiagError , Summary : " " , Detail : fmt . Sprintf ( `The given "for_each" argument value is unsuitable: "for_each" supports maps and sets of strings, but you have provided a set containing type %s.` , forEachVal . Type ( ) . ElementType ( ) . FriendlyName ( ) ) , Subject : expr . Range ( ) . Ptr ( ) , } ) return cty . NullVal ( ty ) , diags } it := forEachVal . ElementIterator ( ) for it . Next ( ) { item , _ := it . Element ( ) if item . IsNull ( ) { diags = diags . Append ( & hcl . Diagnostic { Severity : hcl . DiagError , Summary : " " , Detail : fmt . Sprintf ( `The given "for_each" argument value is unsuitable: "for_each" sets must not contain null values.` ) , Subject : expr . Range ( ) . Ptr ( ) , } ) return cty . NullVal ( ty ) , diags } } } return forEachVal , nil }
evaluateForEachExpressionValue is like evaluateForEachExpression
except that it returns a cty.Value map or set which can be unknown.
evaluateForEachExpressionValue is like evaluateForEachExpression except that it returns a cty . Value map or set which can be unknown .
// 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 // ensure that we have a map, and not a DynamicValue // If the map is empty ({}), return an empty map, because cty will // return nil when representing {} AsValueMap. This also covers an empty // set (toset([])) // since we can't use a set values that are unknown, we treat the // entire set as unknown // A set of strings may contain null, which makes it impossible to // convert to a map, so we must return an error