Resource schema: TypeList as element for TypeMap

Hello!

I’m creating a provider where one of resource configurations is a map of string slices map[string][]string. Proposed config will look smth like this:

headers = {
  Content-Type = ["text/html", "charset=utf-8"]
}

Schema that I wanted to use:

"headers": {
				Type:     schema.TypeMap,
				Optional: true,
				Elem: &schema.Schema{
					Type: schema.TypeList,
					Elem: &schema.Schema{Type: schema.TypeString},
				},
			}

But I see that TypeList is not supported as TypeMap element.
Currently I work around this by using TypeString and then splitting it by ; to create config value.

What would be a better way to manage this? Should I stick to my workaround or maybe I should file an issue for sdk and try adding support for map of lists?

The only option I see besides the workaround you posted is to have a resource called “header_key” with a “key” field and a “values” field, with the values field being a list, and then some sort of identifier that identifies the resource this field is split out from.

But I know that’s kind of a hack and not very nice. This is largely a limitation in the Terraform 0.11 and below type system. Terraform 0.12 fixed it, but we haven’t redesigned the SDK to use the new type system yet. In the new type system, your original instinct should be possible, I believe. Unfortunately, that’s just a matter of waiting for the new SDK.

Hi, i’m trying todo just this with sdk v1.13.0 and whilst the schema doesn’t complain when you model the list in the map. However when setting the map of lists the keys are changed to a list representation:

headers := map[string][]string{
	"Content-Type": {"text/html", "charset=utf-8"},
}
d.Set("headers", headers)
setHeaders := d.Get("headers")
fmt.Println(headers)
fmt.Println(setHeaders)
map[Content-Type:[text/html charset=utf-8]]
map[Content-Type.#:2 Content-Type.0:text/html Content-Type.1:charset=utf-8]

So setHeaders in this case has 3 elements instead of 1 element of a list.

Any idea how I can do the Get without it changing the keys?

Edit 1:
I can obviously add something like this:

func expandMapOfLists(in map[string]interface{}) map[string][]string {
	m := map[string][]string{}
	for s := range in {
		i := strings.LastIndex(s, ".")
		first := s[0:i]
		last := s[i+1:]
		if last != "#" {
			m[first] = append(m[first], in[s].(string))
		}
	}
	return m
}

But it would be nice if their was something that was schema aware in the d.Get that returned the base map keys in the interface{}. Is this sort of solution thats recommended with the SDK?

Edit 2: I was just checking the sdk 2.0.0-rc.1 when i tried to put all this together: https://github.com/hashicorp/terraform-plugin-sdk/blob/1f59663aedfa17173240a53e12c5824d59d79ac3/helper/schema/schema.go#L1903 looks like maps still only handle the primitive types :cry:

The behavior you’re seeing is largely taking advantage of the SDK’s internal representation of types, and not even the current representation; it’s the old “flatmap” representation of 0.11 and below that is maintained for compatibility in 0.12 through (sometimes confusing to understand) shims. It is unlikely to be a stable foundation to build upon and I would recommend against it. As you’re seeing now, it will behave in unexpected ways at times, and we won’t be testing for compatibility and to make sure we don’t break it, because it’s relying on implementation details.

As you noted in your second edit, it’s unfortunately still not something we can support. We’re working on a redesign of the SDK that would enable support for more complex types like this, but that is not version 2.0.0.