Skip to content

Terraform Plugin Migrations#

With the introduction of Terraform Plugin Framework it will become necessary to migrate existing resources from SDKv2. The Provider currently implements both plugins so migration can be done at a resource level.

Migration Tooling#

Tooling has been created that will scaffold an existing resource into a Framework resource. This tool is meant to be used as a starting point so additional editing will be needed.

Build:

make tfsdk2fw

Convert a resource:

The following pattern is used to generate a file: tfsdk2fw [-resource <resource-type>|-data-source <data-source-type>] <package-name> <name> <generated-file>

Example:

tfsdk2fw -resource aws_example_resource examplepackage ResourceName internal/service/examplepackage/resource_name_fw.go

This command creates a separate file that exists alongside the existing SDKv2 resource. Ultimately, the new file should replace the SDKv2 resource.

When done creating the resource using the Framework run make gen to remove the SDK resource and add the Framework resource to the list of generated service packages.

State Upgrade#

Terraform Plugin Framework introduced null values, which differ from zero values. Since the Plugin SDKv2 marked both null and zero values as the same, it will be necessary to use the State Upgrader.

An example of a resource with an upgraded state, while migrating, can be found here.

Custom Types#

The Plugin Framework introduced custom types that allow custom validation on basic types. The following attribute types will require a state upgrade to utilize these custom types.

  • ARNs
  • CIDR Blocks
  • Duration
  • Timestamps

SDKv2 ARN attribute.

func ResourceExampleResource {
    return &schema.Schema{
        "arn_attribute": {      
            Type:         schema.TypeString,
            Optional:     true,
            ValidateFunc: verify.ValidARN,
        },

        // other schema attributes
    }
}

Framework ARN attribute.

func (r *resourceExampleResource) Schema(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) {
    return schema.Schema{
        "arn_attribute": schema.StringAttribute{
            CustomType: fwtypes.ARNType,
            Optional:   true,
            PlanModifiers: []planmodifier.String{
                stringplanmodifier.UseStateForUnknown(),
            },
        },

        // other schema attributes
    }
}

Tagging#

Tagging in the Plugin Framework is done by implementing the ModifyPlan() method on a resource.

func (r *resourceExampleResource) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) {
    r.SetTagsAll(ctx, request, response)
}

Transparent Tagging that is used in SDKv2 also applies to the Framework by using the @Tags decorator when defining the resource.

// @FrameworkResource("aws_service_example", name="Example Resource")
// @Tags(identifierAttribute="arn")
func newResourceExampleResrouce(_ context.Context) (resource.ResourceWithConfigure, error) {
    r := resourceExampleResource{}
    return &r, nil
}

Testing#

It is important to not cause any state diffs that result in breaking changes. Testing will check that the diff before and after the migration presents no changes.

Tip

VersionConstraint should be set to the most recently published version of the AWS Provider.

func TestAccExampleResource_MigrateFromPluginSDK(t *testing.T) {
    ctx := acctest.Context(t)
    var example service.ExampleResourceOutput
    resourceName := "aws_example_resource.test"
    rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

    resource.ParallelTest(t, resource.TestCase{
        PreCheck:     func() { acctest.PreCheck(ctx, t); testAccPreCheck(ctx, t) },
        ErrorCheck:   acctest.ErrorCheck(t, names.ExampleServiceID),
        CheckDestroy: testAccCheckExampleResourceDestroy(ctx),
        Steps: []resource.TestStep{
            {
                ExternalProviders: map[string]resource.ExternalProvider{
                    "aws": {
                        Source:            "hashicorp/aws",
                        VersionConstraint: "5.23.0", // always use most recently published version of the Provider
                    },
                },
                Config: testAccExampleResourceConfig_basic(rName),
                Check: resource.ComposeTestCheckFunc(
                    testAccCheckExampleResourceExists(ctx, resourceName, &example),
                ),
            },
            {
                ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
                Config:                   testAccExampleResourceConfig_basic(rName),
                PlanOnly:                 true,
            },
        },
    })
}