with one click
resource-framework-migration
// Assist in migrating resources from `terraform-plugin-sdk` (legacy) to `terraform-plugin-framework` using a framework wrapper pattern (e.g., `internal/sdk`).
// Assist in migrating resources from `terraform-plugin-sdk` (legacy) to `terraform-plugin-framework` using a framework wrapper pattern (e.g., `internal/sdk`).
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | resource-framework-migration |
| description | Assist in migrating resources from `terraform-plugin-sdk` (legacy) to `terraform-plugin-framework` using a framework wrapper pattern (e.g., `internal/sdk`). |
| triggers | ["migrate resource to terraform-plugin-framework","convert resource to framework","refactor legacy resource","internal/sdk wrapper"] |
This skill assists in migrating existing Terraform resources from the terraform-plugin-sdk (v2) to the terraform-plugin-framework using a custom wrapper (typically located in internal/sdk, internal/tf/framework/wrapper, or an external library).
The wrapper provides a bridge between the legacy ResourceData patterns and the framework's typed models. It simplifies the migration by handling boilerplate and providing familiar (yet type-safe) methods.
Documentation Access: When migrating, you can find the up-to-date Markdown documentation for the framework at
https://github.com/hashicorp/web-unified-docs/tree/main/content/terraform-plugin-framework/v{VERSION}.x/docs/plugin/framework(where{VERSION}matches the provider'sgo.modversion ofterraform-plugin-framework).
All resource models must be placed in a file named *_models.go within the service directory (e.g., linux_virtual_machine_models.go).
[resourceName]ResourceModel (e.g., linuxVirtualMachineResourceModel).[resourceName][Property]Model (e.g., virtualMachineSSHKeyModel).types package (e.g., types.String, types.Bool, types.Int64).types.List or types.Set.typehelpers.ListNestedObjectValueOf[T] or typehelpers.SetNestedObjectValueOf[T].typehelpers.MapValueOf[types.String].Struct tags must match the schema property names exactly.
type exampleResourceModel struct {
ID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Location types.String `tfsdk:"location"`
Tags typehelpers.MapValueOf[types.String] `tfsdk:"tags"`
Secret typehelpers.ListNestedObjectValueOf[exampleSecretModel] `tfsdk:"secret"`
}
sdk.FrameworkWrappedResourceYour resource struct must implement the sdk.FrameworkWrappedResource interface.
type exampleResource struct{}
func (r *exampleResource) ResourceType() string {
return "azurerm_example_resource"
}
func (r *exampleResource) ModelObject() any {
return &exampleResourceModel{}
}
Implement the Schema method. Map pluginsdk.Schema to framework schema.Attribute.
TypeString -> schema.StringAttributeTypeBool -> schema.BoolAttributeTypeInt -> schema.Int64AttributeTypeFloat -> schema.Float64AttributeTypeList/TypeSet -> schema.ListAttribute/schema.SetAttribute (or ListNestedBlock/SetNestedBlock)The wrapper provides Create, Read, Update, and Delete methods that take sdk.ResourceMetadata and the decoded model.
func (r *exampleResource) Create(ctx context.Context, req resource.CreateRequest, resp resource.CreateResponse, meta sdk.ResourceMetadata, plan any) {
data := plan.(*exampleResourceModel)
// ... expansion logic (similar to legacy, but using data fields directly)
}
If the resource requires plan modification (e.g., RequiresReplace) or complex validation, implement:
sdk.FrameworkWrappedResourceWithPlanModifier (ModifyPlan method)sdk.FrameworkWrappedResourceWithConfigValidators (ConfigValidators method)In internal/services/<service>/registration.go, add the resource to the resources slice:
func (r Registration) FrameworkResources() []sdk.FrameworkWrappedResource {
if !features.NextMajorVersion() {
return []sdk.FrameworkWrappedResource{}
}
return []sdk.FrameworkWrappedResource{
&exampleResource{},
}
}
The framework implementation relies on several utility packages within the provider and go-azure-helpers:
fwcommonschema: Common attributes like Name, ResourceGroupName, Location, and Tags.typehelpers: Helpers for working with framework types, such as WrappedStringValidator and NewWrappedStringDefault.values: Utilities for converting between framework types and Go types (e.g., ValueStringPointer).pointer: (from go-azure-helpers) Standard pointer utilities.| Feature | Plugin SDK (Legacy) | Framework Wrapper |
|---|---|---|
| Data Access | d.Get("name").(string) | data.Name.ValueString() |
| Errors | return fmt.Errorf(...) | sdk.SetResponseErrorDiagnostic(resp, ...) |
| Identity | d.SetId(id.ID()) | data.ID = types.StringValue(id.ID()) |
| Defaults | Default: "value" | Default: typehelpers.NewWrappedStringDefault("value") |
| Expanders | expandFoo(d.Get("foo").([]interface{})) | expandFooModel(ctx, data.Foo, &resp.Diagnostics) |
d.Get: Use the typed model fields directly.sdk.AssertResourceModelType: Always assert your model type at the start of CRUD methods.RequiresReplace vs. RequiresReplaceIfConfigured.stringvalidator) over legacy ValidateFunc where possible, or wrap them using typehelpers.WrappedStringValidator.terraform plan output for validation.internal/sdk/README.md