Safeguard
Cloud Security•

How to Secure Terraform State: Encryption, Access, Locking

Secure Terraform state with a remote encrypted backend, tight access, versioning, locking and no secrets in config. Includes an S3 backend example and OpenTofu notes.

Safeguard Security Team
5 min read

To secure Terraform state, store it in a remote backend with encryption at rest, restrict who can read it, enable versioning and locking, and keep secrets out of configuration. State can contain secret values in plain form, so treat the state file like a credential store.

Key takeaways

  • Terraform stores secret values in state and plan files, even when marked sensitive.
  • Use a remote backend, never commit state to Git.
  • Encrypt state, scope IAM access to the state key, and turn on bucket versioning.
  • Enable state locking to prevent concurrent writes.
  • Ephemeral values are omitted from state entirely, so prefer them when your providers support them.

Why is Terraform state sensitive?

State holds the attributes of every resource Terraform manages, and that can include passwords and keys. HashiCorp's documentation says: "If you add secret values directly to your configuration, Terraform stores those secrets in its state and plan files."

It also says that values marked with the sensitive argument are still stored in state and plan files. Only ephemeral values are omitted entirely.

What is the safest place to store state?

A remote backend with access controls and encryption. HashiCorp notes that with remote state, Terraform holds state in memory only while actively using it, and that backends can encrypt state at rest depending on the backend. HCP Terraform, for example, encrypts state at rest and protects it with TLS in transit.

For local development, HashiCorp advises treating the state file as sensitive and keeping it out of Git workflows. Add this to .gitignore:

*.tfstate
*.tfstate.*
.terraform/

How do you configure a secure S3 backend?

Enable encryption, a KMS key, and locking in the backend block.

terraform {
  backend "s3" {
    bucket       = "example-org-tfstate"
    key          = "prod/network/terraform.tfstate"
    region       = "us-east-1"
    encrypt      = true
    kms_key_id   = "arn:aws:kms:us-east-1:111122223333:key/example"
    use_lockfile = true
  }
}

The S3 backend documentation describes encrypt for server side encryption of state and lock files, kms_key_id for a customer managed key, and use_lockfile to enable locking in S3. DynamoDB locking with dynamodb_table is described as deprecated.

If you use kms_key_id, the role running Terraform needs kms:Encrypt, kms:Decrypt and kms:GenerateDataKey on that key.

Should you turn on bucket versioning?

Yes. HashiCorp strongly advises enabling bucket versioning on the S3 bucket so you can recover state after accidental deletion or human error.

aws s3api put-bucket-versioning \
  --bucket example-org-tfstate \
  --versioning-configuration Status=Enabled

Also apply S3 Block Public Access to the bucket. AWS recommends turning on all four settings.

What IAM permissions does Terraform need for state?

Only a few. For the default workspace, the S3 backend docs list s3:ListBucket on the bucket, s3:GetObject and s3:PutObject on the state key, and with use_lockfile also s3:GetObject, s3:PutObject and s3:DeleteObject on the .tflock file. They note s3:DeleteObject is not needed on the state file itself.

Scope each pipeline role to its own state key. A role that plans the dev network should not read production state.

How do you keep secrets out of state?

Avoid putting secrets in configuration at all, and read them at runtime.

  1. Prefer ephemeral values where your Terraform version and providers support them.
  2. Pass secrets by reference, such as a secrets manager ARN, instead of the secret value.
  3. Rotate any secret that has already landed in state, then check old state versions.
  4. Review which resources export secrets as attributes, since those end up in state.

Can you encrypt state on the client side?

OpenTofu can. Its documentation describes encrypting state and plan files at rest, with key providers including PBKDF2 passphrase, AWS KMS, GCP KMS, Azure Key Vault and OpenBao. It recommends a separate KMS key for each state file.

The same page warns that encryption does not protect against data loss, replay of an older state, or someone who can run tofu and see values. Back up unencrypted state and your keys before enabling it.

How do you detect risky state and Terraform code?

Run an infrastructure scanner in pull requests. Checkov lists Terraform, Terraform plan, CloudFormation and many other formats, and also detects AWS credentials in Terraform providers. Trivy scans filesystems for misconfigurations and secrets.

checkov --directory .
trivy fs --scanners vuln,secret,misconfig .

Add audit logging on the state bucket, since HashiCorp recommends audit logs to track state access.

Terraform state security checklist

  • Remote backend, no state in Git.
  • Encryption at rest with a managed key.
  • Versioning enabled and tested with a restore drill.
  • Locking enabled.
  • Block Public Access on the bucket.
  • One state key per environment and role scoped to it.
  • Audit logging on reads.
  • No literal secrets in configuration.
  • IaC scanner on every pull request.

Frequently asked questions

Does the sensitive flag hide values from state?

No. It hides them from CLI output. Terraform still stores them in state and plan files.

Do I still need DynamoDB for locking?

The S3 backend supports use_lockfile. The docs describe DynamoDB locking as deprecated, and both can be set together during migration.

Is it safe to store state in Git?

No. State can contain secrets, and Git history is hard to clean.

What if a secret already leaked into state?

Rotate it first, then restrict and clean old state versions.

Can I check my dependencies for known CVEs too?

Yes, with the free scan at gold.safeguard.sh/scan. It runs in your browser and your file stays on your machine.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.