Terraform 1.10 has introduced the concept of ephemeral values for outputs and input variables, and as a new object type. Before I dig into the details of how they work, first I think it’s important to understand the problem ephemeral values are trying to solve.
We all know that Terraform state can contain sensitive information. After all, it contains all the attributes of each resource and data source and any output values defined in the configuration. Terraform uses these values to calculate an execution plan when updates are made to the configuration.
An additional concern is sensitive information found in saved Terraform plan files. Plan files not only contain the entirety of the configuration, but also the planned changes and variable values used to create the plan.
How can you remove sensitive information from state data and saved plans? That is what ephemeral values are meant to do. You can mark input variables and output values as ephemeral, and there is a brand new object type: ephemeral resources.
Unlike data sources or managed resources, an ephemeral resource and its attributes are never written to state or an execution plan. Additionally, ephemeral resources and their properties cannot be used in the context of a non-ephemeral object. If they could, then the values of the ephemeral resource would end up in state or a plan, and would defeat the purpose behind them.
Input variables and outputs marked as ephemeral have similar restrictions. You can only use an ephemeral value for an argument that will not be recorded in state.
Given that you can’t use an ephemeral value inside of a typical managed resource or data source, what good are they? Well there are a few use cases:
We will explore write-only arguments in their own blog post, so for now let’s focus on the first two use cases.
Input variables and output values can be marked as ephemeral simply by adding the ephemeral
argument and setting it to true:
variable "identity_token" {
type = string
description = "Token used for Azure authentication."
sensitive = true
ephemeral = true
}
The syntax for an ephemeral resource is basically the same as a managed resource:
ephemeral "<ephemeral_resource_type>" "<name_label>" {
<identifier> = <expression>
}
Ephemeral resources are a new object type in Terraform 1.10, and they appear as a separate category in the provider documentation. Often they mirror existing data source types, but the implementation is different. Currently, the AzureRM, AWS,GCP, and Vault providers have all added support for ephemeral resources, with more coming in the future.
The current version (4.35.0
) of the azurerm
provider supports the following ephemeral resource types:
azurerm_key_vault_secret
azurerm_key_vault_certificate
If you have a secret stored in Key Vault that you want to use, the syntax for the ephemeral resource would be:
ephemeral "azurerm_key_vault_secret" "example" {
name = var.key_vault_secret_name
key_vault_id = var.key_vault_id
}
Now what can you do with this secret? Can you use it in a regular resource?:
resource "azurerm_container_group" "example" {
name = "ephemeral-continst"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
#...
container {
name = "hello-world"
image = "mcr.microsoft.com/azuredocs/aci-helloworld:latest"
cpu = "0.5"
memory = "1.5"
secure_environment_variables = {
KEY_VAULT_SECRET = ephemeral.azurerm_key_vault_secret.example.value
}
}
}
Nope! When you run terraform validate
you’ll get the error:
Error: Invalid use of ephemeral value
│
│ with azurerm_container_group.example,
│ on main.tf line 36, in resource "azurerm_container_group" "example":
│ 36: KEY_VAULT_SECRET = ephemeral.azurerm_key_vault_secret.example.value
│
│ Ephemeral values are not valid in resource arguments, because resource instances must persist between Terraform phases.
To generate a proper execution plan, Terraform would have to persist the secret value beyond the planning phase. Since we’re trying to avoid that, Terraform throws an error.
So if we can’t use ephemeral values with regular resource or data source arguments, where can we use them? You can use them with the follow object types:
Note that ephemeral outputs cannot be used in the root module, because then they would be written to state.
Also, since ephemeral values are not written to the plan file, they are calculated on each Terraform run. So you might get a different value for plan than you do for apply. That’s a pretty important thing to note. If your ephemeral value is non-deterministic, the value may be different between the plan and apply.
Honestly, the biggest use case with the release of Terraform 1.10 was provider configuration. Say for instance, you are using the Kubernetes provider to deploy a manifest and you have the Kubernetes credentials stored in Azure Key Vault secret. You could grab the secret with an ephemeral resource and pass it to the Kubernetes provider block:
ephemeral "azurerm_key_vault_secret" "k8s" {
for_each = toset(["client-certificate", "client-key", "cluster-ca-certificate"])
name = each.key
key_vault_id = var.key_vault_id
}
provider "kubernetes" {
host = "https://${var.aks_cluster_host}"
client_certificate = base64decode(ephemeral.azurerm_key_vault_secret.k8s["client-certificate"].value)
client_key = base64decode(ephemeral.azurerm_key_vault_secret.k8s["client-key"].value)
cluster_ca_certificate = base64decode(ephemeral.azurerm_key_vault_secret.k8s["cluster-ca-certificate"].value)
}
Because the provider configuration is never stored in state or the plan, this is a valid use of an ephemeral resource. You can also use meta-arguments with an ephemeral resource, as shown above with the for_each
argument. Ephemeral resources also support:
depends_on
count
for_each
provider
lifecycle
The other potential use case is for provisioners, but here I would remind you that provisioners are considered an anti-pattern and not recommended. If you must though, you could run a remote-exec
every time a member is added to a cluster like this:
ephemeral "azurerm_key_vault_secret" "example" {
name = var.key_vault_secret_name
key_vault_id = var.key_vault_id
}
resource "terraform_data" "cluster" {
triggers_replace = azurerm_linux_virtual_machine.cluster[*].id
connection {
type = "ssh"
user = "clusteradmin"
private_key = ephemeral.azurerm_key_vault_secret.example.value
host = azurerm_linux_virtual_machine.cluster[0].private_ip_address
}
provisioner "remote-exec" {
inline = [#...]
}
}
Provisioners also do not write their connection information to state or plan.
The introduction of ephemeral values in Terraform 1.10 is a huge step towards getting secrets out of state and plan, but there is one key thing missing that closes the loop with resources and data sources. Write-only arguments. I’ll address those in my next post.
July 1, 2025
On HashiCorp, IBM, and Acceptance
March 3, 2025
The Science and Magic of Network Mapping and Measurement
January 9, 2025
January 2, 2025