Terraform path · State · Topic 7 of 14

Secrets and state

Not started

Terraform stores every attribute a provider returns in state, and saved plan files contain the values needed to apply them. For a database password, an API token or a private key, that means a plain-text copy in every state file and plan artefact. sensitive = true does not change this; it only keeps the value out of terminal output and logs. Handling secrets well means deciding, for each one, whether it should pass through Terraform at all.

Why it matters: Terraform often handles passwords, keys and tokens. Unless you design for it, every one of them ends up in plain text in state and in saved plans, readable by anyone with access to the bucket.

  1. 1Sensitive only hides output

    sensitive = true redacts a value in plan and apply output. It is still stored in state and plan files in plain text.

  2. 2Treat state as a secret

    Encrypt the backend, restrict who can read it, and never commit it. Read access to state is read access to its secrets.

  3. 3Ephemeral values stay out of state

    Ephemeral resources (1.10+) and write-only arguments (1.11+) let Terraform use a secret without persisting it in state or plans.

  4. 4Prefer references to values

    Often the best answer is to let the service generate and store the secret (for example a managed master password) and pass only its ARN.

1

How it works

The explanation, with examples you can run.

What "sensitive" does and does not do

From the local demo (Terraform 1.5.7):

resource "random_password" "db" {  length  = 20  special = false} output "db_password" {  value     = random_password.db.result  sensitive = true}
$ terraform outputdb_password = <sensitive>file = "./out/dev-known-woodcock.txt" $ terraform output -raw db_password | wc -c      20

And in terraform.tfstate, the result attribute of random_password.db held the full 20-character password in plain text. The provider marks that attribute sensitive in its schema, so plans redact it, but redaction is about display only.

Protecting state

  • Use a remote backend with encryption at rest (S3 with KMS, for example), versioning, and IAM that limits readers to the roles that run Terraform.
  • Never commit *.tfstate or plan files; add them to .gitignore. Treat CI artefacts containing plans as secrets and expire them quickly.
  • OpenTofu can encrypt state and plan files client-side (since OpenTofu 1.7), so the storage backend never sees plain text. Terraform relies on backend-side encryption.

Keeping secrets out of state

Recent Terraform versions add two features for this:

  • Ephemeral resources (Terraform 1.10+) are read or generated during a run and never persisted: for example, fetching a secret from a secrets manager or generating a password just for this run.
  • Write-only arguments (Terraform 1.11+), on resources whose providers support them, accept ephemeral values and are not stored in plan or state. They usually come with a version argument you bump to trigger rotation.
ephemeral "random_password" "db" {  length = 24} resource "aws_db_instance" "main" {  # ...  password_wo         = ephemeral.random_password.db.result  password_wo_version = 1}

Provider support varies; check the resource documentation for _wo arguments.

Often better: do not handle the secret

Many services can own their secrets. RDS can generate and rotate the master password in Secrets Manager (manage_master_user_password = true), so Terraform only ever sees an ARN. Applications then read the secret at runtime with their own IAM role. The secret never appears in Terraform, its state, its plans or its CI logs, and rotation does not need a Terraform run.

Finally, keep secrets out of variable defaults and .tfvars files in Git, and avoid local-exec provisioners that echo them into logs.

2

Check yourself

3 questions. Get them all right to finish this topic.

  1. 1.You create a random_password and mark the output sensitive. Where can the password be read?
  2. 2.Which feature lets a resource receive a password that Terraform never writes to state?
  3. 3.What is a safe place for a database password a Terraform configuration needs to pass to a service?

0 of 3 right so far. Get all three to finish this topic.