Boolean flags inside SetNestedBlock

I’m looking for some help to understand this behaviour with my provider tests. I’m migrating away from the old SDK and have a resource with a SetNestedBlock whose nested objects have two boolean flags with default values:

type orderResourceModel struct {
	ID    types.String     `tfsdk:"id"`
	Items []orderItemModel `tfsdk:"item"`
}

type orderItemModel struct {
	FlagA types.Bool `tfsdk:"flag_a"`
	FlagB types.Bool `tfsdk:"flag_b"`
}

func (r *orderResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
	resp.Schema = schema.Schema{
		Attributes: map[string]schema.Attribute{
			"id": schema.StringAttribute{
				Computed:    true,
				PlanModifiers: []planmodifier.String{
					stringplanmodifier.UseStateForUnknown(),
				},
			},
		},
		Blocks: map[string]schema.Block{
			"item": schema.SetNestedBlock{
				NestedObject: schema.NestedBlockObject{
					Attributes: map[string]schema.Attribute{
						"flag_a": schema.BoolAttribute{
							Optional: true,
							Computed: true,
							Default:  booldefault.StaticBool(false),
						},
						"flag_b": schema.BoolAttribute{
							Optional: true,
							Computed: true,
							Default:  booldefault.StaticBool(false),
						},
					},
				},
			},
		},
	}
}

The Create method simply reads what comes from req.Plan and records to resp.State. I’m testing it with configuration flipping just one of the flags:

				Config: providerConfig + `
resource "hashicups_order" "test" {
  item {
    flag_a = true
  }
}
`,

And I get a “plan not empty” error:

$ make testacc 
TF_ACC=1 go test ./... -v  -timeout 120m
?   	terraform-provider-hashicups-pf	[no test files]
=== RUN   TestAccOrderResource
    order_resource_test.go:13: Step 1/1 error: After applying this test step, the plan was not empty.
        stdout:
        
        
        Terraform used the selected providers to generate the following execution
        plan. Resource actions are indicated with the following symbols:
          ~ update in-place
        
        Terraform will perform the following actions:
        
          # hashicups_order.test will be updated in-place
          ~ resource "hashicups_order" "test" {
                id = "42"
        
              - item {
                  - flag_a = true -> null
                  - flag_b = false -> null
                }
              + item {
                  + flag_a = false
                  + flag_b = false
                }
            }
        
        Plan: 0 to add, 1 to change, 0 to destroy.
--- FAIL: TestAccOrderResource (0.77s)

As I understand, the error message says that the resource creation process went OK, but when trying to re-apply the same config a plan that was expected to be empty is trying to reset the flipped flag to its default value. The plan is trying to set flag_a to false regardless of the config explicitly setting its value to be true.

This happens when setting any of the flags to a non-default value and won’t happen when the item is empty (both flags assuming default values) nor when both flags have explicit values (even if they happen to be the same as the defaults’).

I’ve provided a minimal reproducible example in the thiagoarrais/terraform-provider-twoflag-issue-example GitHub repo. In order to reproduce, one needs to clone the repo and run make testacc.

I can’t migrate away from the nested blocks just yet to avoid breaking practioners’ code. What am I doing wrong here?

Anyone else experiencing this?

Update: I’ve managed to work around this by re-implementing the defaults with a custom plan modifier. Maybe this is a framework limitation that will need some attention in the near future.

This seems to be a documented bug in terraform-plugin-framework. I’m getting exactly the behaviour described in Multiple computed BoolAttributes with defaults in SetNestedAttribute cause flip-flopping plan on every apply · Issue #867 · hashicorp/terraform-plugin-framework · GitHub