Ephemeral resource Close(): storing sensitive credentials in private state for token revocation

Hi,

We’re building an ephemeral resource in our Terraform Provider that generates a short-lived OAuth2 access token via the Client Credentials flow (3600s TTL).

The resource has an optional revoke_on_closure flag. When enabled, we revoke the token at the end of the Terraform operation during
Close(). Our revocation endpoint (POST follows RFC 7009 and requires:

  • The access token being revoked (in the request body)
  • Client credentials for authentication (Basic auth with client_id:client_secret)

Since Close() only receives req.Private (not the original config or result data), we store the access_token, client_id, and client_secret in private state during Open() so they’re available for revocation in Close().

We have a few questions about this approach:

  1. What are the persistence and isolation guarantees of private state in ephemeral resources?

The private state docs describe private state for managed resources, where it is persisted in the state file. For ephemeral resources, our understanding is that private state is held in process memory only and never written to disk. Is this correct?

Is this documented or guaranteed anywhere for ephemeral resources specifically?

  1. Is private state the recommended way to pass data from Open() to Close()?

Close() doesn’t have access to the resource config or result data, only req.Private. For cleanup operations that need information from Open(), private state seems like the only option. Is this the intended pattern?

  1. Are there any security considerations we should be aware of when storing sensitive values (tokens, credentials) in ephemeral private state?

Our concern is that we’re storing three sensitive values (access_token, client_id, client_secret) in private state bytes. These
same values are already in process memory during Open() (as Go locals and in the Terraform model struct), so we don’t believe private state increases the attack surface. But we want to confirm there’s no scenario where ephemeral private state could be logged, serialized to a crash dump, or otherwise made visible outside the process.

For reference, we looked at how HashiCorp’s own Vault provider handles this. The vault_azure_access_credentials ephemeral resource
stores a lease_id in private state and revokes it in Close(). Their case is simpler because Vault’s revoke API only needs a lease ID (not credentials), but the pattern of using private state to pass cleanup data to Close() is the same.

Any guidance on these questions would be appreciated. This is one of the first ephemeral resources in the ecosystem that implements Close() with credential-dependent revocation, so we want to make sure we’re following best practices.

  1. What are the persistence and isolation guarantees of private state in ephemeral resources?

Terraform does not serialize the ephemeral resource private data to the plan or state, or attempt to decode it in any way – the bytes are just stored in memory and returned to the provider for subsequent calls.

  1. Is private state the recommended way to pass data from Open() to Close()?

The only use for the private data returned by OpenEphemeralResource is to carry data forward to the RenewEphemeralResource and CloseEphemeralResource calls. The private field here isn’t related to that of managed resources, it’s just to carry any internal state you might need. Here’s the docs from that field in the core code:

	// Private is any internal data needed by the provider to perform a
	// subsequent [Interface.CloseEphemeralResource] request for the same object. The
	// provider may choose any encoding format to represent the needed data,
	// because Terraform Core treats this field as opaque.
	//
	// Providers should aim to keep this data relatively compact to minimize
	// overhead. Although Terraform Core does not enforce a specific limit just
	// for this field, it would be very unusual for the internal context to be
	// more than 256 bytes in size, and in most cases it should be on the order
	// of only tens of bytes. For example, a lease ID for the remote system is a
	// reasonable thing to encode here.
	//
	// Because ephemeral resource instances never outlive a single Terraform
	// Core phase, it's guaranteed that a CloseEphemeralResource request will be
	// received by exactly the same plugin instance that returned this value,
	// and so it's valid for this to refer to in-memory state belonging to the
	// provider instance.
  1. Are there any security considerations we should be aware of when storing sensitive values (tokens, credentials) in ephemeral private state?

Exposing sensitive data in more places obviously expands the possible ways it could leak, but Terraform does attempt to keep that data undisclosed. It would also be possible to store all the secure values only in the provider, and give Terraform an opaque token to correlate with those stored credentials. To me that feels like overkill if your data is already compact, since terraform and the provider typically run in the same security context, if one could be compromised they both can.