Hi all,
I’m writing my first Terraform provider, using the new Framework. I’ve got quite a lot implemented already, but I’m stuck when trying to use ModifyPlan to dynamically modify the expected state.
I have a “rally configuration” resource in a remote API that is automatically updated by the API as part of the resource creation/update. For example, if I try to create/update a rally configuration with the value {"hello": "world"}
, then the API will return the value {"hello": "world", "PresetName": "something"}
. I want to work out how to model this in my Terraform provider. I am allowing the user to set {"hello": "world"}
but Terraform errors when procesing the response from the API as it does not exactly match the configuration value the user provided.
I’ve tried to use ModifyPlan to model this:
func (r presetResource) ModifyPlan(
ctx context.Context,
req resource.ModifyPlanRequest,
resp *resource.ModifyPlanResponse,
) {
// If the entire plan is null, the resource is planned for destruction.
if req.Plan.Raw.IsNull() {
return
}
// Set the PresetName inside the rallyConfiguration, so that Terraform knows what
// to expect as a response from the SDVI Rally API
rallyConfigurationString := ""
diags := resp.Plan.GetAttribute(ctx,
path.Root("rally_configuration"),
&rallyConfigurationString,
)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
rallyConfiguration := map[string]any{}
err := json.Unmarshal([]byte(rallyConfigurationString), &rallyConfiguration)
if err != nil {
resp.Diagnostics.AddError(
"Error modifying plan",
"Could not set modify rally configuration: "+err.Error(),
)
return
}
name := ""
diags = resp.Plan.GetAttribute(ctx,
path.Root("name"),
&name,
)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
rallyConfiguration["PresetName"] = name
rallyConfigurationBytes, err := jettison.Marshal(rallyConfiguration)
if err != nil {
resp.Diagnostics.AddError(
"Error modifying plan",
"Could not set modify rally configuration: "+err.Error(),
)
}
resp.Plan.SetAttribute(
ctx,
path.Root("rally_configuration"),
string(rallyConfigurationBytes),
)
resp.RequiresReplace.Append(path.Root("rally_configuration"))
}
When I try to terraform plan
with this code I get an error:
╷
│ Error: Provider produced invalid plan
│
│ Provider "sky.local/gap/sdvi-rally" planned an invalid value for rally_preset.minimal_example.rally_configuration: planned value
│ cty.StringVal("{\"InputFileLabel\":\"a-label\",\"OutputStorageName\":\"what-a-lovely-bucket\",\"PresetName\":\"xxxxxxx\",\"ProxyTypeName\":\"sdvi_proxy\"}") does
│ not match config value cty.StringVal("{\"InputFileLabel\":\"a-label\",\"OutputStorageName\":\"what-a-lovely-bucket\",\"ProxyTypeName\":\"sdvi_proxy\"}\n").
│
│ This is a bug in the provider, which should be reported in the provider's own issue tracker.
I’m struggling because I want the planned value to be different from the config value!
I’m struggling to find resources about how to use ModifyPlan and/or how to write custom providers with this level of complexity. Can someone help me out please?
Thanks,
Craig