Default values are not being used when the resource is removed from the template

Hello everyone,

I am trying to use a default values for a custom provided using the the terraform plugin framework[ 1]. However, when I remove the field from the template it does not detect the change in the plan and it says “No changes. Your infrastructure matches the configuration.”

These are the schema of my objects

			"description": schema.StringAttribute{
				Description: "Report description",
				Optional:    true,
				Default:     stringdefault.StaticString(""),
				Computed:    true,
			},

		      "include_promotional_credits": schema.BoolAttribute{
			  Description: "",
			  Optional: true,
			  Computed: true,
			  Default:  booldefault.StaticBool(false),
		      },

                    "advanced_analysis": schema.SingleNestedAttribute{
				Attributes: map[string]schema.Attribute{
					"forecast": schema.BoolAttribute{
						Description: "",
							Optional:    true,
							Computed:    true,
							Default:     booldefault.StaticBool(false),
						},
						"not_trending": schema.BoolAttribute{
							Description: "",
							Optional:    true,
							Computed:    true,
							Default:     booldefault.StaticBool(false),
						},
						"trending_down": schema.BoolAttribute{
							Description: "",
							Optional:    true,
							Computed:    true,
							Default:     booldefault.StaticBool(false),
						},
						"trending_up": schema.BoolAttribute{
							Description: "",
							Optional:    true,
							Computed:    true,
							Default:     booldefault.StaticBool(false),
						},
					},
					Description: "",
					Optional:    true,
					Computed:    true,
					Default:    objectdefault.StaticValue(types.ObjectValueMust(AdvancedAnalysisModel{}.attrTypes(), AdvancedAnalysisModel{}.defaultObject())),
			},

When I create my template as the following

resource "doit-console_report" "my-report" {
  name = "test10"
  description = "a"
  include_promotional_credits = true
}

But if I remove the fields

resource "doit-console_report" "my-report" {
  name = "test10"
}

It says:

No changes. Your infrastructure matches the configuration.

I believe it is related with the default value being the same value as the empty value

[1] Plugin Development - Framework: Default | Terraform | HashiCorp Developer

Hi @dianibar :wave:

I’ve tried to reproduce the issue you describe using the following:

func (e *exampleResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
	resp.Schema = schema.Schema{
		Attributes: map[string]schema.Attribute{
			"description": schema.StringAttribute{
				Description: "Report description",
				Optional:    true,
				Default:     stringdefault.StaticString(""),
				Computed:    true,
			},
		},
	}
}

type exampleResourceData struct {
	Description types.String `tfsdk:"description"`
}

func (e *exampleResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
	var data exampleResourceData

	diags := req.Plan.Get(ctx, &data)
	resp.Diagnostics.Append(diags...)

	if resp.Diagnostics.HasError() {
		return
	}

	tflog.Trace(ctx, "created a resource")

	diags = resp.State.Set(ctx, &data)
	resp.Diagnostics.Append(diags...)
}

func (e *exampleResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
	var data exampleResourceData

	diags := req.State.Get(ctx, &data)
	resp.Diagnostics.Append(diags...)

	if resp.Diagnostics.HasError() {
		return
	}

	diags = resp.State.Set(ctx, &data)
	resp.Diagnostics.Append(diags...)
}

func (e *exampleResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
	var data exampleResourceData

	diags := req.Plan.Get(ctx, &data)
	resp.Diagnostics.Append(diags...)

	if resp.Diagnostics.HasError() {
		return
	}

	diags = resp.State.Set(ctx, &data)
	resp.Diagnostics.Append(diags...)
}

func (e *exampleResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
	var data exampleResourceData

	diags := req.State.Get(ctx, &data)
	resp.Diagnostics.Append(diags...)

	if resp.Diagnostics.HasError() {
		return
	}
}

Using the following Terraform configuration, I see this output:

resource "example_resource" "example" {
  description = "some-value"
}
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # example_resource.example will be created
  + resource "example_resource" "example" {
      + description = "some-value"
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

example_resource.example: Creating...
example_resource.example: Creation complete after 0s

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Then removing description from the Terraform configuration:

resource "example_resource" "example" {
}
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:

  # example_resource.example will be updated in-place
  ~ resource "example_resource" "example" {
      - description = "some-value" -> null
    }

Plan: 0 to add, 1 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

example_resource.example: Modifying...
example_resource.example: Modifications complete after 0s

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.

Could you perhaps share the complete schema you are using (including the code you’re using for AdvancedAnalysisModel) along with the CRUD methods you have defined.

Hi Bendbennett,

Thanks a lot for taking the time, I checked and if i leave advanced_analysis, the description and the include_promotional_credits work. Therefore while I was getting the code to copy it here I found my error:


type AdvancedAnalysisModel struct {
	Forecast     types.Bool `tfsdk:"forecast"`
	NotTrending  types.Bool `tfsdk:"not_trending"`
	TrendingDown types.Bool `tfsdk:"trending_down"`
	TrendingUp   types.Bool `tfsdk:"trending_up"`
}

func (aa AdvancedAnalysisModel) attrTypes() map[string]attr.Type {
	return map[string]attr.Type{
		"Forecast":     types.BoolType,
		"NotTrending":  types.BoolType,
		"TrendingDown": types.BoolType,
		"TrendingUp":   types.BoolType,
	}
}

func (aa AdvancedAnalysisModel) defaultObject() map[string]attr.Value {
	log.Println("defaultObject AdvancedAnalysisModel")
	return map[string]attr.Value{
		"Forecast":     types.BoolValue(true),
		"NotTrending":  types.BoolValue(false),
		"TrendingDown": types.BoolValue(false),
		"TrendingUp":   types.BoolValue(false),
	}
}

I was using the Model variables names instead the of the terraform console one after changed the AdvancedAnalysisModel functions like this, the issue was fixed:

func (aa AdvancedAnalysisModel) attrTypes() map[string]attr.Type {
	return map[string]attr.Type{
		"forecast":     types.BoolType,
		"not_trending":  types.BoolType,
		"trending_down": types.BoolType,
		"trending_up":   types.BoolType,
	}
}

func (aa AdvancedAnalysisModel) defaultObject() map[string]attr.Value {
	log.Println("defaultObject AdvancedAnalysisModel")
	return map[string]attr.Value{
		"forecast":     types.BoolValue(true),
		"not_trending":  types.BoolValue(false),
		"trending_down": types.BoolValue(false),
		"trending_up":   types.BoolValue(false),
	}
}

Thanks a lot.