diff --git a/config/interpolate_funcs.go b/config/interpolate_funcs.go index 4645d923f..b8bd804c6 100644 --- a/config/interpolate_funcs.go +++ b/config/interpolate_funcs.go @@ -84,6 +84,7 @@ func Funcs() map[string]ast.Function { "title": interpolationFuncTitle(), "trimspace": interpolationFuncTrimSpace(), "upper": interpolationFuncUpper(), + "zipmap": interpolationFuncZipMap(), } } @@ -386,6 +387,39 @@ func interpolationFuncFormat() ast.Function { } } +func interpolationFuncZipMap() ast.Function { + return ast.Function{ + ArgTypes: []ast.Type{ + ast.TypeList, // Keys + ast.TypeList, // Values + }, + ReturnType: ast.TypeMap, + Callback: func(args []interface{}) (interface{}, error) { + keys := args[0].([]ast.Variable) + values := args[1].([]ast.Variable) + + if len(keys) != len(values) { + return nil, fmt.Errorf("count of keys (%d) does not match count of values (%d)", + len(keys), len(values)) + } + + for i, val := range keys { + if val.Type != ast.TypeString { + return nil, fmt.Errorf("keys must be strings. value at position %d is %s", + i, val.Type.Printable()) + } + } + + result := map[string]ast.Variable{} + for i := 0; i < len(keys); i++ { + result[keys[i].Value.(string)] = values[i] + } + + return result, nil + }, + } +} + // interpolationFuncFormatList implements the "formatlist" function that does // string formatting on lists. func interpolationFuncFormatList() ast.Function { diff --git a/config/interpolate_funcs_test.go b/config/interpolate_funcs_test.go index dbfa113f6..89c147a90 100644 --- a/config/interpolate_funcs_test.go +++ b/config/interpolate_funcs_test.go @@ -11,6 +11,115 @@ import ( "github.com/hashicorp/hil/ast" ) +func TestInterpolateFuncZipMap(t *testing.T) { + testFunction(t, testFunctionConfig{ + Cases: []testFunctionCase{ + { + `${zipmap(var.list, var.list2)}`, + map[string]interface{}{ + "Hello": "bar", + "World": "baz", + }, + false, + }, + { + `${zipmap(var.list, var.nonstrings)}`, + map[string]interface{}{ + "Hello": []interface{}{"bar", "baz"}, + "World": []interface{}{"boo", "foo"}, + }, + false, + }, + { + `${zipmap(var.nonstrings, var.list2)}`, + nil, + true, + }, + { + `${zipmap(var.list, var.differentlengthlist)}`, + nil, + true, + }, + }, + Vars: map[string]ast.Variable{ + "var.list": { + Type: ast.TypeList, + Value: []ast.Variable{ + { + Type: ast.TypeString, + Value: "Hello", + }, + { + Type: ast.TypeString, + Value: "World", + }, + }, + }, + "var.list2": { + Type: ast.TypeList, + Value: []ast.Variable{ + { + Type: ast.TypeString, + Value: "bar", + }, + { + Type: ast.TypeString, + Value: "baz", + }, + }, + }, + "var.differentlengthlist": { + Type: ast.TypeList, + Value: []ast.Variable{ + { + Type: ast.TypeString, + Value: "bar", + }, + { + Type: ast.TypeString, + Value: "baz", + }, + { + Type: ast.TypeString, + Value: "boo", + }, + }, + }, + "var.nonstrings": { + Type: ast.TypeList, + Value: []ast.Variable{ + { + Type: ast.TypeList, + Value: []ast.Variable{ + { + Type: ast.TypeString, + Value: "bar", + }, + { + Type: ast.TypeString, + Value: "baz", + }, + }, + }, + { + Type: ast.TypeList, + Value: []ast.Variable{ + { + Type: ast.TypeString, + Value: "boo", + }, + { + Type: ast.TypeString, + Value: "foo", + }, + }, + }, + }, + }, + }, + }) +} + func TestInterpolateFuncList(t *testing.T) { testFunction(t, testFunctionConfig{ Cases: []testFunctionCase{ diff --git a/website/source/docs/configuration/interpolation.html.md b/website/source/docs/configuration/interpolation.html.md index 42e28e173..c42c828e1 100644 --- a/website/source/docs/configuration/interpolation.html.md +++ b/website/source/docs/configuration/interpolation.html.md @@ -252,6 +252,13 @@ The supported built-in functions are: returned by the `keys` function. This function only works on flat maps and will return an error for maps that include nested lists or maps. + * `zipmap(list, list)` - Creates a map from a list of keys and a list of + values. The keys must all be of type string, and the length of the lists + must be the same. + For example, to output a mapping of AWS IAM user names to the fingerprint + of the key used to encrypt their initial password, you might use: + `zipmap(aws_iam_user.users.*.name, aws_iam_user_login_profile.users.*.key_fingerprint)`. + ## Templates