Renew 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.
Note
Renew
cannot return new result data for the ephemeral resource instance, so this logic is only appropriate for remote objects like HashiCorp Vault leases, which can be renewed without changing their data.
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.Private
field 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.RenewAt
field to indicate to Terraform when to call the providerRenew
method. - Writing private data needed to
Renew
orClose
the ephemeral resource to theephemeral.RenewResponse.Private
field.
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
RenewAt
response field, add extra time (usually no more than a few minutes) before an ephemeral resource expires to account for latency.