---
page_title: 'Plugin Development - Framework: Update Resources'
description: >-
  How to implement resource in-place update in the provider development framework.
---

# Update Resources

In-place update is part of the basic Terraform lifecycle for managing resources. During the [`terraform apply` command](/terraform/cli/commands/apply), Terraform calls the provider [`ApplyResourceChange`](/terraform/plugin/framework/internals/rpcs#applyresourcechange-rpc) RPC, in which the framework calls the [`resource.Resource` interface `Update` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#Resource.Update). The request contains Terraform prior state, configuration, and plan data. The response contains updated state data. The data is defined by the [schema](/terraform/plugin/framework/schemas) of the resource.

To ensure that Terraform plans replacement of a resource, rather than perform an in-place update, use the [`resource.RequiresReplace()` attribute plan modifier](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#RequiresReplace) in the schema or implement [resource plan modification](/terraform/plugin/framework/resources/plan-modification).

Other resource lifecycle implementations include:

- [Create](/terraform/plugin/framework/resources/create) resources by receiving Terraform configuration and plan data, performing creation logic, and saving Terraform state data.
- [Read](/terraform/plugin/framework/resources/read) resources by receiving Terraform prior state data, performing read logic, and saving refreshed Terraform state data.
- [Delete](/terraform/plugin/framework/resources/delete) resources by receiving Terraform prior state data and performing deletion logic.

## Define Update Method

Implement the `Update` method by:

1. [Accessing the data](/terraform/plugin/framework/accessing-values) from the [`resource.UpdateRequest` type](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#UpdateRequest). Most use cases should access the plan data in the [`resource.UpdateRequest.Plan` field](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#UpdateRequest.Plan).
1. Performing logic or external calls to modify the resource.
1. [Writing state data](/terraform/plugin/framework/writing-state) into the [`resource.UpdateResponse.State` field](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#UpdateResponse.State).

If the logic needs to return [warning or error diagnostics](/terraform/plugin/framework/diagnostics), they can added into the [`resource.UpdateResponse.Diagnostics` field](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#UpdateResponse.Diagnostics).

In this example, the `Update` function makes a HTTP call and returns successfully if the status code was 200 OK:

```go
// ThingResource defines the resource implementation.
// Some resource.Resource interface methods are omitted for brevity.
type ThingResource struct {
	// client is configured via a Configure method, which is not shown in this
	// example for brevity. Refer to the Configure Resources documentation for
	// additional details for setting up resources with external clients.
	client *http.Client
}

// ThingResourceModel describes the Terraform resource data model to match the
// resource schema.
type ThingResourceModel struct {
	Name types.String `tfsdk:"name"`
	Id   types.String `tfsdk:"id"`
}

// ThingResourceAPIModel describes the API data model.
type ThingResourceAPIModel struct {
	Name string `json:"name"`
	Id   string `json:"id"`
}

func (r ThingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
	resp.Schema = schema.Schema{
		Attributes: map[string]schema.Attribute{
			"name": schema.StringAttribute{
				MarkdownDescription: "Name of the thing to be saved in the service.",
				Required:            true,
			},
			"id": schema.StringAttribute{
				Computed:            true,
				MarkdownDescription: "Service generated identifier for the thing.",
				PlanModifiers: []planmodifier.String{
					stringplanmodifier.UseStateForUnknown(),
				},
			},
		},
		MarkdownDescription: "Manages a thing.",
	}
}

func (r ThingResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
	var data ThingResourceModel

	// Read Terraform plan data into the model
	resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)

	// Convert from Terraform data model into API data model
	updateReq := ThingResourceAPIModel{
		Id:   data.Id.ValueString(),
		Name: data.Name.ValueString(),
	}

	httpReqBody, err := json.Marshal(updateReq)

	if err != nil {
		resp.Diagnostics.AddError(
			"Unable to Update Resource",
			"An unexpected error occurred while creating the resource update request. "+
				"Please report this issue to the provider developers.\n\n"+
				"JSON Error: "+err.Error(),
		)

		return
	}

	// Create HTTP request
	httpReq := http.NewRequestWithContext(
		ctx,
		http.MethodPut,
		"http://example.com/things",
		bytes.NewBuffer(httpReqBody),
	)

	// Send HTTP request
	httpResp, err := d.client.Do(httpReq)
	defer httpResp.Body.Close()

	if err != nil {
		resp.Diagnostics.AddError(
			"Unable to Update Resource",
			"An unexpected error occurred while attempting to update the resource. "+
				"Please retry the operation or report this issue to the provider developers.\n\n"+
				"HTTP Error: "+err.Error(),
		)

		return
	}

	// Return error if the HTTP status code is not 200 OK
	if httpResp.StatusCode != http.StatusOK {
		resp.Diagnostics.AddError(
			"Unable to Update Resource",
			"An unexpected error occurred while attempting to update the resource. "+
				"Please retry the operation or report this issue to the provider developers.\n\n"+
				"HTTP Status: "+httpResp.Status,
		)

		return
	}

	// Save updated data into Terraform state
	resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
```

## Caveats

Note these caveats when implementing the `Update` method:

* An error is returned if the response state is not set when `Update` is called by the framework. If the resource does not support modification and should always be recreated on configuration value updates, the `Update` logic can be left empty and ensure all configurable schema attributes implement the [`resource.RequiresReplace()` attribute plan modifier](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#RequiresReplace).
* An error is returned if the response state contains unknown values. Set all attributes to either null or known values in the response.
* An error is returned if the response state has the `RemoveResource()` method called. This method is not valid during update. Return an error if the resource is no longer exists.
* An error is returned unless every null or known value in the request plan is saved exactly as-is into the response state. Only unknown plan values can be modified.

## Recommendations

Note these recommendations when implementing the `Update` method:

* Get request data from the Terraform plan data over configuration data as the schema or resource may include [plan modification](/terraform/plugin/framework/resources/plan-modification) logic which sets plan values.
* Only successfully modified parts of the resource should be return updated data in the state response.
* Use the [`resource.UseStateForUnknown()` attribute plan modifier](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#UseStateForUnknown) for `Computed` attributes that are known to not change during resource updates. This will enhance the Terraform plan to not show `=> (known after apply)` differences.

## Additional Use Cases

This section highlights implementation details for specific use cases.

### Detect Specific Attribute Changes

Certain update APIs require that only value changes are sent in the update request or require individual update API calls. Compare the attribute plan value to the attribute prior state value to determine individual attribute changes.

In this example, the entire plan and prior state are fetched then the attribute values are compared:

```go
type ThingResourceModel struct {
	Name types.String `tfsdk:"name"`
	// ... other attributes ...
}

func (r ThingResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
	var plan, state ThingResourceModel

	resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
	resp.Diagnostics.Append(req.State.Get(ctx, &state)...)

	if resp.Diagnostics.HasError() {
		return
	}

	// Compare name attribute value between plan and prior state
	if !plan.Name.Equal(state.Name) {
		// name attribute was changed
	}

	// ... further logic ...
}
```

In this example, the attribute is individually fetched from the plan and prior state then the values are compared:

```go
func (r ThingResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
	var namePlan, nameState types.String

	resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, path.Root("name"), &namePlan)...)
	resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("name"), &nameState)...)

	if resp.Diagnostics.HasError() {
		return
	}

	// Compare name attribute value between plan and prior state
	if !namePlan.Equal(nameState) {
		// name attribute was changed
	}

	// ... further logic ...
}
```
