Terraform Secrets & Sensitive Variables Prompt
Manage secrets in Terraform — sensitive flag, ephemeral resources, external secret managers, plan/state masking.
- Target user
- Terraform engineers handling production secrets
- Difficulty
- Intermediate
- Tools
- Claude, ChatGPT
The prompt
You are a senior platform engineer who has handled secrets in Terraform — sensitive variables, external lookups, ephemeral resources (1.10+).
I will provide:
- The secret type
- Current handling
- Concerns
Your job:
1. **Sensitive flag**:
- `sensitive = true` on variable / output
- Masked in plan/apply output
- Still in state (encrypted at rest)
2. **For ephemeral resources** (1.10+):
- Not stored in state
- Re-read on each plan
- Good for short-lived secrets
3. **For external secret managers**:
- **AWS Secrets Manager**: `data "aws_secretsmanager_secret"`
- **HashiCorp Vault**: vault provider
- **GCP Secret Manager**: `data "google_secret_manager_secret_version"`
- **Azure Key Vault**: `data "azurerm_key_vault_secret"`
- Fetch at apply time
4. **For state-stored secrets**:
- Encrypt state at rest (KMS)
- Restrict access
- Audit
5. **For provider auth**:
- Don't hardcode in .tf
- Env vars / OIDC / instance role
6. **For Terraform Cloud variables**:
- Mark sensitive in UI
- Encrypted at rest
7. **For sensitive in outputs**:
- `output "secret" { sensitive = true value = ... }`
- Required for downstream consumers
8. **For preventing leak**:
- No sensitive vars in logs / errors
- Mask in CI output
- Don't commit tfvars with secrets
Mark DESTRUCTIVE: secrets in committed tfvars, sensitive output passed to non-sensitive output, plan output captured in CI logs containing secrets.
---
Secret type: [DESCRIBE]
Current handling: [DESCRIBE]
Concerns: [DESCRIBE]
Why this prompt works
Secrets in Terraform need care. This prompt walks patterns.
How to use it
- Mark sensitive.
- External secrets for production.
- Encrypt state.
- No secrets in code.
Patterns
Sensitive variable + output
variable "db_password" {
description = "Database master password"
type = string
sensitive = true
nullable = false
validation {
condition = length(var.db_password) >= 16
error_message = "db_password must be at least 16 chars."
}
}
output "db_connection_string" {
value = "postgres://admin:${var.db_password}@${aws_db_instance.main.endpoint}/myapp"
sensitive = true # required because depends on sensitive
}
In plan output:
db_password = (sensitive value)
db_connection_string = (sensitive value)
External secret (AWS Secrets Manager)
data "aws_secretsmanager_secret" "db" {
name = "prod/db/master-password"
}
data "aws_secretsmanager_secret_version" "db" {
secret_id = data.aws_secretsmanager_secret.db.id
}
resource "aws_db_instance" "main" {
identifier = "myapp-prod"
engine = "postgres"
instance_class = "db.t3.medium"
allocated_storage = 100
username = "admin"
password = jsondecode(data.aws_secretsmanager_secret_version.db.secret_string)["password"]
storage_encrypted = true
kms_key_id = aws_kms_key.rds.arn
}
HashiCorp Vault
provider "vault" {
address = "https://vault.example.com"
# Auth via VAULT_TOKEN env var
}
data "vault_kv_secret_v2" "db" {
mount = "kv"
name = "prod/db"
}
resource "aws_db_instance" "main" {
password = data.vault_kv_secret_v2.db.data["password"]
}
Ephemeral resource (1.10+)
ephemeral "aws_secretsmanager_secret_version" "db" {
secret_id = "prod/db/master-password"
}
# Use in same plan; not stored in state
resource "aws_db_instance" "main" {
password = ephemeral.aws_secretsmanager_secret_version.db.secret_string
}
Generated random password (stored in state)
resource "random_password" "db" {
length = 32
special = true
upper = true
lower = true
numeric = true
lifecycle {
ignore_changes = [length, special, upper, lower, numeric]
}
}
resource "aws_secretsmanager_secret_version" "db" {
secret_id = aws_secretsmanager_secret.db.id
secret_string = jsonencode({
username = "admin"
password = random_password.db.result
})
}
resource "aws_db_instance" "main" {
password = random_password.db.result
}
Auth via env (no hardcoded keys)
provider "aws" {
region = "us-east-1"
# Credentials via AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY env
# OR shared profile (AWS_PROFILE)
# OR IAM instance role (no env needed)
# OR OIDC from CI
}
Terraform Cloud sensitive variables
TFC UI → Workspace → Variables:
- Name: db_password
- Value: (encrypted)
- Sensitive: true (masks UI + plan logs)
- Description: Database password
# In Terraform code, TFC injects as TF_VAR_db_password
variable "db_password" {
sensitive = true
}
CI/CD masking
# GitLab CI: mark variable as Masked + Protected
# Settings → CI/CD → Variables:
# - TF_VAR_db_password
# - Masked: true
# - Protected: true
# - Environment: production
terraform-apply:
variables:
TF_VAR_db_password: $DB_PASSWORD # masked in CI logs
script:
- terraform apply -auto-approve 2>&1 | sed "s/$DB_PASSWORD/******/g"
Common findings this catches
- Secret in committed .tfvars → rotate, remove, vault.
- Sensitive var without flag → outputs show value.
- Output without sensitive flag depends on sensitive → error.
- Provider creds hardcoded → env / OIDC.
- State file unencrypted → KMS.
- CI logs containing plan output → mask + restrict.
- Random_password regenerates on every apply → ignore_changes.
When to escalate
- Compromised secret → rotate; IR.
- Secret manager migration → staged.
- Cross-org secret policy — security.
Related prompts
-
Ansible Vault Secrets Management Prompt
Use Ansible Vault — encrypt secrets, vault IDs, multi-vault setups, integration with external secret managers.
-
Terraform Cloud Provider Authentication Best Practices Prompt
Authenticate Terraform to cloud providers safely — AWS IAM roles, GCP service accounts, Azure managed identity, OIDC from CI.
-
Terraform Variable Validation Prompt
Add Terraform variable validation — types, validation blocks, sensitive, nullable, custom error messages.