| name | terraform-resource-development |
| description | Best practices for developing Terraform resources and data sources in this provider. Use when creating new resources, modifying schema definitions, implementing CRUD operations, handling state transitions, or working with autogenerated resources. |
Terraform Resource Development Best Practices
Framework Choice
- Always use Terraform Plugin Framework (TPF) for new resources and data sources. Never use the legacy SDKv2 for new code.
Schema Design
CreateOnly Plan Modifier for Non-Updatable Attributes
Use customplanmodifier.CreateOnly() on attributes that can only be set at creation time and cannot be updated. This generates a clear error during plan if a user tries to change the value, instead of silently failing or producing confusing API errors.
UseStateForUnknown Plan Modifier
Be cautious with UseStateForUnknown on Optional+Computed attributes. It can cause "inconsistent result after apply" errors when the API returns a value that differs from the prior state.
- Preferred approach: Omit
UseStateForUnknown unless you are certain the value never changes server-side when not defined by the user. If omitted, users see (known after apply) during plan, which is safe.
Optional+Computed Attributes
Prefer Optional-only over Optional+Computed. With Optional+Computed, when a user removes an attribute from their config Terraform will not generate a plan diff because it treats the server-side value as authoritative. This means removals go undetected and the previous value silently persists. Only use Optional+Computed when the API genuinely returns a server-computed default for a field the user did not set.
Avoiding Breaking Changes
Any change to a resource or data source must be backward-compatible by default. Treat the following as breaking unless the intent is explicitly documented and acknowledged:
- Schema changes: renaming, removing, or changing the type of an existing attribute; converting between Optional, Required, and Computed; tightening or removing valid values from a validation.
- State representation: altering how a value is stored in or read from state (e.g. changing casing, encoding, flattening/nesting structure). Even if the API value is the same, a different state representation triggers unexpected diffs or "inconsistent result after apply" errors.
- Default value changes: adding, removing, or modifying a
Default or plan modifier that changes what gets written to state when the user omits a field.
- Import behavior: changing the format of the import ID or what is read during import.
When a breaking change is genuinely required (e.g. aligning with an API deprecation), call it out explicitly in the PR description.