TFLAB32-Sensitive Variables: Protecting Secrets in Terraform

Sensitive Variables: Protecting Secrets in Terraform

🔧 Terraform Core ⭐ Intermediate sensitive = true TF_VAR_* Sensitive Outputs State File Security

Scenario

Your configuration needs a database password and an API key. You must ensure these values are never shown in CLI output or plan logs. You also need to understand that sensitive = true masks values in output but does NOT encrypt the state file.

Your Objectives
  • Declare a variable with sensitive = true.
  • Pass the sensitive value via TF_VAR_ environment variable.
  • Create a sensitive output.
  • Observe that the value is masked as (sensitive value).
  • Understand the security limitation: values are still in the state file in plain text.

Additional Context

sensitive = true on a variable prevents Terraform from showing the value in CLI plan/apply output. It also prevents the value from being used in resource names (Terraform errors if you try). If you mark a variable sensitive, any output that references it must also be marked sensitive.

Best practices: Never store secrets in .tfvars committed to Git. Use environment variables, a secrets manager (AWS Secrets Manager, HashiCorp Vault), or HCP Terraform's encrypted variable storage.

variables.tf

variable "db_password" {
  description = "Database password — will be masked in all output."
  type        = string
  sensitive   = true     # ← VALUE IS MASKED IN CLI OUTPUT
}

variable "api_key" {
  description = "External API key."
  type        = string
  sensitive   = true
  default     = "default-key-for-dev"
}

variable "aws_region" { type = string; default = "us-east-1" }

main.tf

terraform {
  required_version = ">= 1.5.0"
  required_providers {
    aws = { source = "hashicorp/aws", version = "~> 5.0" }
  }
}
provider "aws" { region = var.aws_region }

# Store the secret in SSM Parameter Store (SecureString type)
resource "aws_ssm_parameter" "db_password" {
  name  = "/myapp/db-password"
  type  = "SecureString"       # encrypted at rest by AWS
  value = var.db_password       # this value is sensitive
  tags  = { ManagedBy = "terraform" }
}

resource "aws_ssm_parameter" "api_key" {
  name  = "/myapp/api-key"
  type  = "SecureString"
  value = var.api_key
  tags  = { ManagedBy = "terraform" }
}

outputs.tf

# Outputs that reference sensitive values MUST also be sensitive
output "db_param_name" { value = aws_ssm_parameter.db_password.name }
output "db_param_arn"  { value = aws_ssm_parameter.db_password.arn; sensitive = true }
output "api_key_name"  { value = aws_ssm_parameter.api_key.name }

Workflow Commands

# Pass sensitive values via environment variables (NEVER commit to Git)
export TF_VAR_db_password="SuperSecret123!"

terraform init && terraform apply
# Plan shows: db_password = (sensitive value)
# The actual value is NEVER printed

# Force view the sensitive output
terraform output -raw db_param_arn

# WARNING: State file contains secrets in plain text
terraform state pull | grep -i "SecureString"

unset TF_VAR_db_password
terraform destroy
✓ Sensitive Value in Plan
  + resource "aws_ssm_parameter" "db_password" {
      + name  = "/myapp/db-password"
      + type  = "SecureString"
      + value = (sensitive value)    ← masked!
    }