Terraform must not read Key Vault secrets via data source
| Field | Value |
|---|---|
| ID | DX-TF-0001 |
| AVD ID | AVD-DX-0001 |
| Severity | HIGH |
| Source | DX |
Ensure Key Vault secret values are not read through Terraform data sources
Reading Key Vault secrets through the data.azurerm_key_vault_secret data
source exposes the plaintext secret value during Terraform evaluation. The
resolved value is written to the Terraform plan and to the state file, where it
remains in clear text regardless of how it is consumed afterwards.
State and plan artifacts should always be treated as sensitive, so secret values must never be materialized into them.
Impact
Plaintext secret values become accessible to anyone who can read the Terraform plan or state, widening the blast radius of a credential leak.
Recommended Actions
Do not read secret values with data.azurerm_key_vault_secret. Instead, use
runtime secret references or the write-only secret pattern (value_wo) so that
the value never enters the Terraform plan or state. Follow the appropriate
remediation steps below to resolve the issue.
Problematic Code
data "azurerm_key_vault_secret" "bad_example" {
name = "my-secret"
key_vault_id = azurerm_key_vault.this.id
}
resource "azurerm_app_service" "this" {
# ...
app_settings = {
"SECRET" = data.azurerm_key_vault_secret.bad_example.value
}
}
Recommended Code
# Reference the secret at runtime via Key Vault references.
resource "azurerm_app_service" "this" {
# ...
app_settings = {
"SECRET" = "@Microsoft.KeyVault(VaultName=${azurerm_key_vault.this.name};SecretName=my-secret)"
}
}
When you need to write a secret, prefer the ephemeral resources and write-only
value_wo argument so the value is kept out of state:
# Key Vault
resource "azurerm_key_vault_secret" "this" {
name = "my-secret"
key_vault_id = azurerm_key_vault.this.id
value_wo = var.secret_value
value_wo_version = 1
}
# PostgreSQL server
ephemeral "random_password" "db" {
length = 32
special = true
}
resource "azurerm_postgresql_flexible_server" "this" {
# ...
administrator_login = "pgadmin"
administrator_password = ephemeral.random_password.db.result
}
resource "azurerm_key_vault_secret" "db_password" {
name = "postgres-admin-password"
key_vault_id = azurerm_key_vault.this.id
content_type = "text/plain"
value_wo = ephemeral.random_password.db.result
value_wo_version = 1 # increment this on every rotation
}