rfc/20250317-ephemeral-resources.md
Issue: https://github.com/opentofu/opentofu/issues/1996
Right now, OpenTofu information for resources and outputs are written to state and plan files as it is. This is presenting a security risk as some information from the stored objects can contain sensitive bits that can become visible to whoever is having access to the state or plan files.
To provide a better solution for the aforementioned situation, OpenTofu introduces the concept of "ephemerality" which is meant to make use of the already existing functionality in terraform-plugin-framework. Any new feature under this new concept should provide ways to skip values from being written to the state and plan files.
This new concept is going to offer another way to tackle the aforementioned issue, adding one more option in OpenTofu to choose for securing the plan and state files. Here are the other existing options, providing different levels of safety:
Two new concepts will be introduced:
ephemeral resourcesresource's write-only attributesSeveral existing features will have to be able to be updated with the new functionality:
connection block[!NOTE]
In order to provide a similar and familiar UX for the users, the proposal in this RFC is heavily inspired by the Terraform public documentation and available blog posts on the matter.
In the attempt to provide to the reader an in-depth understanding of the ephemerality implications in OpenTofu, this section will try to explain the functional approach of the new concept in each existing feature.
This is a new concept that allows any existing resource to define attributes in its schema that can be only written without the ability to retrieve the value afterwards.
By not being readable, this also means that an attribute configured by a provider this way should not be written to the state or plan file either. Therefore, these attributes are suitable for configuring specific resources with sensitive data, like passwords, access keys, etc.
A write-only attribute can accept an ephemeral or a non-ephemeral value, even though it's recommended to use ephemeral values for such attributes.
Because these attributes are not written to the plan file, updating a write-only attribute is getting a bit trickier.
Provider implementations do generally include also a "version" argument linked to the write-only one.
For example having a write-only argument called secret, providers should also include
a non-write-only argument called secret_version. Every time the user wants to update the value of secret, it needs to change the value of secret_version to trigger a change.
The provider implementation is responsible with handling this particular case: because the version attribute is stored also in the state, the provider needs to compare the value from the state with the one from the configuration and in case it differs, it will trigger the update of the secret attribute.
At the time of writing this RFC, write-only attributes are supported by a low number of providers and resources.
Having the aws_db_instance as one of those, here is an example on how to use the write-only attributes:
resource "aws_db_instance" "example" {
// ...
password_wo = "your-initial-password"
password_wo_version = 1
// ...
}
By updating only the password_wo, on the tofu apply, the password should not be updated.
To do so, the password_wo_version needs to be incremented too:
resource "aws_db_instance" "example" {
// ...
password_wo = "new-password"
password_wo_version = 2
// ...
}
As seen in this particular change of the terraform-plugin-framework, the write-only attribute cannot be configured for set attributes, set nested attributes, and set nested blocks.
Write-only attributes cannot generate a plan diff. This is because the prior state does not contain a value that OpenTofu can use to compare the new value against and also the value provider returns during planning of a write-only argument should always be null. This means that there could be inconsistencies between plan and apply for the write-only arguments.
Any variable block can be marked as ephemeral.
variable "ephemeral_var" {
type = string
ephemeral = true
}
OpenTofu should allow usage of these variables only in other ephemeral contexts:
Usage in any other place should raise an error:
│ Error: Invalid use of an ephemeral value
│
│ with playground_secret.store_secret,
│ on main.tf line 30, in resource "playground_secret" "store_secret":
│ 30: secret_name = var.password
│
│ "secret_name" cannot accept an ephemeral value because it is not a write-only attribute, meaning it will be written to the state.
╵
For being able to use ephemeral variables, the module's authors need to mark those as so. If OpenTofu finds an ephemeral value given to a non-ephemeral variable in a module call, an error will be shown:
│ Error: Invalid usage of ephemeral value
│
│ on main.tf line 21, in module "secret_management":
│ 21: secret_map = var.secrets
│
│ Variable `secret_map` is not marked as ephemeral. Therefore, it cannot reference an ephemeral value. In case this is actually wanted, you can add the following attribute to its declaration:
│ ephemeral = true
OpenTofu should not store ephemeral variable(s) in plan files. If a plan is generated from a configuration that is having at least one ephemeral variable, when the plan file will be applied, the value(s) for the ephemeral variable(s) needs to be provided again.
Most output blocks can be configured as ephemeral.
To mark an output as ephemeral, use the following syntax:
output "test" {
// ...
ephemeral = true
}
The ephemeral outputs are available during plan and apply phase and can be accessed only in specific contexts:
provisioner blockconnection blockAn output block from a root module cannot be marked as ephemeral.
This limitation is natural since ephemeral outputs are meant to be skipped from the state file.
Therefore, there is no use for such a defined output block in a root module.
When encountering an ephemeral output in a root module, an error similar to this one should be shown:
│ Error: Unallowed ephemeral output
│
│ on main.tf line 36:
│ 36: output "write_only_out" {
│
│ Root module is not allowed to have ephemeral outputs
Ephemeral outputs are useful when a child module returns sensitive data, allowing the caller to use the value of that output in other ephemeral contexts. When using outputs in non-ephemeral contexts, OpenTofu should show an error similar to the following:
│ Error: Invalid use of an ephemeral value
│
│ with aws_secretsmanager_secret_version.store_from_ephemeral_output,
│ on main.tf line 31, in resource "aws_secretsmanager_secret_version" "store_from_ephemeral_output":
│ 31: secret_string = module.secret_management.secrets
│
│ "secret_string" cannot accept an ephemeral value because it is not a write-only attribute, meaning it will be written to the state.
╵
Any output that wants to use an ephemeral value must also be marked as ephemeral. Otherwise, it needs to show an error:
│ Error: Output not marked as ephemeral
│
│ on mod/main.tf line 33, in output "password":
│ 33: value = reference.to.ephemeral.value
│
│ In order to allow this output to store ephemeral values add `ephemeral = true` attribute to it.
[!NOTE]
It needs to be said that the last error will be raised only when a non-ephemeral output references an ephemeral value. However, an ephemeral marked output needs to be allowed to reference a non-ephemeral value.
Local values are automatically marked as ephemeral if any of the value that is used to compute the local is already an ephemeral one.
Eg:
variable "a" {
type = string
default = "a value"
}
variable "b" {
type = string
default = "b value"
ephemeral = true
}
locals {
a_and_b = "${var.a}_${var.b}"
}
Because variable b is marked as ephemeral, then the local a_and_b is marked as ephemeral too.
Locals marked as ephemeral are available during plan and apply phase and can be referenced only in specific contexts:
provider blocks configurationconnection and provisioner blocksIn contrast with the write-only arguments where only specifically tagged attributes are not stored in the state/plan file, ephemeral resources must not be stored in the state file and have only a reference stored in the plan file.
The ephemeral blocks are behaving similar to data, where it reads the indicated resource and once it's done with it, is going to close it.
Ephemeral resources can be referenced only in specific contexts:
provider blocksprovisioner and connection blocksFor example, you can have an ephemeral resource that is retrieving the password from a secret manager, password that can be passed later into a write-only attribute of another normal resource.
To do so, the flow of an ephemeral resource should look similar to the following:
RenewAt:
RenewAt, call the provider Renew method.Close on the provider for all opened ephemeral resources.Besides the attributes in a schema of an ephemeral resource, the block should also support the meta-arguments existing in OpenTofu:
depends_oncountfor_eachproviderlifecycleThe only lifecycle content that ephemerals should support are precondition and postcondition. In case OpenTofu will find other known attributes of the lifecycle block, it should show an error similar to the following:
│ Error: Invalid lifecycle configuration for ephemeral resource
│
│ on ../mod/main.tf line 44, in ephemeral "aws_secretsmanager_secret_version" "secret_retrieval":
│ 44: create_before_destroy = true
│
│ The lifecycle argument "create_before_destroy" cannot be used in ephemeral resources. This is meant
│ to be used strictly in "resource" blocks.
The meta-arguments provisioner and connection should not be supported.
provider block is ephemeral by nature, meaning that the configuration of this is never stored into state/plan file.
Therefore, this block should be configurable by using ephemeral values.
provisioner blockAs provisioner information is not stored into the plan/state file, this can reference ephemeral values like ephemeral variables, outputs, locals and values from ephemeral resources.
Whenever doing so, the output of the provisioner execution should be suppressed:
(local-exec): (output suppressed due to ephemeral value in config)
connection blockWhen the connection block is configured, this should be allowed to use ephemeral values from variables, outputs, locals and values from ephemeral resources.
To better understand how all of this should work in OpenTofu, let's take a look at a comprehensive example.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "6.0.0-beta1"
}
}
}
variable "secret_map" {
type = map(string)
default = {}
ephemeral = true # (1)
}
variable "secret_version" { # (2)
type = number
default = 1
}
variable "secret_manager_arn" {
type = string
default = ""
}
resource "aws_secretsmanager_secret" "manager" {
count = var.secret_version > 1 ? 1 : 0
name = "ephemeral-rfc-example"
}
resource "aws_secretsmanager_secret_version" "secret_creation" {
count = var.secret_version > 1 ? 1 : 0
secret_id = aws_secretsmanager_secret.manager[0].arn
secret_string_wo = jsonencode(var.secret_map) # (3)
secret_string_wo_version = var.secret_version
}
ephemeral "aws_secretsmanager_secret_version" "secret_retrieval" { # (4)
count = var.secret_version > 1 ? 1 : 0
secret_id = aws_secretsmanager_secret.manager[0].arn
depends_on = [
aws_secretsmanager_secret_version.secret_creation
]
}
ephemeral "aws_secretsmanager_secret_version" "secret_retrieval_direct" {
count = var.secret_version > 1 ? 0 : 1
secret_id = var.secret_manager_arn
}
output "secrets" {
value = "${var.secret_version > 1 ?
jsondecode(ephemeral.aws_secretsmanager_secret_version.secret_retrieval[0].secret_string) :
jsondecode(ephemeral.aws_secretsmanager_secret_version.secret_retrieval_direct[0].secret_string)}"
ephemeral = true # (5)
}
output "secret_manager_arn" {
value = var.secret_version > 1 ? aws_secretsmanager_secret.manager[0].arn : null
}
This module can be used for two separate operations:
secret_version is having a value greater than 1, it will add the given secret into a secret manager.secret_version is not given, it will use the secret_manager_arn to read the secret by using an ephemeral resource.Details:
aws_secretsmanager_secret_version.secret_creation.secret_string_wo is the write-only attribute that is receiving the secret variable which is ephemeral (even though a write-only argument can use also a non-ephemeral value).resource is using the write-only attribute secret_string_wo to store the information, that field is going to be null when referenced.sensitive values.terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "6.0.0-beta1"
}
}
}
provider "aws" {
alias = "secrets-read-write"
}
variable "access_key" {
type = string
ephemeral = false # (1)
}
variable "secret_key" {
type = string
ephemeral = false
}
locals {
secrets = {
"access_key" : var.access_key,
"secret_key" : var.secret_key
}
}
module "secret_management" {
providers = {
aws : aws.secrets-read-write
}
source = "../mod"
secret_map = local.secrets
secret_version = 2
}
output "secret_manager_arn" {
value = module.secret_management.secret_manager_arn
}
This file is a configuration used to manage the secrets from the secret manager and just outputs the ARN of the secret manager to be used later.
Details:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "6.0.0-beta1"
}
}
}
provider "aws" {
alias = "read-secrets"
}
variable "secret_manager_arn" {
type = string
}
module "secret_management" { # (1)
providers = {
aws : aws.read-secrets
}
source = "../mod"
secret_manager_arn = var.secret_manager_arn
}
provider "aws" {
alias = "dev-access"
access_key = module.secret_management.secrets["access_key"] # (2)
secret_key = module.secret_management.secrets["secret_key"]
}
resource "aws_ssm_parameter" "store_ephemeral_in_write_only" { # (3)
provider = aws.dev-access
name = "parameter_from_ephemeral_value"
type = "SecureString"
value_wo = jsonencode(module.secret_management.secrets) # (4)
value_wo_version = 1
provisioner "local-exec" {
when = create
command = "echo non-ephemeral value: ${aws_ssm_parameter.store_ephemeral_in_write_only.arn}"
}
# provisioner "local-exec" { # (5)
# when = create
# command = "echo write-only value: ${aws_ssm_parameter.store_ephemeral_in_write_only.value_wo}"
# }
provisioner "local-exec" {
when = create
command = "echo ephemeral value from module: #${jsonencode(module.secret_management.secrets)}#" # (6)
}
}
This configuration is using the same module to retrieve the secret by using an ephemeral resource and is using it to create a new resource, passing the ephemeral value into a write-only attribute.
Details:
aws.dev-access provider.aws_ssm_parameter which can be configured with write-only arguments.provisioner block is commented out because interpolation of null values is not allowed in OpenTofu. Reminder: a write-only argument will always be returned as null from the provider even when the configuration is actually having a value.module.secret_management.ephemeral.aws_secretsmanager_secret_version.secret_retrieval[0]: Configuration unknown, deferring...
# ^^^ (1)
OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with
the following symbols:
+ create
OpenTofu will perform the following actions:
# module.secret_management.aws_secretsmanager_secret.manager[0] will be created
+ resource "aws_secretsmanager_secret" "manager" {
+ name = "ephemeral-rfc-example"
# ...
}
# module.secret_management.aws_secretsmanager_secret_version.secret_creation[0] will be created
+ resource "aws_secretsmanager_secret_version" "secret_creation" {
+ secret_string_wo = (write-only attribute)
+ secret_string_wo_version = 2
# ...
}
# ^^^ (2)
Plan: 2 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ secret_manager_arn = (known after apply)
Do you want to perform these actions?
OpenTofu will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
module.secret_management.aws_secretsmanager_secret.manager[0]: Creating...
module.secret_management.aws_secretsmanager_secret.manager[0]: Creation complete after 0s [id=arn:aws:secretsmanager:AWS_REGION:ACC_ID:secret:ephemeral-rfc-example-WGZP9D]
module.secret_management.aws_secretsmanager_secret_version.secret_creation[0]: Creating...
module.secret_management.aws_secretsmanager_secret_version.secret_creation[0]: Creation complete after 0s [id=arn:aws:secretsmanager:AWS_REGION:ACC_ID:secret:ephemeral-rfc-example-WGZP9D]
module.secret_management.ephemeral.aws_secretsmanager_secret_version.secret_retrieval[0]: Opening... # <--- (3)
module.secret_management.ephemeral.aws_secretsmanager_secret_version.secret_retrieval[0]: Opening complete after 0s
module.secret_management.ephemeral.aws_secretsmanager_secret_version.secret_retrieval[0]: Closing... # <--- (4)
module.secret_management.ephemeral.aws_secretsmanager_secret_version.secret_retrieval[0]: Closing complete after 0s
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
This is an output that would be visible when running tofu apply by using store/main.tf.
Details:
module.secret_management.ephemeral.aws_secretsmanager_secret_version.secret_retrieval_direct[0]: Opening...
module.secret_management.ephemeral.aws_secretsmanager_secret_version.secret_retrieval_direct[0]: Opening complete after 0s
module.secret_management.ephemeral.aws_secretsmanager_secret_version.secret_retrieval_direct[0]: Closing...
module.secret_management.ephemeral.aws_secretsmanager_secret_version.secret_retrieval_direct[0]: Closing complete after 0s
# ^^^ (1)
OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
+ create
OpenTofu will perform the following actions:
# aws_ssm_parameter.store_ephemeral_in_write_only will be created
+ resource "aws_ssm_parameter" "store_ephemeral_in_write_only" {
+ name = "parameter_from_ephemeral_value"
+ type = "SecureString"
+ value = (sensitive value)
+ value_wo = (write-only attribute) # <--- (2)
+ value_wo_version = 1
# ...
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
OpenTofu will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
module.secret_management.ephemeral.aws_secretsmanager_secret_version.secret_retrieval_direct[0]: Opening...
module.secret_management.ephemeral.aws_secretsmanager_secret_version.secret_retrieval_direct[0]: Opening complete after 0s
aws_ssm_parameter.store_ephemeral_in_write_only: Creating...
aws_ssm_parameter.store_ephemeral_in_write_only: Provisioning with 'local-exec'...
aws_ssm_parameter.store_ephemeral_in_write_only (local-exec): Executing: ["/bin/sh" "-c" "echo non-ephemeral value: arn:aws:ssm:AWS_REGION:ACC_ID:parameter/parameter_from_ephemeral_value"]
aws_ssm_parameter.store_ephemeral_in_write_only (local-exec): non-ephemeral value: arn:aws:ssm:AWS_REGION:ACC_ID:parameter/parameter_from_ephemeral_value
aws_ssm_parameter.store_ephemeral_in_write_only: Provisioning with 'local-exec'...
aws_ssm_parameter.store_ephemeral_in_write_only (local-exec): (output suppressed due to ephemeral value in config) # <--- (3)
aws_ssm_parameter.store_ephemeral_in_write_only (local-exec): (output suppressed due to ephemeral value in config)
aws_ssm_parameter.store_ephemeral_in_write_only: Creation complete after 0s [id=parameter_from_ephemeral_value]
module.secret_management.ephemeral.aws_secretsmanager_secret_version.secret_retrieval_direct[0]: Closing...
module.secret_management.ephemeral.aws_secretsmanager_secret_version.secret_retrieval_direct[0]: Closing complete after 0s
This is an output that would be visible when running tofu apply by using read/main.tf.
Details:
[!NOTE]
Any rule ending in
If any found, an error will be raised.is having an error defined in the User Documentation section.
In this section, as in the "Proposed Solution" section, we'll go over each concept, but this time with a more technical focus.
Most of the write-only arguments logic is already in the provider-framework:
On the OpenTofu side the following needs to be tackled:
[!NOTE]
Write-only attributes should be presented in the OpenTofu's UI as
(write-only attribute)instead of the actual value.
For enabling ephemeral variables, these are the basic steps that need to be taken:
ephemeral attribute.var.password (ephemeral)
Enter a value:
We should use boolean marks, as no additional information is required to be carried. When introducing the marks for these, extra care should be taken in all the places marks are handled and ensure that the existing implementation around marks is not affected.
[!NOTE]
When adding the mark for ephemeral, ensure that there are unit tests to confirm that multiple mark types can work together:
- When an ephemeral marked value is marked also with deprecated/sensitive, all marks are present on the value.
- When an ephemeral marked value is unmarked for some operations, the other marks are still carried over.
For enabling ephemeral outputs, these are the basic steps that need to be taken:
ephemeral attribute.[!INFO]
For an example on how to properly introduce a new mark in the outputs, you can check the PR for the deprecated outputs.
Strict rules:
Considering the rules above, root modules cannot have any ephemeral outputs defined.
Any local declaration should be marked as ephemeral if in the expression that initialises it an ephemeral value is used:
variable "var1" {
type = string
}
variable "var2" {
type = string
}
variable "var3" {
type = string
ephemeral = true
}
locals {
eg1 = var.var1 == "" ? var.var2 : var.var1 // not ephemeral
eg2 = var.var2 // not ephemeral
eg3 = var.var3 == "" ? var.var2 : var.var1 // ephemeral because of var3 conditional
eg4 = var.var1 == "" ? var.var2 : var.var3 // ephemeral because of var3 usage
eg5 = "${var.var3}-${var.var1}" // ephemeral because of var3 usage
eg6 = local.eg4 // ephemeral because of eg4 is ephemeral
}
Once a local is marked as ephemeral, this can be used only in other ephemeral contexts. Check the Proposed Solution section for more details on the allowed contexts.
Due to the fact ephemeral resources are not stored in the state, this block is not creating a diff in the OpenTofu's UI. Instead, OpenTofu should notify the user of opening/renewing/closing an ephemeral resource with messages similar to the following:
ephemeral.playground_random.password: Opening...
ephemeral.playground_random.password: Opening succeeded after 0s
ephemeral.playground_random.password: Closing...
ephemeral.playground_random.password: Closing succeeded after 0s
Methods that an ephemeral resource should/could have:
Open contains a valid RenewAt, OpenTofu should call this method in order to instruct the provider to renew any possible remote information related to the secret returned from the Open call.Open.Ephemeral resources lifecycle is similar to the data blocks:
Metadata and Schema) while the datasource defines Read compared with the ephemeral resource defining Open. When talking about the basic functionality of the ephemeral resources, the Open method should behave similarly to the Read on a datasource, where it asks the provider for the data associated with that particular ephemeral resource.ValidateConfig as extension of the basic definition.Renew
Open method call, the provider can also specify a RenewAt which will be a specific moment in time when OpenTofu should call the Renew method to trigger an update on the remote information related with the secret returned from the Open call. OpenTofu will have to check for RenewAt value anytime it intends to use the value returned by the ephemeral resource.Close
provider.Close is called. A good example of this is with a Vault/OpenBao provider that could provide a secret by obtaining a lease, and when the secret is done being used, OpenTofu should call Close on that ephemeral resource to instruct on releasing the lease and revoking the secret.As per an initial analysis, the ephemeral blocks should be handled similarly to a data source block by allowing ConfigTransformer to generate a NodeAbstractResource. This is needed because ephemeral resources lifecycle needs to follow the ones for resources and data sources where they need to have a graph vertices in order to allow other concepts of OpenTofu to create depedencies on it.
The gRPC proto schema is already updated in the OpenTofu project and contains the methods and data structures necessary for the epehemeral resources. In order to make that available to be used, providers.Interface needs to get the necessary methods and implement those in GRPCProviderPlugin (V5) and GRPCProviderPlugin (V6).
Beside the attributes that are defined by the provider for an ephemeral resource, the following meta-arguments needs to be supported by any ephemeral block:
lifecycle in ephemeral blocks context are precondition and postcondition. If any found, an error will be raised.Open method detailsWhen OpenTofu will have to use an ephemeral resource, it needs to call its Open method, passing over the config of the ephemeral resource.
The call to the Open method will return the following data:
Private that OpenTofu is not going to use in other contexts than calling the provider Close or Renew optionally defined methods.Result will contain the actual ephemeral information. This is what OpenTofu needs to handle to make it available to other ephemeral contexts to reference.RenewAt timestamp indicating when OpenTofu should call Renew method on the provider before using the data from the Result.Observations:
Result, OpenTofu is expecting to find any non-computed given values in the request, otherwise should return an error.Result, the fields marked as computed can be either null or have an actual value. If an unknown if found, OpenTofu should return an error.[!NOTE]
If any information in the configuration of an ephemeral resource is unknown during the
planphase, OpenTofu should defer the provisioning of the resource for theapplyphase. This means that inconsistency can occur between the plan and the apply phase.
Renew method detailsThe Renew method is called only if the response from Open or another Renew call is containing a valid RenewAt value.
When RenewAt is present, OpenTofu, before using the Result from the Open method response, should check if the current timestamp is at or over RenewAt and should call the Renew method by providing the previously returned Private information, that could be from the Open call or a previous Renew call.
[!NOTE]
Renewdoes not return a new information meant to replace the initialResultreturned by theOpencall. Due to this,Renewis only useful for systems where an entity can be renewed without generating new data.
Close method detailsRight before closing the provider, all the ephemeral resources that were open during the operation should be cleaned up. This means that OpenTofu needs to call Close on every opened ephemeral resource to ensure that any remote data associated with the data returned in OpenResponse.Result is released and/or cleaned up properly.
ConfigValidators and ValidateConfig methods detailsThere is not much to say here, since this is the same lifecycle that a datasource is having.
In case of the checks that an ephemeral resource can be configured with, the behavior of those should not be affected, meaning that even for the ephemeral resources, the results of blocks like precondition and postcondition should be stored in the state.
Having a configuration like the following:
ephemeral "playground_secret" "test" {
...
lifecycle {
precondition {
condition = 1 == 1
error_message = "your message here"
}
}
}
in the state file we should see the following:
{
"...": "...",
"check_results": [
{
"object_kind": "resource",
"config_addr": "ephemeral.playground_secret.test",
"status": "pass",
"objects": [
{
"object_addr": "ephemeral.playground_secret.test",
"status": "pass"
}
]
}
]
}
During the implementation of the ephemeral resources execution, we had to make a decision on how we can include ephemeral resources
in the execution graph for the apply phase, especially when tofu is executed with a plan file.
There were two immediate options available:
DiffTransformer to create concrete graph nodes based on the changelist.We went with the second to ensure that the flow is consistent between tofu apply -auto-aprove and tofu plan -out planfile && tofu apply planfile.
The information from the ephemeral resources that is stored in the plan file is limited to only the address of the resource and the action (ie: OPEN). Anything else related to this type of changes, are trimmed out and must never be stored in the plan file.
[!NOTE]
For more details please refer to #2985.
Due to the scope size this RFC is covering, the testing support will be documented later into a different RFC, or as amendment to this one.
There are already OpenTofu contexts that are not saved in state/plan file:
provider configurationprovisioner blocksconnection blocksIn all of these, referencing an ephemeral value should work as normal.
tofu.applyingThe tofu.applying needs to be introduced to allow the user to check if the current running phase is apply or not.
This is useful when the user wants to configure different properties between these 2 phases.
tofu.applying should be set to true when the apply phase is running and false in any other phase (plan, validate).
[!NOTE]
This keyword is related to the
applyphase and not to theapplycommand, meaning that when runningtofu apply,terraform.applyingshould betrueduring theapplyphase andfalseduring theplanphase. The same rule should stand also when a destroy command is executed.
This is an ephemeral value that should be handled accordingly, meaning that its value or any other value generated from it will not end up in a plan/state file.
[!NOTE]
For feature parity, the same functionality under
tofu.applyingshould be available underterraform.applyingtoo.
ephemeralasnull functionephemeralasnull function is useful when an object built by referencing an ephemeral value wants to be used into a non-ephemeral context.
This is getting a dynamic value and by traversing it, is looking for any ephemeral value and is nullifying it, but it does not nullify any non-ephemeral value within the object.
For example:
variable "secret" {
type = string
default = "test"
ephemeral = true
}
locals {
config = {
"non-ephemeral": "non-ephemeral-value"
"ephemeral": var.secret
}
}
output "test" {
value = ephemeralasnull(local.config)
}
Which after running tofu apply should show an output like this:
test = {
"ephemeral" = tostring(null)
"non-ephemeral" = "non-ephemeral-value"
}
This function should also work perfectly fine with a non-ephemeral value.
[!NOTE]
When we encounter an output in the root module that is referencing an ephemeral value, we could recommend to use
ephemeralasnullto be able to store that information in the state. This would be a warning that will come together with the error diagnostic discussed in the ephemeral outputs section.
Some questions that are also scattered across the RFC:
provider, provisioner and connection be configured with such outputs? Or there is no such thing as "early evaluating a module"?check blocks can have a data block to be used for the assertions, should we consider adding also the ephemeral blocks support inside of the check blocks? Or should we have this as a possible future feature?Website documentation that needs to be updated later: