Is it possible to use DiffSuppressFunc for a TypeMap in Terraform SDK v2?

Viewed 107

When I create a new attribute:

"foo": {
    Type: schema.TypeMap,
    Elem: &schema.Schema{
        Type: schema.TypeString,
    },
    Sensitive:   true,
    Required:    true,
    ForceNew:    false,
},

everything works as expected. However when I add a new property to it:

DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
  return true
},

to ignore every change between state and main.tf (foo property is only being used on CREATE) d.Get("foo") starts returning an empty map which is super confusing. As soon as I remove DiffSuppressFunc d.Get("foo") is not an empty map anymore (as expected).

https://github.com/hashicorp/terraform-plugin-sdk/issues/477 seems to be related.

Update: the issue seems to be the fact that DiffSuppressFunc is being called with

"foo.%" "0" "2"

even before d.Get().

1 Answers

This SDKv2 is based on the SDK originally written for very old versions of Terraform (v0.11 and earlier) and so it uses some conventions that are no longer applicable, which includes this unusual behavior you've seen part of where all of the nested attributes and complex types of a resource type get flattened down into a single map from strings to strings, and in particular a map attribute called foo with a value like { a = 1, b = 2 } ends up being stored in the following idiosyncratic way inside the SDK abstractions:

"foo.%" = "2"
"foo.a" = "1"
"foo.b" = "2"

The foo.% element serves both to record that this is a map (as opposed to foo.#, which would indicate a list) and to record the number of elements in the map, which turns out to not be very useful for maps but is an important part of how the SDK deals with lists, so that it can know which indexes to expect to find.

Unfortunately this implementation detail shows through in DiffSuppressFunc because that particular function is handled very late in the process when the schema.ResourceData object has already been flattened down into a shape like the above, and so what you recieve in that function is raw keys and values after flattening.

For that reason, it isn't really practical or reliable to use DiffSuppressFunc with map or list attributes. Due to this requirement to stay compatible with Terraform v0.11's expectations, it probably never will be. A team at HashiCorp has been working on a replacement for SDKv2 called Terraform Plugin Framework which was designed from the outset around the type system and assumptions of modern Terraform, and so it's in a much better position to support complex types in a reasonable way.

Unfortunately the new framework doesn't currently have a direct equivalent to DiffSuppressFunc since its design is still under consideration, but it is possible in principle to achieve a similar effect via plan modification, albeit with rather more boilerplate. Under the hood, what DiffSuppressFunc is doing is simply overriding the planned value to match the prior state instead of the configuration, which Terraform then understands as the configuration and prior state values being two different ways to write down the same information.

Related