Make your Terraform deployments more secure with Ephermal resources

From Terraform version 1.10
and later we now have support for ephermal
blocks which are resources that will not be persisted to the Terraform state. A reoccurring issue with Terraform is secrets and sensitive values in state management. Sure, state is for example encrypted at rest when residing in Azure Storage but that does not mean that I as an Azure Administrator with access to the blob can't open it and find the secret values stored in there.
Ephermal resource blocks in Terraform resolve this issue as the resource data is not persisted to the state-file (or any plan or state artifact) as is the case with a data
or resource
block for example. ephermal
blocks are essentially temporary resources used to reference data and connect to other systems. Once Terraform is done reading from the resources it closes it and will only use the data for the duration of the actual run-phase.
Were you aware that secrets were available in Terraform state with resource
and data
blocks? I don't know if too many are aware of this.
Example with Azure Keyvault
To demonstrate what this looks like in the actual "wild" we will first create a Terraform deployment that contains a key vault and a key vault secret. We do this by first defining our teraraform
block and provider
configuration:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "4.12.0"
}
}
}
provider "azurerm" {
features {}
subscription_id = "-your-subscription-id-"
}
data "azurerm_client_config" "current" {}
The data block will be used by the deployment later
Once we have that we will add our resource group, key vault and key vault secret:
resource "azurerm_resource_group" "this" {
name = "rg-prod-we-kv"
location = "West Europe"
}
resource "azurerm_key_vault" "this" {
name = "kv-prod-we-secret"
location = azurerm_resource_group.this.location
resource_group_name = azurerm_resource_group.this.name
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = "premium"
soft_delete_retention_days = 7
access_policy {
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = data.azurerm_client_config.current.object_id
key_permissions = [
"Create",
"Get",
]
secret_permissions = [
"Set",
"Get",
"Delete",
"Purge",
"Recover"
]
}
}
resource "azurerm_key_vault_secret" "this" {
name = "secret-sauce"
value = "szechuan"
key_vault_id = azurerm_key_vault.this.id
}
This is what the configuration looks like:

Now if we look at the terraform.tfstate
file we can drill down into the details of it and sure enough find the secret we created:

Here is the password, in clear text. The first time I learned that state deals with secrets this way I must admit I was a bit shocked. This is why it is important to store the statefiles in a safe place, using services that provide encryption like Azure Storage.
Similar deployment but with an Ephermal resource instead
In my second deployment we will reference the previously created key using the ephermal Terraform resource:
ephemeral "azurerm_key_vault_secret" "this" {
name = "secret-sauce"
key_vault_id = data.azurerm_key_vault.this.id
}

The resulting terraform.tfstate
file looks like this instead:

Here is a side by side comparison of the two different statefiles with my first configuration on the left and the configuration with the ephermal resource on the right:

Conclusion
Today we looked at deploying resources that deal with secrets. It was key vaults and its components but it could also have been a virtual machine where we need to deal with its local administrator password in some way.
There are other resources and deployments that will contain sensitive values. Maybe secrets should be created outside of your IaC configuration and referenced by Terraform using ephermal
blocks instead? How would you do it?
Hope you found this useful.
References


About me
