· via dev.to (home feed)
Terraform Ephemeral Resources Keep Vault Secrets Out of State and Git
A dev.to guide shows how Terraform's ephemeral resources and write-only arguments pull Vault secrets at apply time without writing them to state, with a fallback for resources that lack support.

A guide published on dev.to by Oleksandr Kuryzhev, originally posted to his kuryzhev.cloud blog, lays out a working pattern for teams that need a database password in Terraform: keep it in HashiCorp Vault, fetch it at apply time, and make sure nothing sensitive lands in the repository or the state file.
The state file is the leak
The article starts from a familiar shortcut: someone pastes a terraform.tfvars file into a CI variable. The conventional alternative, the vault_kv_secret_v2 data source, has a flaw the author emphasizes because HashiCorp documents it: every value read through a data source is written into Terraform state in plain text. Flagging a variable as sensitive only suppresses it in CLI output; the underlying value still travels to whatever backend holds the state.
Ephemeral values close the gap
According to the guide, newer Terraform and Vault provider releases fix this with two features: ephemeral resources, available from Terraform 1.10, and write-only arguments, which need 1.11. An ephemeral vault_kv_secret_v2 block pulls the secret while planning and applying without recording the value anywhere. Terraform additionally rejects references to ephemeral values from ordinary attributes at validation time, which turns a misconfiguration into an error rather than a silent leak.
Write-only arguments are the receiving end of the pattern: the value is passed to the provider's API call but omitted from both plan and state files. The example wires the ephemeral secret into password_wo on an AWS database instance. Because Terraform no longer stores the value, it cannot notice when the secret changes in Vault, so a companion argument, password_wo_version in the example, has to be incremented by hand to force an update.
The prerequisites deserve a check before adoption: the Vault provider must be a release that ships the ephemeral resource (the article suggests pinning the 5.x line and verifying the minimum in the changelog), and the target resource must actually expose a write-only argument, something providers have been adding gradually.
Setup gotchas
Two failure modes stand out. First, the Vault provider by default asks for a child token, which requires permission on auth/token/create; a tightly scoped CI policy will fail with a 403 unless that capability is granted or skip_child_token is set. Second, KV v2 policies must target secret/data/... rather than the CLI-style path, and a policy scoped to secret/app/db appears correct yet still yields permission denied.
For authentication, the guide recommends AppRole or JWT/OIDC for CI rather than a long-lived root token, with short token TTLs so a leaked credential dies quickly. An encrypted remote state backend with restricted access remains a baseline in every variant of the setup.
The fallback when write-only is unavailable
Not every resource supports write-only arguments yet. Where the data source is unavoidable, the article's advice is to assume the state file contains the secret and lock it down accordingly: encrypted remote backend, read access limited to the people entitled to see the secret, and versioning plus access logging when the backend is an S3 or GCS bucket. Dynamic secrets from Vault's database and AWS engines are the preferred option in that situation, since a value that does end up captured becomes useless once its short TTL lapses. The piece also notes that recent provider releases add a write-only data_json_wo argument for cases where Terraform manages the secret's value itself.
Verifying the pattern
The test sequence in the guide runs as follows: confirm the policy with vault token capabilities against the data path, run terraform validate and a plan, during which an illegal reference from a normal attribute fails, then apply using a throwaway secret and search both the state and the saved plan for that value. Both matches should count zero; a non-zero result means something is still reading through a data source, and terraform state list will surface the offending data.vault_kv_secret_v2 entries. Rotation is confirmed by changing the secret in Vault, bumping the version argument, and checking that the plan shows an in-place update.
Why it matters
State files are one of the most common ways managed secrets escape an infrastructure team: they are plain text, often broadly readable, and easy to overlook when attention is focused on git history. This pattern removes the secret from state entirely instead of trying to obscure it, and the validation-time guardrail catches regressions automatically. For teams not yet on Terraform 1.11, or whose providers lack write-only support, the fallback guidance of encrypted backends, restricted access and dynamic credentials is the practical interim control.
- #terraform
- #hashicorp-vault
- #infrastructure-as-code
- #security
- #cloud