Ephemeral Resources and Write-Only Arguments
For most of Terraform's life, any secret it touched ended up in state in plaintext — a database password it generated, a credential it fetched, a token it read. The sensitive flag hid those values from the console, but the bytes still sat on disk in the state file. Ephemeral resources (Terraform 1.10) and write-only arguments (1.11) are the direct fix: a way for values to flow through a run without ever being written to state.
These are recent additions, and provider support is still expanding — not every resource exposes write-only arguments yet. But they change the answer to "how do I keep secrets out of state" from "you can't, mitigate the blast radius" to "use the feature built for it, where the provider supports it."
The Secrets-in-State Problem
State records resource attributes so Terraform can diff against them, and that recording is indiscriminate. When you set an RDS instance's password, it lands in state. When random_password generates a credential, the generated value lands in state. A fetched secret, a private key, an access token — all plaintext, all on disk. Marking the value sensitive changes none of that; it only stops the value from printing in plan and apply output. The real exposure has always been the state file itself, which is why the earlier advice was to encrypt the backend and lock it down.
Ephemeral Resources
An ephemeral resource produces a value that exists only for the duration of a run and is never persisted to state. You declare it with the ephemeral block, and Terraform opens it when needed, hands the value to whatever consumes it, and closes it when the run ends. The classic use is fetching a short-lived credential or a secret to pass into another resource — the value is used in flight and then gone.
# fetch a secret at run time; its value is never written to state ephemeral "aws_secretsmanager_secret_version" "db" { secret_id = aws_secretsmanager_secret.db.id }
The result is referenced like any other resource, but it cannot be stored — you can use it within the same apply, pass it to a write-only argument, or surface it through an ephemeral output, and that is all. Terraform enforces that an ephemeral value never crosses into state.
Write-Only Arguments
A write-only argument is an argument a provider sends to its API but deliberately does not save to state. A database password is the canonical example: the provider needs the value to create or update the instance, but there is no reason to keep it on disk afterward. Because the value is not in state, Terraform can't compare it across runs to decide whether it changed — so a write-only argument is paired with a version argument that you bump to signal "the secret changed, send it again."
resource "aws_db_instance" "main" { engine = "postgres" username = "app" # the value is sent to the API but never stored in state password_wo = ephemeral.aws_secretsmanager_secret_version.db.secret_string # bump this when the secret rotates so the provider re-sends it password_wo_version = 2 }
Without the version argument the provider has no way to know an out-of-band rotation happened, because it can't see the old value to compare. Incrementing password_wo_version is the explicit "update it now" signal. Pair an ephemeral resource with a write-only argument and a secret can travel from a secrets store to an API without ever touching state.
Ephemeral Values in Variables and Outputs
The ephemeral property extends to the configuration's edges. An input variable can be marked ephemeral = true so a secret passed in at run time is never persisted, and an output can be marked ephemeral so it can carry a value between modules within a run without landing in state. This lets a secret flow end to end — in through a variable, through the modules, out to an argument — without a single plaintext copy on disk.
Adoption and Provider Support
Ephemeral resources require Terraform 1.10 and write-only arguments require 1.11, so neither exists on older versions. More limiting in practice is provider coverage: a provider has to explicitly add a write-only variant of an argument, and that rollout is ongoing rather than complete. Before you design around these features, check the resource's Registry documentation for the write-only argument or ephemeral resource you intend to use — and have the state-encryption-and-lockdown approach ready as the fallback where support hasn't landed yet.
sensitive — hides a value from plan and apply output, but still stores it in plaintext in state. It is display hygiene, not protection. Use it so secrets don't print into logs, never as your actual secret-protection mechanism.
ephemeral / write-only — keep the value out of state entirely: ephemeral resources exist only during a run, write-only arguments reach the API but are never persisted. Use these for genuine secret protection wherever your Terraform version and the provider support them.
- Still treating
sensitiveas secret protection when ephemeral resources and write-only arguments now keep the value out of state, leaving the real plaintext exposure in place. - Assuming every resource supports write-only arguments — provider coverage is still expanding, and many arguments have no write-only variant yet.
- Using a write-only argument without its paired version argument, so the provider can't detect a rotation and silently never updates the secret.
- Designing around these features on Terraform older than 1.10 / 1.11, where ephemeral resources and write-only arguments simply don't exist.
- Trying to store or output an ephemeral value normally — Terraform refuses, because an ephemeral value is forbidden from crossing into state.
- Use ephemeral resources and write-only arguments to keep secrets out of state wherever the provider and your Terraform version support them.
- Pair every write-only argument with its version argument and bump it on rotation, so the provider re-sends the changed secret.
- Check the resource's Registry documentation for write-only and ephemeral support before relying on it, and keep state encryption as the fallback.
- Treat
sensitiveas display hygiene to keep secrets out of logs, not as the mechanism that protects them. - Flow secrets end to end through ephemeral variables, resources, and outputs so no plaintext copy ever lands on disk.
Knowledge Check
How does an ephemeral resource or write-only argument differ from marking a value sensitive?
- Ephemeral/write-only keep the value out of state entirely; sensitive only hides it from output while still storing it in plaintext
- Sensitive encrypts the value at rest inside the state file, while ephemeral and write-only only hide it from the CLI logs and plan output
- They are aliases for the exact same behavior, just named differently across Terraform versions
- Ephemeral values are stored encrypted; sensitive values are stored in a separate file
Why does a write-only argument need a paired version argument?
- Because the value isn't in state, Terraform can't compare it across runs — bumping the version is the explicit signal to re-send it
- To encrypt the argument with a freshly rotating symmetric key derived on each and every apply
- To let multiple providers share the same secret value
- Because Terraform requires every argument in a resource block to carry its own explicit version number before any plan or apply will run
What is the main practical limit on using these features today?
- They need Terraform 1.10/1.11, and provider support for write-only arguments is still expanding rather than complete
- They only work with the S3 remote backend and refuse to run against local state on disk
- They require a paid HCP Terraform subscription before the CLI will enable either feature
- They can only protect secrets generated in-config by random_password, not values that arrive from an external provider
What can you do with an ephemeral resource's value?
- Use it within the run — pass it to a write-only argument or an ephemeral output — but never store it in state
- Store it as a normal persisted output so that other downstream stacks can read the value later through remote state
- Persist it to state so the next plan can reliably diff the stored value against it
- Write it to the dependency lock file for reuse
You got correct