Terraform framework Optional inside a SetNestedAttribute produces a `does not correlate with any element in actual`

Hi, this is my first time using the framework instead of the sdk plugin.
I have the following question, how to handle the following scenario

At the start of the create I perform a search to check if the resource exists in the lab, and if it does, I load it, but when I load the resource I get an error.

I have a list of objects with three parameters that are part of the create or update, but the get only returns two of the parameters, causing the following error: does not correlate with any element in actual..
How can I handle it?

Part of the schema

			"radius_servers": schema.SetNestedAttribute{
				PlanModifiers: []planmodifier.Set{
					setplanmodifier.UseStateForUnknown(),
				},
				MarkdownDescription: `The RADIUS 802.1x servers to be used for authentication.`,
				Optional:            true,
				Computed:            true,
				NestedObject: schema.NestedAttributeObject{
					Attributes: map[string]schema.Attribute{
						"host": schema.StringAttribute{
							PlanModifiers: []planmodifier.String{
								stringplanmodifier.UseStateForUnknown(),
							},
							MarkdownDescription: `The IP address of your RADIUS server.`,
							Optional:            true,
							Computed:            true,
						},
						"port": schema.Int64Attribute{
							PlanModifiers: []planmodifier.Int64{
								int64planmodifier.UseStateForUnknown(),
							},
							MarkdownDescription: `The UDP port your RADIUS servers listens on for Access-requests.`,
							Optional:            true,
							Computed:            true,
						},
						"secret": schema.StringAttribute{
							PlanModifiers: []planmodifier.String{
								stringplanmodifier.UseStateForUnknown(),
							},
							MarkdownDescription: `The RADIUS client shared secret.`,
							Optional:            true,
						},
					},
				},
			},

Part of the code where I assign the get response

	if response.RadiusServers != nil {
		result := make([]ResponseApplianceGetNetworkApplianceSsidRadiusServersRs, len(*response.RadiusServers))
		for i, radiusServers := range *response.RadiusServers {
			result[i] = ResponseApplianceGetNetworkApplianceSsidRadiusServersRs{
				Host:   types.StringValue(radiusServers.Host),
				Port:   types.Int64Value(int64(*radiusServers.Port)),
			}
		}
		r.RadiusServers = &result
	}

provider “provider["hashicorp.com/edu/meraki"]” produced an unexpected new
│ value: .radius_servers: planned set element cty.ObjectVal(map[string]cty.Value{“host”:cty.StringVal(“1.2.3.4”), “port”:cty.NumberIntVal(1000),
│ “secret”:cty.StringVal(“secret”)}) does not correlate with any element in actual.


What can I do in this case?
Is there any way to ignore that?
If you can provide me with an example, I would appreciate it.

Hi @bvargasre,

Terraform itself requires that a provider must produce a new state that matches what was proposed during the plan phase, or return an error explaining why that isn’t possible.

A set of objects that contains a mixture of user-provided and API-provided attributes, as you seem to have here, makes that contract hard to uphold because set elements are identified only by their content and so Terraform must try to find an element from the plan that seems to match each element of the final result, or vice-versa.

Aside from that, API-decided attributes (“computed” attributes, in Terraform’s terminology) inside a set of objects are also pretty inconvenient for use elsewhere in the configuration given that there’s no separate index or key to look them up by, and so module authors must resort to complicated for expressions to project the set into a more useful data structure, such as a map.

With that in mind, I have two recommendations:

  1. Make all of the attributes in this set be either required or optional, and not “computed”. Writing a set of objects is convenient for declaring the argument values, but it’s not convenient for referring to generated values from expressions elsewhere.
  2. If you have any attributes you need to return from the API, rather than being specified by the module author, declare a separate attribute that’s entirely “computed” and whose type is something that’s more ergonomic to use in expression references, such as a map of objects where the keys are something an author can predict and access directly without having to reshape the structure themselves first.

This will firstly make the provider API more ergonomic to use, but it will also secondarily help with your problem, because the set of objects will then always exactly match what the author wrote in their configuration, and the separate result attribute will be exclusively generated by the provider and never have to agree with something the module author specified, and therefore you won’t have any situations where Terraform needs to try to match a configuration object with a computed object to prove that the provider is behaving correctly.

1 Like