Skip to main content

Ephemerality

Ephemerality refers to the nature of a value to be "ephemeral", meaning that it will not be stored in the state or the plan. With this concept, transient and/or confidental information can be safely handled within OpenTofu.

Related concepts:

  • Sensitive resource fields, outputs and variables
    • Marking values as sensitive ensures the contents are sanitized from the user interface.
    • Sensitive values will still be stored in plaintext in the state and plan.
  • State Encryption
    • Encryption of state and plan data can be configured to prevent unauthorized access and tampering.
    • Security of this data is determined by the encryption methods used and access to the encryption keys.

In contrast, Ephemeral values only exist for the duration of single execution of the tofu command and are never stored in state or plan data. This enables representation of transient concepts, such as temporary network tunnels or confidential keys managed by an external system.

It is recommended that all three concepts are considered when working with confidential information. If possible use ephemeral values/resources to prevent storing the data at all. If confidential data must be stored, ensure that it is marked as sensitive and that state and plan encryption are enabled and properly configured.

As part of this concept, the following constructs can be used:

Compatibility​

Additional Topics​

Usage example​

A module to handle various usage patterns of secrets​

In the following example you can see a module that can handle secret creation and retrieval by using ephemeral variables, ephemeral resources, write-only attributes, ephemeral outputs and validations using ephemeral values in their condition.

Code Block
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">=6.0.0"
}
}
}

variable "secret_map" {
type = map(string)
default = null # default because in some cases this module can be used only for reading the secret and not storing it
ephemeral = true # defines an ephemeral variable since it holds the secrets to be processed
description = "The map of secrets to be used to create the new secret entry. Omit this when using this module to read the secret"
}

variable "secret_version" {
# needed to update a write-only attribute
type = number
default = 0
description = "The version used to update the secret. You need to bump this from the previous version in order for the secret_map content to be persisted. Omit this when using this module to read the secret"
}

variable "secret_manager_arn" {
type = string
default = ""
description = "The map of secrets to be used to create the new secret entry"
validation {
condition = (var.secret_manager_arn == "" && var.secret_map != null) || (var.secret_manager_arn != "" && var.secret_map == null)
error_message = "var.secret_manager_arn should not be used in the same time with var.secret_map. Use the module only with var.secret_manager_arn to read the secret or use it with var.secret_map and var.secret_version to create a new secret"
}
}

resource "aws_secretsmanager_secret" "manager" {
count = var.secret_version > 0 ? 1 : 0 # used when we create a new secret manager
name = "testin-secret-manager"
}

resource "aws_secretsmanager_secret_version" "secret_creation" {
count = var.secret_version > 0 ? 1 : 0 # used when we want to create a new secret
secret_id = aws_secretsmanager_secret.manager[0].arn
secret_string_wo = jsonencode(var.secret_map) # here we pass in the ephemeral variable into a write-only attribute
secret_string_wo_version = var.secret_version
# and when we want to update it, we can provide a different value for var.secret_map and an incremented secret_version
}

ephemeral "aws_secretsmanager_secret_version" "secret_retrieval" {
# used to read the secret from the secret manager after creating it
count = var.secret_version > 0 ? 1 : 0
secret_id = aws_secretsmanager_secret.manager[0].arn
depends_on = [
aws_secretsmanager_secret_version.secret_creation # ensure that we want for the secret creation before reading it
]
}

ephemeral "aws_secretsmanager_secret_version" "secret_retrieval_direct" {
# used to read the secret from the secret manager when the module is used only for reading without creating a new secret
count = var.secret_version > 0 ? 0 : 1
secret_id = var.secret_manager_arn
}

output "secrets" {
value = "${var.secret_version > 0 ?
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 # marking an output as ephemeral is mandatory when the value points to an ephemeral value
}

output "secret_manager_arn" {
value = var.secret_version > 0 ? aws_secretsmanager_secret.manager[0].arn : null
# available only when the module is used to create a secret
}

Using the module in a configuration to store a secret​

The following configuration uses the module above to create a new secret where it stores the given aws credentials and outputs the ARN of the created secret manager:

Code Block
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "6.0.0-beta1"
}
}
}

provider "aws" {
alias = "secrets-read-write"
}

variable "access_key" {
type = string
ephemeral = true
}

variable "secret_key" {
type = string
ephemeral = true
}

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 = 1 # first version of the secret. If want to update the secret inside, bump this version
}

output "secret_manager_arn" {
value = module.secret_management.secret_manager_arn
}

Using the module in a configuration to retrieve a secret and configure a provider​

The following configuration uses the module above to read the secret, configure a provider with the credentials retrieved and store the same credentials in a write-only attribute value_wo of the aws_ssm_parameter resource. Additionally, it adds two local-exec provisioners. The execution of the first one will print the command content but the second one will print (output suppressed due to ephemeral value in config):

Code Block
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" {
providers = {
aws = aws.read-secrets
}
source = "../mod"
secret_manager_arn = var.secret_manager_arn
}

# Provider can be configured with the credentials returned by the ephemeral resource inside the module.
provider "aws" {
alias = "dev-access"
access_key = module.secret_management.secrets["access_key"]
secret_key = module.secret_management.secrets["secret_key"]
}

resource "aws_ssm_parameter" "store_ephemeral_in_write_only" {
provider = aws.dev-access
name = "parameter_from_ephemeral_value"
type = "SecureString"
value_wo = jsonencode(module.secret_management.secrets) # Using the secrets again in a write-only attribute
value_wo_version = 1 # bump this if `value_wo` needs to be updated

# Because this provisioner uses only regular attributes, it will print the output of the command
provisioner "local-exec" {
when = create
command = "echo non-ephemeral value: ${aws_ssm_parameter.store_ephemeral_in_write_only.arn}"
}

# Because this provisioner uses ephemeral values, its output will be suppressed
provisioner "local-exec" {
when = create
command = "echo ephemeral value from module: #${jsonencode(module.secret_management.secrets)}#"
}
}