OpenTofu
Configure OpenTofu's built-in state encryption to protect sensitive values at rest. AES-GCM with PBKDF2 or AWS KMS / GCP KMS / Azure Key Vault key providers.
LLuca Berton2 min read
On this page
- Why state encryption matters
- Enabling encryption (PBKDF2 passphrase)
- AWS KMS key provider
- GCP KMS key provider
- Azure Key Vault key provider
- Key rotation
- CI/CD integration
- Migrating from Terraform unencrypted state
- Limitations
- Related reading
- Conclusion
State encryption is OpenTofu's flagship feature and the single biggest reason teams migrate from Terraform. It encrypts state files and plan files end-to-end — at rest in the backend, in transit, and on disk in CI runners. HashiCorp Terraform does not offer this; secrets in state are stored in plaintext.
This guide walks through enabling state encryption with both passphrase and cloud-KMS key providers.
Why state encryption matters
Terraform state files contain every resource attribute — including database passwords, API keys, certificates, and IAM credentials. Anyone who reads the backend (S3 bucket, GCS bucket, Azure blob) sees them in plaintext. Bucket policies and IAM are necessary but insufficient defence in depth.
OpenTofu encrypts the entire state JSON before it's written and decrypts it
on read. Without the key, a stolen terraform.tfstate is useless.
Enabling encryption (PBKDF2 passphrase)
The simplest method is a passphrase derived through PBKDF2. Add an
encryption block to the top-level terraform { ... } block:
terraform {
encryption {
key_provider "pbkdf2" "passphrase" {
passphrase = var.tofu_state_passphrase
}
method "aes_gcm" "default" {
keys = key_provider.pbkdf2.passphrase
}
state {
method = method.aes_gcm.default
enforced = true
}
plan {
method = method.aes_gcm.default
enforced = true
}
}
}
variable "tofu_state_passphrase" {
type = string
sensitive = true
}Set the variable via environment:
export TF_VAR_tofu_state_passphrase="$(pass show ops/tofu-state)"
tofu init
tofu applyInspect the encrypted state:
cat terraform.tfstate | head -c 200
# {"key_provider_meta":{"passphrase":{...}},"encrypted_data":"..."You can no longer cat secrets out of the file.
AWS KMS key provider
For team workflows, store the encryption key in AWS KMS so access is gated by IAM:
terraform {
encryption {
key_provider "aws_kms" "team" {
kms_key_id = "arn:aws:kms:eu-west-1:123456789012:key/abcd-1234"
region = "eu-west-1"
key_spec = "AES_256"
}
method "aes_gcm" "kms" {
keys = key_provider.aws_kms.team
}
state { method = method.aes_gcm.kms enforced = true }
plan { method = method.aes_gcm.kms enforced = true }
}
}Now anyone running tofu plan must have kms:Decrypt on the team key. CI
runners get scoped IAM roles; engineers use SSO. Auditing is a single
CloudTrail query.
GCP KMS key provider
key_provider "gcp_kms" "team" {
kms_encryption_key = "projects/my-proj/locations/global/keyRings/tofu/cryptoKeys/state"
key_length = 32
}Azure Key Vault key provider
key_provider "azure_kv" "team" {
vault_name = "tofu-state-kv"
key_name = "state-encryption"
}Key rotation
Rotate by adding a new method and falling back to the old one for decryption:
method "aes_gcm" "v2" { keys = key_provider.aws_kms.team_v2 }
method "aes_gcm" "v1" { keys = key_provider.aws_kms.team_v1 }
state {
method = method.aes_gcm.v2 # encrypt with v2
enforced = true
fallback {
method = method.aes_gcm.v1 # decrypt v1 written before rotation
}
}Run tofu apply -auto-approve -refresh-only to re-encrypt the state with v2,
then drop the fallback at the next apply.
CI/CD integration
In GitHub Actions:
- name: OpenTofu init
env:
AWS_REGION: eu-west-1
run: |
tofu init
tofu plan -out=tfplanThe plan file is also encrypted, so artefact storage between jobs is safe.
Migrating from Terraform unencrypted state
OpenTofu refuses to encrypt an existing plaintext state automatically — it needs your explicit consent:
tofu init -migrate-state
# OpenTofu detected unencrypted state. Encrypt now? [yes/no]
yesAlways back up the plaintext state file before this step.
Limitations
- Encryption is OpenTofu-only. Migrating back to Terraform requires
decryption first (
tofu apply -refresh-onlywithenforced = false). - The HashiCorp
terraformbinary cannot read encrypted state. tofu showandtofu state liststill work; they decrypt in memory.
- Terraform vs OpenTofu in 2026
- How to Migrate from Terraform to OpenTofu
- Terraform sensitive data management
- Terraform state explained
Conclusion
State encryption closes the single biggest security gap in the Terraform workflow. With cloud-KMS providers and IAM-scoped access, you get a defensible, auditable secret-at-rest story for every project — without external tools like SOPS or a custom backend. It's the killer feature that makes OpenTofu worth evaluating, even for teams happy with Terraform.
#OpenTofu#State#Encryption#Security#KMS
OpenTofu
How to Install OpenTofu on macOS, Linux, and Windows
Step-by-step guide to install OpenTofu on macOS (Homebrew), Linux (apt, dnf, rpm), and Windows (Chocolatey, Scoop). Includes verification, version pinning...
2 min read
OpenTofu
OpenTofu Early Variable Evaluation: Dynamic Backends
OpenTofu's early evaluation lets you use variables and locals in backend configuration, module sources, and required_providers — features Terraform doesn't...
3 min read
OpenTofu
OpenTofu Provider Registry: Sources, Mirrors, and Caching
How OpenTofu resolves providers from registry.opentofu.org, configures the registry, sets up filesystem mirrors for air-gapped environments, and caches...
3 min read
DevOps
Terraform for Digital Provenance: C2PA Content Signing
Provision digital provenance and C2PA content signing infrastructure with Terraform: certificate authorities, signing services, ledgers, and verification APIs.
2 min read
Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.