hashicorp / terraform-plugin-sdk

Terraform Plugin SDK enables building plugins (providers) to manage any service providers or custom in-house solutions
https://developer.hashicorp.com/terraform/plugin
Mozilla Public License 2.0
439 stars 232 forks source link

SetNew does not work on nested fields #459

Open marclop opened 4 years ago

marclop commented 4 years ago

SDK version

{
  "Path": "github.com/hashicorp/terraform-plugin-sdk",
  "Version": "v1.12.0"
}

Use-cases

Currently, trying to set any nested (computed) field value in a CustomizeDiff function will fail due to a hardcoded false value on d.checkKey() in schema.ResourceDiff.SetNewComputed() and schema.ResourceDiff.SetNew().

This seems to have been changed in commit 1e08e98. Not entirely sure why this change was made, and in any case, the Elasticsearch property is a list, but not a computed one, so not even that would be possible to Get and Set.

My use case is having global values in resources which are used to override nested values.

func Deployment() *schema.Resource {
    return &schema.Resource{
        Schema: map[string]*schema.Schema{
            "version": {
                Type:     schema.TypeString,
                Required: true,
            },

            // Workloads
            "elasticsearch": {
                Type:     schema.TypeList,
                MinItems: 1,
                MaxItems: 1,
                Required: true,
                Elem: &schema.Resource{
                    Schema: map[string]*schema.Schema{
                        // ...
                        "version": {
                            Type:     schema.TypeString,
                            Computed: true,
                        },
                    },
                },
            },
            // ...
        },
        CustomizeDiff: customdiff.All(
            customdiff.IfValueChange("version", func(old, new, meta interface{}) bool {
                return new.(string) > old.(string)
            }, func(d *schema.ResourceDiff, _ interface{}) error {
                // Succeeds obtaining the key.
                log.Println("[DEBUG]", d.Get("elasticsearch.0.version"))
                // Fails setting ANY nested key.
                if err := d.SetNew("elasticsearch.0.version", d.Get("elasticsearch.0.version")); err != nil {
                    return err
                }
                return nil
            }),
        ),
    }
}

Proposal

Make SetNewComputed and SetNew able to set items on a list.

pdecat commented 4 years ago

Hi, the referenced change https://github.com/hashicorp/terraform-plugin-sdk/commit/1e08e982731eb0c0a35fc00dcc3878bb1f790f70#diff-5572654f34bded06e0d3e5eb9ed7d1bf does not really remove any capability to update nested fields, it only catches the error earlier.

Before that change, an error was triggered in the field writer implementation.

Error: Cannot set new diff value for key metadata.0.resource_version: metadata.0.resource_version: can only set full list

https://github.com/hashicorp/terraform-plugin-sdk/blob/v1.13.1/helper/schema/field_writer_map.go#L72

marclop commented 4 years ago

Right, I see.

I wonder what the recommended approach would be when this use-case is required

marclop commented 4 years ago

This still doesn't work with the V2 of the SDK.

joey-squid commented 5 months ago

Can anyone speak to whether this is a limitation in Terraform or in the SDK (or in protocol 5, which seems unlikely)? I'm wondering if porting to use a different plugin framework would help.

bflad commented 5 months ago

Hi @joey-squid 👋 It very likely is only an issue with terraform-plugin-sdk (also where this specific error message is being generated). Terraform has some data consistency rules around plan modifications (e.g. you cannot change a configured value to a different planned value), but otherwise will let you fill in or update computed values regardless of whether it is under a nested block or not. Provider code based on terraform-plugin-framework performing plan modification should not run into this type of problem unless it is something disallowed by Terraform itself.