Terraform
Renewing ephemeral resources
Renew is an optional part of the Terraform lifecycle for an ephemeral resource, which is different from the managed resource lifecycle. During any Terraform operation (like terraform plan or terraform apply), when an ephemeral resource's data is needed, Terraform initially retrieves that data with the Open lifecycle handler. During Open, ephemeral resources can opt to include a timestamp in the RenewAt response field to indicate to Terraform when a provider must renew an ephemeral resource. If an ephemeral resource's data is still in-use and the RenewAt timestamp has passed, Terraform calls the provider RenewEphemeralResource RPC, in which the framework calls the ephemeral.EphemeralResourceWithRenew interface Renew method. The request contains any Private data set in the latest Open or Renew call. The response contains Private data and an optional RenewAt field for further renew executions.
Renew is an optional lifecycle implementation for an ephemeral resource, other lifecycle implementations include:
- Open an ephemeral resource by receiving Terraform configuration, retrieving a remote object, and returning ephemeral result data to Terraform.
- Close a remote object when Terraform no longer needs the data.
Define Renew Method
The ephemeral.EphemeralResourceWithRenew interface on the ephemeral.EphemeralResource interface implementation will enable renew support for an ephemeral resource.
Implement the Renew method by:
- Accessing private data from
ephemeral.RenewRequest.Privatefield needed to renew the remote object. - Performing logic or external calls to renew the remote object.
- Determining if a remote object needs to be renewed again, setting the
ephemeral.RenewResponse.RenewAtfield to indicate to Terraform when to call the providerRenewmethod. - Writing private data needed to
ReneworClosethe ephemeral resource to theephemeral.RenewResponse.Privatefield.
If the logic needs to return warning or error diagnostics, they can be added into the ephemeral.RenewResponse.Diagnostics field.
In this example, an ephemeral resource named examplecloud_thing with hardcoded behavior is defined. It indicates a renewal should occur 5 minutes from when either the Open or Renew method is executed:
var _ ephemeral.EphemeralResourceWithRenew = (*ThingEphemeralResource)(nil)
// ThingEphemeralResource defines the ephemeral resource implementation, which also implements Renew.
type ThingEphemeralResource struct{}
type ThingEphemeralResourceModel struct {
Name types.String `tfsdk:"name"`
Token types.String `tfsdk:"token"`
}
type ThingPrivateData struct {
Name string `json:"name"`
}
func (e *ThingEphemeralResource) Schema(ctx context.Context, req ephemeral.SchemaRequest, resp *ephemeral.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"name": schema.StringAttribute{
Description: "Name of the thing to retrieve a token for.",
Required: true,
},
"token": schema.StringAttribute{
Description: "Token for the thing.",
Computed: true,
},
},
}
}
func (e *ThingEphemeralResource) Open(ctx context.Context, req ephemeral.OpenRequest, resp *ephemeral.OpenResponse) {
var data ThingEphemeralResourceModel
// Read Terraform config data into the model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Typically ephemeral resources will make external calls and reference returned data,
// however this example hardcodes the setting of result and private data for brevity.
data.Token = types.StringValue("token-123")
// Renew 5 minutes from now
resp.RenewAt = time.Now().Add(5 * time.Minute)
// When renewing, pass along this data (error handling omitted for brevity).
privateData, _ := json.Marshal(ThingPrivateData{Name: data.Name.ValueString()})
resp.Private.SetKey(ctx, "thing_data", privateData)
// Save data into ephemeral result data
resp.Diagnostics.Append(resp.Result.Set(ctx, &data)...)
}
func (e *ThingEphemeralResource) Renew(ctx context.Context, req ephemeral.RenewRequest, resp *ephemeral.RenewResponse) {
privateBytes, _ := req.Private.GetKey(ctx, "thing_data")
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// Unmarshal private data (error handling omitted for brevity).
var privateData ThingPrivateData
json.Unmarshal(privateBytes, &privateData)
// Perform external call to renew "thing" data
// Renew again in 5 minutes
resp.RenewAt = time.Now().Add(5 * time.Minute)
// If needed, you can also set new `Private` data on the response.
}
Recommendations
- When setting the
RenewAtresponse field, add extra time (usually no more than a few minutes) before an ephemeral resource expires to account for latency.