How to detect O/C nested attribute removals

TL;DR;

Removal of O/C nested attributes don’t trigger a plan change. However when unrelated attributes are changed, a plan change is triggered where the nested attribute is unknown.
While this makes sense, it’s a problem because it’s not possible to tell if the nested attribute was ever defined in the config or not.

Full example

For example, if we start with this definition where read_only_specs is an Optional / Computed SingleNestedAttribute and all its attributes are also Optional / Computed:

resource "mongodbatlas_advanced_cluster" "test" {
  project_id   = "<PROJECT_ID>"
  name         = "test"
  cluster_type = "SHARDED"
  replication_specs = [
    {
      region_configs = [
        {
          provider_name = "AWS"
          region_name   = "US_EAST_1"
          priority      = 7
          electable_specs = {
            node_count    = 3
            instance_size = "M10"
          }
          read_only_specs = {
            node_count    = 1
            instance_size = "M10"
          }
        }
      ]
    }
  ]
}
  1. We do terraform apply to create the resource.
  2. We remove the read_only_specs attribute from the config.
  3. We do terraform plan (or apply) and no plan changes are shown. Debugging the provider with breakpoints, I can see in Read operation that read_only_specs state is known, in ModifyPlan operation both state and plan for read_only_specs are known, config is null as it has been deleted from the definition. This behavior is expected, as read_only_specs is O/C and it’s not anymore in the definition, it behaves as Optional, Read returns the same state, so the plan matches the state and no plan changes are shown.
  4. We change an unrelated attribute, e.g. region_name from US_EAST_1 to US_WEST_2.
  5. We do terraform plan (or apply) and it shows this plan change:
...
 ~ read_only_specs        = {
     ~ disk_iops       = 3000 -> (known after apply)
     ~ disk_size_gb    = 10 -> (known after apply)
     ~ ebs_volume_type = "STANDARD" -> (known after apply)
     ~ instance_size   = "M10" -> (known after apply)
     ~ node_count      = 1 -> (known after apply)
} -> (known after apply)
    ~ region_name            = "US_EAST_1" -> "US_WEST_2"
...

Read operation sends in the response the *known state for read_only_specs, however ModifyPlan and Update receive a plan that is unknown, presumably because it behaves as Optional as it’s not in the config anymore. We have some unknown we don’t send it to our API, and the API assumes that we want to delete that, so the read-only nodes are deleted.

Questions

  1. What is the recommended behavior when an Optional/Computed nested attribute is deleted from the config? Is it to behave like if it was never there and reset to default values (e.g. remove read-only nodes), or it is to keep the current state (e.g. keep read-only nodes), even if they are not in the config anymore, similar to SDKv2 behavior where it behaves like if state is copied to plan.
  • Right now we’re using ModifyPlan to copy values from state to plan so it behaves like SDKv2, keeping the read-only nodes, is this a good approach?
  • Another approach could be to use ModifyPlan to set plan to null so the block will be deleted setting plan to nil, so Update will be called. Is this a good approach?
  • Do you recommend other approaches?
  1. Is there a way to detect when an Optional/Computed attribute has been deleted from the config? As the plan is unknown, how can we differentiate the case where it was never present in the config vs it was deleted.

Thanks.