Skip to content

id Attributes#

Background#

Historically all resources in the Terraform AWS provider have included a read-only id attribute, as Terraform Plugin SDK V2 and its associated acceptance testing library require it. In most cases, this attribute corresponds to a unique identifier generated by AWS during resource creation. However, for some resources the identifier is a value provided by the user and the resulting id attribute inherently duplicates the value of some other required argument.

With the general availability of Terraform Plugin Framework, the separation of the provider testing functionality into its own standalone library (terraform-plugin-testing), and some corresponding enhancements made to this library, resources are no longer required to have an id attribute.

Standard Usage#

As net-new resources must be implemented with Terraform Plugin Framework, the following standard should be applied.

id Attribute Standard

For all net-new resources, the id attribute should be omitted if it is redundant with an existing argument or is a combination of multiple arguments. When the unique identifier is a combination of arguments, these should be delimited with a comma (,), and rely on the internal ExpandResourceID function to handle splitting values within the ImportState method. In all other cases the id attribute should continue to be used as it has been historically.

Examples#

For resources omitting an id argument, minor changes are required to customize the import method and acceptance test the import operation. For testing, the import verification TestStep will now require the ImportStateVerifyIdentifierAttribute and one of ImportStateID or ImportStateIdFunc be configured. Examples for a single, non-id identifier and a multi-part identifier are included below.

Non-id Identifier#

In this example, the vpc_endpoint_id argument is used as the resource identifier.

ImportState method:

func (r *resourceEndpointPrivateDNS) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
    resource.ImportStatePassthroughID(ctx, path.Root("vpc_endpoint_id"), req, resp)
}

TestStep:

{
    ResourceName:                         resourceName,
    ImportState:                          true,
    ImportStateIdFunc:                    testAccVPCEndpointPrivateDNSImportStateIdFunc(resourceName),
    ImportStateVerify:                    true,
    ImportStateVerifyIdentifierAttribute: "vpc_endpoint_id",
},

ImportStateIdFunc:

func testAccVPCEndpointPrivateDNSImportStateIdFunc(resourceName string) resource.ImportStateIdFunc {
    return func(s *terraform.State) (string, error) {
        rs, ok := s.RootModule().Resources[resourceName]
        if !ok {
            return "", fmt.Errorf("Not found: %s", resourceName)
        }

        return rs.Primary.Attributes["vpc_endpoint_id"], nil
    }
}

Multi-part Identifier#

In this example, the resource identifier is a combination of the function_name and qualifier arguments.

ImportState Method:

func (r *resourceRuntimeManagementConfig) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
    parts, err := intflex.ExpandResourceId(req.ID, runtimeManagementConfigIDParts, true)
    if err != nil {
        resp.Diagnostics.AddError(
            "Unexpected Import Identifier",
            fmt.Sprintf("Expected import identifier with format: function_name,qualifier. Got: %q", req.ID),
        )
        return
    }

    resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("function_name"), parts[0])...)
    resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("qualifier"), parts[1])...)
}

TestStep:

{
    ResourceName:                         resourceName,
    ImportState:                          true,
    ImportStateIdFunc:                    testAccRuntimeManagementConfigImportStateIdFunc(resourceName),
    ImportStateVerify:                    true,
    ImportStateVerifyIdentifierAttribute: "function_name",
},

ImportStateIdFunc:

func testAccRuntimeManagementConfigImportStateIdFunc(resourceName string) resource.ImportStateIdFunc {
    return func(s *terraform.State) (string, error) {
        rs, ok := s.RootModule().Resources[resourceName]
        if !ok {
            return "", fmt.Errorf("Not found: %s", resourceName)
        }

        return fmt.Sprintf("%s,%s", rs.Primary.Attributes["function_name"], rs.Primary.Attributes["qualifier"]), nil
    }
}