In version 1.11, HashiCorp introduced Terraform Ephemeral resources and write-only attributes to allow for root configs that do not store secrets in the Terraform statefile. But many users ask about how they can adopt ephemerals. This blog attempts to lay out the ways secrets can be stored in state and how you should update your configurations to remove those secrets.
Note: For a primer on ephemerals (see this blog post).
Scenarios to consider:
- Data sources that fetch a static secret
- Resources that receive a secret
- Resources that generate a dynamic secret
- Resources that fetch generated secrets to store in another 3rd party system
Scenario 1: Data sources with static secrets
Ephemeral resources can often be a drop-in replacement for data sources pulling static values:
data "vault_kv_secret_v2" "static_kv" {
mount = "kvv2"
name = "my_password"
}
ephemeral "vault_kv_secret_v2" "static_kv" {
mount = "kvv2"
name = "my_password"
}
However, using these values has 1 specific difference. The attributes on a ephemeral resource are considered ephemeral and can only be used as ephemeral arguments. That means 2 places:
Provider blocks
Provider blocks are considered ephemeral, so ephemeral resources may populate arguments:
provider "example" {
password = tostring(ephemeral.vault_kv_secret_v2.static_kv.data.password)
}
Write-only arguments
Write-only arguments are special arguments that require the ephemeral taint for values:
resource "aws_db_instance" "example" {
...
password_wo = tostring(ephemeral.vault_kv_secret_v2.static_kv.data.password)
}
If the resource you wish to pass a value to does not have an available ephemeral, open an issue with that provider. You can reference:
- this blog post
- this agent skill
Scenario 2: Resources that receive a static secret
Without duplicating to the section above, write-only arguments are a way to get secrets out of state. Above has guidance if the secret value comes from a data source, but what if its from a variable? In that case, we just need to add the ephemeral taint to the variable declaration:
variable "my_password" {
type = string
ephemeral = true
senstitive = true
}
resource "aws_db_instance" "example" {
...
password_wo = var.my_password
}
Scenario 3: Resources that generate a dynamic secret
Now is when things get slightly trickier. There are some resources that generate dynamic secret content:
# generates a random password
resource "random_password" "pw" {
length = 16
special = true
override_special = "!#$%&*()-_=+[]{}<>:?"
}
These can also be replaced with an ephemeral resource but theres 1 difference. Ephemeral resources have no state and run every plan & apply. This means that every time plan or apply is run, the random_password will be regenerated.
To account for this, users should use the wo_version arguments:
variable "password_keeper" {
type = number
description = "Bump this number to update the password associated with `ephemeral.random_password.pw`."
default = 1
}
ephemeral "random_password" "pw" {
length = 16
special = true
override_special = "!#$%&*()-_=+[]{}<>:?"
}
resource "aws_db_instance" "example" {
...
password_wo = ephemeral.random_password.pw.result
password_wo_version = var.password_keeper
}
If you would like to know about how to implement this pattern in modules, see this blog post.
Scenario 4: Resources or data sources that fetch generated secrets to store in another 3rd party system
There are many situations when a user would like to use Terraform to generate and hand off a secret between 2 3rd party systems. A typical workflow is asking Terraform to generate a API key that will be given to another 3rd party system to give it access. This is currently a feature gap for ephemerals.
Take the example of giving a HCP Terraform API token to give to another tool (GitHub Pipeline, auditing tool, etc):
resource "tfe_team_token" "test" {
team_id = tfe_team.test.id
description = "my team token"
}
Although you may think an ephemeral resource for this could fix the issue, it sneakily does not. Heres why:
ephemeral "tfe_team_token" "example" {
team_id = tfe_team.example.id
}
The above code would execute every plan & apply and delete the token at the end of the run. This means that for normal terraform run workflow, 2 team tokens would be generated and deleted. Even if you gave the token to a 3rd party system (AWS secrets manager), the value would be stored but the key deleted.
As stated above, these ephemerals can be used to authenticate providers. This is commonly done with the value dynamic credentials backends:
ephemeral "vault_terraform_token" "tf_token" {
role_name = "tfe_admin"
}
provider "tfe" {
token = ephemeral.vault_terraform_token.tf_token.token
}
Closing
This blog walked you through the scenarios and solutions you will encounter when trying to remove secrets from your Terraform statefile.
If you would like a way to detect when your workspaces are using resources that store secrets in state, check out this Sentinel Ephemerality Policy.
Top comments (0)