Does sensitive = true Fully Protect Your Data in Terraform?
Does sensitive = true Fully Protect Your Data in Terraform?
Marking a Terraform variable or output as sensitive = true is a useful safety measure, but it is widely misunderstood. It does not provide complete protection for your secrets. Here is exactly what it does and does not do.
What sensitive = true Does
variable "db_password" {
type = string
sensitive = true
}
output "db_connection_string" {
value = "postgres://admin:${var.db_password}@${aws_db_instance.main.endpoint}/app"
sensitive = true
}
When marked sensitive, Terraform will:
- Mask the value in
terraform planandterraform applyoutput as(sensitive value). - Suppress the value in
terraform outputunless you use-jsonor-raw. - Prevent the value from appearing in regular log output.
# Plan output with sensitive = true
+ password = (sensitive value) # Value is hidden in terminal
What sensitive = true Does NOT Do
This is the critical part most engineers miss:
- It does NOT encrypt the value in the state file.
- It does NOT prevent the value from being read with
terraform state pull. - It does NOT remove the value from the
.tfstatefile on disk.
# The password is stored in CLEARTEXT inside terraform.tfstate
{
"resources": [
{
"instances": [
{
"attributes": {
"password": "SuperSecretPassword123!" # Fully visible in state file!
}
}
]
}
]
}
How to Truly Secure Sensitive Data
1. Use a Remote Backend with Encryption at Rest
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
encrypt = true # Server-side encryption
kms_key_id = "arn:aws:kms:us-east-1:123456789:key/abc-123" # KMS key
dynamodb_table = "terraform-lock"
}
}
2. Restrict Access to the State File
# S3 bucket policy — restrict to only Terraform IAM role
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-terraform-state/*",
"Condition": {
"StringNotEquals": {
"aws:PrincipalArn": "arn:aws:iam::123456789:role/terraform-role"
}
}
}
3. Avoid Storing Secrets in State at All
# Use a data source to fetch secrets at apply time instead of storing them
data "aws_secretsmanager_secret_version" "db_pass" {
secret_id = "prod/db/password"
}
Key Takeaway
sensitive = true only masks values in CLI output and logs — it is a display-layer protection, not encryption. The secret is still stored as cleartext in your state file. For true security, use a remote backend with KMS encryption, restrict state file access via IAM policies, and source secrets from a secret manager at apply time.