Hey there @thiagoarrais
! Thanks for providing that example.
So you’re running into Terraform’s strict data consistency rules, that for the old Plugin SDK were previously allowed to be broken. We have a dedicated page in the SDKv2 documentation that describes the how/what/why of that, which I’ll link below, but I’ll also pull some excerpts out that are specific to your example.
So, as of Terraform 0.12, it’s required that:
- Resources should never set or change an attribute value without the schema
Computed
flag.
- Resources should always set an attribute state value to the exact configuration value or prior state value, if not null.
Both of these rules are being violated with that usage of StateFunc
and are the main reason a similar functionality cannot be achieved with Plugin Framework. If you try to change a configured value in Plugin Framework you’ll get an error like:
examplecloud_thing.this: Modifying...
╷
│ Error: Provider produced inconsistent result after apply
│
│ When applying changes to examplecloud_thing.this, provider "provider[\"TYPE\"]" produced an unexpected
│ new value: .word: was cty.StringVal("value"), but now cty.StringVal("VALUE").
│
│ This is a bug in the provider, which should be reported in the provider's own issue tracker.
The only real solution to this problem is to follow Terraform’s data consistency rules. To migrate that type of schema from SDKv2 to Framework you would need to create a new Computed
attribute that stores the SHA hash of the contents
coming from the config. There is a similar example of this in the local_file
resource: https://github.com/hashicorp/terraform-provider-local/blob/eacefcc827b0838d60d0ae2a3099b8d13fbb4754/internal/provider/resource_local_file.go#L153
With your schema:
func (*FileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"path": schema.StringAttribute{
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"contents": schema.StringAttribute{
Required: true,
CustomType: Base64String,
},
"contents_sha256": schema.StringAttribute{
Computed: true,
},
},
}
}
Unfortunately, solving it this way doesn’t address your original goal, as you still are required to store anything from config in state, byte-for-byte. That’s a Terraform core design decision and isn’t something that the Plugin Framework can step around.