Terraform
Resources - Customizing Differences
Terraform tracks the state of provisioned resources in its state file, and compares the user-passed configuration against that state. When Terraform detects a discrepancy, it presents the user with the differences between the configuration and the state.
Sometimes determining the differences between state and configuration requires special handling, which can be managed with the CustomizeDiff
function.
CustomizeDiff
is passed a *schema.ResourceDiff
. This is a structure similar to schema.ResourceData
— it lacks most write functions (like Set
), but adds some functions for working with the difference, such as SetNew
, SetNewComputed
, and ForceNew
.
NOTE: CustomizeDiff
does not currently support computed/"known after apply" values from other resource attributes.
Any function can be provided for difference customization. For the majority of simple cases, we recommend that you first try to compose the behavior using the customdiff helper package, which allows for a more declarative configuration. However, for highly custom requirements, a custom-made function is usually easier and more maintainable than working around the helper's limitations.
package example
import (
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func resourceExampleInstance() *schema.Resource {
return &schema.Resource{
Create: resourceExampleInstanceCreate,
Read: resourceExampleInstanceRead,
Update: resourceExampleInstanceUpdate,
Delete: resourceExampleInstanceDelete,
Schema: map[string]*schema.Schema{
"size": {
Type: schema.TypeInt,
Required: true,
},
},
CustomizeDiff: customdiff.All(
customdiff.ValidateChange("size", func (ctx context.Context, old, new, meta any) error {
// If we are increasing "size" then the new value must be
// a multiple of the old value.
if new.(int) <= old.(int) {
return nil
}
if (new.(int) % old.(int)) != 0 {
return fmt.Errorf("new size value must be an integer multiple of old value %d", old.(int))
}
return nil
}),
customdiff.ForceNewIfChange("size", func (ctx context.Context, old, new, meta any) bool {
// "size" can only increase in-place, so we must create a new resource
// if it is decreased.
return new.(int) < old.(int)
}),
),
}
}
In this example we use the helpers to ensure the size can only be increased to multiples of the original size, and that if it is ever decreased it forces a new resource. The customdiff.All
helper will run all the customization functions, collecting any errors as a multierror
. To have the functions short-circuit on error, please use customdiff.Sequence
.