Terraform Secrets Management
This guide covers secure handling of sensitive data in Terraform configurations.
Security Approach
The configuration uses a hybrid approach that separates sensitive and non-sensitive data across different files.
File-Based Separation
Non-Sensitive Configuration
The terraform.tfvars file contains safe-to-commit values:
# terraform.tfvars (committed to Git)
server_base_name = "server"
server_type = "cx33"
server_image = "debian-13"
location = "nbg1"
environment = "development"
project = "hello-world"Sensitive Data
The .env file contains secrets and is ignored by Git:
# .env (ignored by Git)
export HCLOUD_TOKEN="API_TOKEN"
export TF_VAR_ssh_public_key="ADD_YOUR_SSH_KEY"
export TF_VAR_ssh_public_key_secondary=""Environment Variables
Terraform automatically reads environment variables with the TF_VAR_ prefix:
# Maps to variable "ssh_public_key"
export TF_VAR_ssh_public_key="ssh-ed25519 ..."
# Maps to variable "ssh_public_key_secondary"
export TF_VAR_ssh_public_key_secondary="ssh-rsa ..."Provider credentials use their own environment variables:
# Hetzner Cloud provider reads this automatically
export HCLOUD_TOKEN="API_TOKEN"Variable Precedence
Terraform follows this order when resolving variable values:
- Command line flags:
-var="key=value" - Variable files:
-var-file="terraform.tfvars" terraform.tfvarsfile (if present)- Environment variables:
TF_VAR_name - Variable defaults in
variables.tf
Git Configuration
The .gitignore file prevents committing sensitive data:
# Terraform sensitive files
*.tfvars
!*.tfvars.example
!terraform.tfvars
.env
.env.local
*.tfstate
*.tfstate.*
.terraform/This configuration:
- Ignores all
.tfvarsfiles by default - Allows
terraform.tfvars(contains only non-sensitive data) - Allows example files for reference
- Ignores all environment files
Environment File Template
The .env.example file serves as a template:
# Environment variables for Terraform (SENSITIVE VALUES ONLY)
export HCLOUD_TOKEN="API_TOKEN"
export TF_VAR_ssh_public_key="ssh-ed25519 ..."
export TF_VAR_ssh_public_key_secondary=""Variable Definitions
Variables requiring sensitive input have no defaults in variables.tf:
variable "ssh_public_key" {
description = "Primary SSH public key for server access"
type = string
# No default - must be provided via environment variable
}Alternative Approaches
SOPS Encryption
For encrypted secrets in version control:
brew install sops age
age-keygen -o ~/.config/sops/age/keys.txt
sops -e -i terraform.tfvarsHashiCorp Vault
For centralised secret management:
data "vault_generic_secret" "hcloud" {
path = "secret/hetzner"
}
provider "hcloud" {
token = data.vault_generic_secret.hcloud.data["api_token"]
}Validation
Before committing changes, verify no secrets are exposed:
# Check Git status
git status
# Verify .env is ignored
git check-ignore .env
# Search for potential secrets in staged files
git diff --cached | grep -i "token\|key\|password"Recovery Procedures
If secrets are accidentally committed:
- Immediately revoke the exposed credentials in Hetzner Cloud console
- Generate new API tokens and SSH keys
- Remove secrets from Git history using
git filter-branch - Force push to all remote repositories
- Update all systems and team members with new credentials
- Review access logs for any unauthorized usage