RSS

The IaC Engineer's Guide to Terraform Ephemeral Resources

Terraform Security State Management Best Practices

Terraform ephemeral resources close a hole that's existed since the first terraform.tfstate file: a secret you reference has to live somewhere. Until v1.10, that somewhere was plaintext state. Ephemeral resources fix the leak at the value level, but they don't settle the next question a security team asks: not if the secret is visible, but if anyone can prove it was ever touched at all.

TL;DR
$ cat terraform-ephemeral-resources.tldr
• What actually makes an ephemeral block different from a resource or a data source.
• The open, renew, close lifecycle Terraform runs for every ephemeral resource, and where it defers to apply.
• A working example pairing an ephemeral resource with a write-only argument to get a database password onto an RDS instance without it ever touching state.
• Which providers support ephemeral resource types today, and what to do about the ones that don't yet.

Every Terraform state file is, structurally, a database of everything your infrastructure has ever needed to know about itself: instance IDs, subnet ranges, security group rules, and the credentials you passed in along the way.

For years, that's meant a terraform.tfstate (or a saved plan) that sits somewhere as a plaintext liability, one misconfigured Terraform backend away from becoming an incident.

Terraform 1.10 introduced ephemeral resources to close that gap. It is a new kind of block with a value that Terraform uses during a run, but never writes down.

In this guide, I cover what an ephemeral block actually is, how it differs from the resources and data sources you already know, the lifecycle Terraform runs for one, and a working process to pair an ephemeral resource with a write-only argument and get a secret onto a managed resource without it touching state.

I also touch on what this means for a team running Terraform at Enterprise scale, as eliminating one line item from state doesn't eliminate every governance question a security team has.

What are Terraform ephemeral resources?

An ephemeral block declares a resource that exists only for the current Terraform operation: plan, apply, or destroy. Terraform never writes it, or any value derived from it, into state or a plan file. You reference one block through ephemeral.<TYPE>.<LABEL>, which is the same addressing structure as a managed resource. However, any resemblance stops at the syntax.

Terraform already had two categories of blocks that persist data:

  1. A managed resource, which Terraform creates, tracks, and keeps writing back to state for the life of the infrastructure
  2. A data source, which Terraform re-reads and re-persists on every refresh.

Ephemeral resources are a third category, with a defining trait that applies to neither of the first two: These resources are essentially temporary, existing only for the phase that requested them and gone the moment it finishes.

Terraform 1.10 shipped this update alongside a companion mechanism: variables and outputs marked ephemeral = true.

A variable flagged this way carries the same guarantee (its value won't be recorded in state), making it useful for passing a short-lived token into a module without it becoming a permanent fixture of that module's state.

An ephemeral resource and an ephemeral variable solve the same problem from two directions: one generates or fetches ephemeral data itself, the other lets you pass ephemeral data through your configuration's boundaries.

In practice, the provider ecosystem has settled on unsurprising ephemeral resource types that map to the systems you'd expect:

If you've been reading a secret out of a data source and feeding it into a provider block, there is very likely now an ephemeral equivalent for it.

The ephemeral resource lifecycle and how it works

Terraform manages an ephemeral resource through three explicit steps, using new verbs you won't find elsewhere: open, renew, and close.

  1. It opens the resource when something downstream needs its result (a provider block, a locals reference, another ephemeral resource, for example).
  2. If the run needs that access for longer than the remote system's own lease allows, Terraform renews it, calling back into the provider to extend the expiration rather than re-opening from scratch.
  3. Once everything that depended on it has finished the current phase, Terraform closes it, which for something like a Vault lease means the credential is explicitly revoked, not just forgotten about.

That lifecycle holds together because Terraform restricts where you're allowed to reference an ephemeral value. Try to pass one into a regular managed resource argument (not a write-only one), and Terraform refuses outright:

Error: Invalid use of ephemeral value
Ephemeral values are not valid in resource arguments, because
resource instances must persist between Terraform phases.

That error is intentional. A managed resource's arguments are supposed to be reconstructable from state on the next run; an ephemeral value, by definition, won't be there to reconstruct from.

The valid contexts – a locals block, a provider block, another ephemeral resource, ephemeral variables and outputs, provisioner connections, and write-only arguments – are exactly the places where nothing needs to persist between runs.

Ephemeral resources also sit in Terraform's dependency graph like any other node. If an ephemeral resource's arguments reference something Terraform can't resolve until after the plan is generated, it defers execution of that resource to the apply stage, the same deferral behavior you'd see from a managed resource with an unknown value.

How to use Terraform ephemeral resources

Pair an ephemeral resource with a write-only argument to make it useful for more than provider configuration.

Write-only arguments (named with a _wo suffix and a companion _wo_version integer) are the other half of Terraform's secrets work: a managed resource argument that accepts an ephemeral value, uses it during apply, and then, like the ephemeral resource that fed it, never stores it.

Here's a working example: generating a distinct database password per environment and writing it straight into each RDS instance's master password, without the password ever reaching state.

locals {
environments = toset(["staging", "production"])
}
ephemeral "random_password" "db_master" {
for_each = local.environments
length = 24
override_special = "!#%&*()-_=+[]{}"
}
resource "aws_db_instance" "primary" {
for_each = local.environments
identifier = "${each.key}-primary-db"
engine = "postgres"
engine_version = "16.4"
instance_class = "db.r6g.large"
allocated_storage = 100
username = "app_admin"
password_wo = ephemeral.random_password.db_master[each.key].result
password_wo_version = 1
skip_final_snapshot = each.key != "production"
}

Bump password_wo_version on a future apply, and Terraform regenerates and rewrites the password; leave it alone, and the existing password stays untouched, because Terraform has no old value in state to compare it against in the first place.

The same shape works if the source is Vault or AWS Secrets Manager instead of random_password: fetch the credential ephemerally, decode it in a local value if it arrives as JSON, then hand the individual fields to whichever write-only arguments need them.

Every meta-argument you'd expect on a managed resource carries over to an ephemeral block:

Use that last one deliberately. A postcondition is one of the only ways to validate an ephemeral value's shape – length, character set, and format – without assigning it anywhere that would persist it, since you obviously can't run terraform output on an ephemeral value to eyeball it.

Where ephemeral resources fit into an Enterprise Terraform workflow

Getting a secret out of state stops the leak, but it doesn't answer the question a security team asks the day after an incident: which run touched this credential, when, under whose authorization, and what else that run reached.

GitGuardian's State of Secrets Sprawl 2026 report found that 64% of secrets detected as exposed back in 2022 were still valid and exploitable four years on, largely because teams can't rotate a credential without risking a production outage, so most don't.

GitGuardian chart of the percentage of exposed secrets still valid rather than rotated, by year of detection: 64% still valid in 2022, 66% in 2023, 70% in 2024, and 77% in 2025.

A flat state file is exactly where a secret goes to become one of those statistics. It was never built to say who touched it and when, regardless of what's ephemeral; it's a snapshot of current values, not an audit trail of access.

That governance question sits one layer above the language feature, closer to IaC security than to Terraform syntax. Of course, an ephemeral resource, by design, never reaches state or a plan file, so a data model built from state and plan (Stategraph's included) can't reconstruct that specific lease's open, renew, and close events after the fact.

What Stategraph's transaction timeline does cover is the layer directly above it: who ran the apply, when, and which state-affecting resources that run touched – an immutable, exportable log built to answer the compliance and incident-review questions a flat state file was never designed to handle.

The deferral behavior described above (an ephemeral resource with an unknown input waiting until apply, ordered against everything it depends_on) is a single-run instance of graph-aware execution: Terraform is already treating an ephemeral resource as a node in a dependency graph with its own ordering guarantees.

That same principle is the one Stategraph runs at a larger scale as a Terraform control plane, honoring those guarantees across a subgraph, including cases where independent branches run in parallel execution rather than one at a time.

Once a credential from an ephemeral resource lands in a write-only argument on a managed resource, Stategraph's blast-radius analysis picks up where the ephemeral resource's own lifecycle leaves off, showing you everything downstream of that resource before you touch it.

There is provider support for Vault, AWS, Azure, GCP, and Kubernetes, while a handful of others ship ephemeral resource types as of this writing, and plenty of providers don't yet.

Until yours does, the write-only argument still works, fed by a regular data source, but it only closes half of the Terraform security gap for that particular value.

Conclusion

Ephemeral resources are a narrow, well-scoped fix for a genuine issue: Terraform state and plan files holding secrets in plaintext because there was nowhere else for that data to live.

Adopt them wherever your providers support the resource types you need, pair them with write-only arguments to reach managed resources, and use write-only arguments alone as the bridge for the providers that haven't caught up yet.

None of that closes the governance question above it, though, the one about proving what happened to a secret during a run, not just where its value ended up. Stategraph does.

Try Stategraph free and see what a queryable audit trail looks like against your own state.

Terraform ephemeral resources FAQs

Do Terraform ephemeral resources work in OpenTofu?

Yes. OpenTofu added its own ephemeral block and write-only argument support starting with v1.11, following the same open, renew, close lifecycle and the same restricted set of valid reference contexts as Terraform.

Provider support tracks whichever fork's SDK a given provider targets, so check a specific provider's documentation before assuming full parity between the two.

Can you reference an ephemeral resource from inside a module?

Yes, within the module itself: pass the value in through a variable marked ephemeral = true, or fetch it directly inside a child module's own ephemeral block.

However, you can't return it from the root module as an output, since a root module's outputs get written to state; ephemeral outputs are only valid from a child module back to its caller.

What happens if you try to use an ephemeral value somewhere it isn't allowed?

Terraform throws immediately at plan time rather than letting the run proceed with a redacted or placeholder value: Error: Invalid use of ephemeral value, with a message explaining that resource instances have to persist between Terraform phases.

There's no silent fallback. The configuration simply won't plan until the value moves into a supported context: a write-only argument, a locals block, a provider block, or another ephemeral resource.