Terraform
Resources - List
Whilst list
isn't supported natively in SDKv2 there are compatibility implementations available to support the use of the framework interface on SDKv2 resources.
The following pre-requisites are required to implement list
for an SDKv2 resource:
- The provider must be able to access framework functionality, this can be achieved by combining providers
- The list resource type needs to be added to the provider so that it is available to practitioners
In addition to the pre-requisites above we recommend reading the list resource documentation for framework to understand the concepts and requirements of implementing a list resource.
Implement the RawV5Schemas Method
The list.ListResourceWithRawV5Schemas
interface RawV5Schemas
method allows practitioners to provide the necessary resource and identity schema information upfront.
To facilitate the use of this method, the ProtoSchema
and ProtoIdentitySchema
methods are available on the SDKv2 schema.Resource
type and will return the protocol V5 representations of the resource and identity schemas.
In this example, the resource examplecloud_thing
implements the RawV5Schemas
method:
// Some list.ListResource interface methods are omitted for brevity
type ThingListResource struct {}
func (r *ThingListResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = "examplecloud_thing"
}
func (r *ThingListResource) RawV5Schemas(ctx context.Context, req list.RawV5SchemaRequest, resp *list.RawV5SchemaResponse) {
// Where `resourceThing` returns the `schema.Resource` instance
thingResource := resourceThing()
resp.ProtoV5Schema = thingResource.ProtoSchema(ctx)()
resp.ProtoV5IdentitySchema = thingResource.ProtoIdentitySchema(ctx)()
}
Implement the List Method
The list.ListResource
interface List
method is where the actual list API call is made and the resulting resource state is returned to the framework.
The results that are returned must be framework types. To simplify the conversion of identity and resource data on SDKv2 objects to framework types, the following two methods were added on schema.ResourceData
:
TfTypeIdentityState()
- Converts the identity data on theResourceData
to atftypes.Value
.TfTypeResourceState()
- Converts the resource state on theResourceData
to atftypes.Value
.
In this example, the list method on the resource examplecloud_thing
is defined:
// Some list.ListResource interface methods are omitted for brevity.
type ThingListResource struct{}
func (r *ThingListResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = "examplecloud_thing"
}
func (r *ThingListResource) RawV5Schemas(ctx context.Context, req list.RawV5SchemaRequest, resp *list.RawV5SchemaResponse) {
thingResource := resourceThing()
resp.ProtoV5Schema = thingResource.ProtoSchema(ctx)()
resp.ProtoV5IdentitySchema = thingResource.ProtoIdentitySchema(ctx)()
}
func (r *ThingListResource) List(ctx context.Context, req list.ListRequest, stream *list.ListResultsStream) {
var data ThingListResourceModel
// Read list config data into the model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Typically lists will make external calls here using data from the config
// as input. For brevity, we assume the `things` slice below was returned by an
// API call here
// Define the function that will push results into the stream
stream.Results = func(push func(list.ListResult) bool) {
for _, thing := range things {
// Initialize a new result object for each thing
result := req.NewListResult(ctx)
// Set the user-friendly name of this thing
result.DisplayName = thing.Name
// Create an instance of the SDKv2 resource
thingResource := resourceThing()
// Create a new ResourceData object to hold the state of this thing
rd := thingResource.Data(&terraform.InstanceState{})
// Set the ID of the resource for the ResourceData object
rd.SetId("a_unique_id_related_to_the_thing")
// Set the attributes of interest into the resource state
rd.Set("name", thing.Name)
rd.Set("attribute_one", thing.AttributeOne)
rd.Set("attribute_two", thing.AttributeTwo)
// Convert and set the identity and resource state into the result
tfTypeIdentity, err := rd.TfTypeIdentityState()
if err != nil {
result.Diagnostics.AddError(
"Converting identity data",
"An error was encountered when converting the identity data: "+err.Error(),
)
}
if err := result.Identity.Set(ctx, *tfTypeIdentity); err != nil {
result.Diagnostics.AddError(
"Setting identity data",
"An error was encountered when setting the identity data: "+err.Error(),
)
}
// Convert and set the resource state into the result
tfTypeResource, err := rd.TfTypeResourceState()
if err != nil {
result.Diagnostics.AddError(
"Converting resource state",
"An error was encountered when converting the resource state: "+err.Error(),
)
}
if err := result.Resource.Set(ctx, *tfTypeResource); err != nil {
result.Diagnostics.AddError(
"Setting resource state",
"An error was encountered when setting the resource state: "+err.Error(),
)
}
// Send the result to the stream.
if !push(result) {
return
}
}
}
}