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
}
}