Terraform Sensitive Variables and Secrets Management

Infrastructure code manages credentials, connection strings, API keys, and passwords every day. Handling these secrets poorly — pasting them into code, committing them to Git, or printing them in logs — creates serious security risks. This topic covers Terraform's built-in sensitive value handling and the strategies professionals use to keep secrets safe.

The Core Problem

Terraform needs certain secret values to create and configure infrastructure — an RDS master password, an API key for a third-party service, an SSH private key. These values must reach Terraform somehow. The question is: how do you get them there without exposing them?

Marking Variables as Sensitive

Add sensitive = true to any variable declaration to tell Terraform to redact the value from all output.

variable "db_password" {
  description = "Master password for the RDS database"
  type        = string
  sensitive   = true
}

resource "aws_db_instance" "main" {
  identifier     = "main-db"
  engine         = "postgres"
  instance_class = "db.t3.medium"
  password       = var.db_password
}

With sensitive = true, Terraform replaces the value with (sensitive value) everywhere it would normally appear:

# In terraform plan output:
  + password = (sensitive value)

# In terraform apply output:
Changes to Outputs:
  + db_connection = (sensitive value)

The value is still stored in the state file. Sensitive marking only hides values from terminal output — it does not encrypt them in state.

Marking Outputs as Sensitive

When a resource attribute contains a secret and you expose it as an output, mark that output sensitive too.

output "db_connection_string" {
  description = "Full database connection string"
  value       = "postgres://${var.db_user}:${var.db_password}@${aws_db_instance.main.endpoint}/appdb"
  sensitive   = true
}

Terraform automatically propagates sensitivity — if you use a sensitive variable inside any expression, the result of that expression also becomes sensitive. This propagation means you often do not need to manually mark outputs as sensitive; Terraform does it for you.

How to Provide Sensitive Values Safely

Never put secret values in terraform.tfvars files committed to Git. Use these safe methods instead:

Method 1: Environment Variables

export TF_VAR_db_password="super-secret-password-123"
terraform apply

Environment variables work well in CI/CD pipelines. Most platforms (GitHub Actions, GitLab CI, CircleCI) provide encrypted secret storage that injects values as environment variables at runtime.

Method 2: Interactive Prompt

Declare a variable with no default and mark it sensitive. Terraform prompts for the value at runtime:

variable "db_password" {
  type      = string
  sensitive = true
  # No default — Terraform will prompt
}
$ terraform apply
var.db_password
  Enter a value: (input hidden)

The terminal hides the input as you type.

Method 3: AWS Secrets Manager or HashiCorp Vault

Fetch secrets from a dedicated secret store at apply time using a data source. The secret never appears in code or variable files.

# Fetch a secret from AWS Secrets Manager
data "aws_secretsmanager_secret_version" "db_password" {
  secret_id = "prod/myapp/db_password"
}

resource "aws_db_instance" "main" {
  identifier     = "main-db"
  engine         = "postgres"
  instance_class = "db.t3.medium"
  password       = jsondecode(data.aws_secretsmanager_secret_version.db_password.secret_string)["password"]
}

Diagram: Secret Manager Flow

AWS Secrets Manager
  "prod/myapp/db_password": { "password": "s3cr3t" }
        |
        | data source reads at plan time
        v
Terraform configuration uses the secret value
        |
        v
RDS instance created with correct password
        |
Secret value never appears in:
  - .tf files
  - .tfvars files
  - Git history
  - Terminal output (sensitive = true propagates)

Protecting the State File

Sensitive values are stored in the Terraform state file — even values marked as sensitive. Anyone who can read the state file can read the secrets. Protect state files with:

  • S3 server-side encryption: Enable AES-256 or KMS encryption on the state bucket
  • Strict IAM policies: Only specific roles and users can read the state bucket — not the whole organisation
  • Versioning: Enable S3 versioning to recover from accidental corruption or overwrites
  • Access logging: Enable S3 access logs to track who reads or writes the state bucket

What Never to Do

  • Never commit terraform.tfvars files containing passwords to version control
  • Never hard-code secrets directly in .tf files
  • Never pass secrets as command-line flags (-var="password=secret") — shell history stores them
  • Never print secrets in output blocks unless they are marked sensitive

Key Points

  • Mark variables and outputs as sensitive = true to redact them from terminal and plan output.
  • Terraform propagates sensitivity automatically — expressions using sensitive inputs also become sensitive.
  • Provide secrets through environment variables, interactive prompts, or secret manager data sources — never in committed files.
  • State files store sensitive values in plaintext — encrypt the state backend and restrict access strictly.
  • AWS Secrets Manager, Azure Key Vault, and HashiCorp Vault are the recommended stores for secrets used by Terraform.

Leave a Comment