Environment & Secret #
Terraform sering perlu tahu tentang hal-hal yang sensitif: password database, API key untuk provider pihak ketiga, private certificate. Masalahnya adalah Terraform menyimpan semua yang dikelolanya di state file — termasuk nilai-nilai sensitif. Dan state file bisa terbaca oleh siapa saja yang punya akses ke backend. Mengelola secret di Terraform dengan aman berarti memahami apa yang masuk ke state, apa yang tidak, dan bagaimana merancang konfigurasi agar secret tidak lebih banyak tersebar dari yang seharusnya.
flowchart LR
A["Password\nAPI Key\nCertificate"] --> B["Terraform"]
B --> C["State File\n(plaintext!)"]
B --> D["Cloud Resources\n(encrypted)"]
style A fill:#f59e0b,stroke:#d97706,color:#fff
style B fill:#8b5cf6,stroke:#6d28d9,color:#fff
style C fill:#ef4444,stroke:#dc2626,color:#fff
style D fill:#10b981,stroke:#059669,color:#fffApa yang Masuk ke State #
Salah satu kesalahpahaman umum adalah mengira bahwa sensitive = true pada variable atau output menyembunyikan nilai dari state. Ini tidak benar.
APA YANG MASUK KE STATE TERRAFORM:
resource "aws_db_instance" "main" {
identifier = "production-db"
password = var.db_password ← PASSWORD INI MASUK KE STATE
}
Setelah terraform apply:
terraform.tfstate berisi:
{
"resources": [{
"type": "aws_db_instance",
"instances": [{
"attributes": {
"password": "MyS3cr3tP4ssword" ← PLAINTEXT di state
}
}]
}]
}
sensitive = true HANYA menyembunyikan dari:
- Output terminal (terraform output, terraform plan)
- Terraform plan display
sensitive = true TIDAK menyembunyikan dari:
- State file (tetap plaintext)
- terraform output -raw <nama_output>
- Siapapun yang punya akses ke state backend
Jangan Generate Secret di Terraform Jika Bisa Dihindari #
Cara paling aman mengelola secret adalah dengan tidak membuat secret di Terraform sama sekali. Biarkan secret dibuat dan dirotasi di luar Terraform, lalu Terraform hanya membaca referensinya.
# ANTI-PATTERN: Generate password di Terraform
resource "random_password" "db" {
length = 32
special = true
}
resource "aws_db_instance" "main" {
password = random_password.db.result # Password masuk ke state
}
# BENAR: Password dibuat di luar Terraform (manual atau via secrets manager)
# Terraform hanya membaca ARN-nya, bukan nilainya
# Buat secret di AWS Secrets Manager (sekali, di luar Terraform)
# aws secretsmanager create-secret --name prod/db/password --secret-string "..."
# Terraform hanya referensikan ARN secret, bukan nilainya
data "aws_secretsmanager_secret" "db_password" {
name = "prod/db/password"
}
resource "aws_db_instance" "main" {
# Gunakan referensi ARN ke Secrets Manager, bukan nilai password
manage_master_user_password = true
# Atau: gunakan master_user_secret yang di-manage AWS
}
Membaca Secret dari AWS Secrets Manager #
Saat Terraform memang perlu menggunakan nilai secret (misalnya untuk mengkonfigurasi resource yang butuh password), baca dari Secrets Manager sebagai data source.
# Baca secret dari Secrets Manager
data "aws_secretsmanager_secret_version" "db_password" {
secret_id = "prod/db/master-password"
}
# Gunakan nilai secret
resource "aws_db_instance" "main" {
identifier = "production-db"
engine = "postgres"
instance_class = "db.t3.medium"
# Nilai ini masuk ke state — ini trade-off yang perlu disadari
# Pastikan state di-enkripsi dan akses ke state sangat terbatas
username = "admin"
password = data.aws_secretsmanager_secret_version.db_password.secret_string
# Lebih baik: gunakan manage_master_user_password yang di-manage AWS
# sehingga password tidak pernah muncul di state sama sekali
}
# Cara terbaik untuk RDS: biarkan AWS mengelola password
resource "aws_db_instance" "main_v2" {
identifier = "production-db"
engine = "postgres"
instance_class = "db.t3.medium"
username = "admin"
manage_master_user_password = true
# AWS buat password, simpan di Secrets Manager
# Password TIDAK masuk ke Terraform state
# Rotasi bisa dikonfigurasi via master_user_secret_rotation
}
AWS SSM Parameter Store untuk Konfigurasi #
SSM Parameter Store cocok untuk konfigurasi yang bukan secret tapi perlu diakses oleh banyak layanan — endpoint URL, feature flag, konfigurasi environment.
# Simpan konfigurasi di SSM Parameter Store
resource "aws_ssm_parameter" "db_endpoint" {
name = "/prod/database/endpoint"
type = "String" # Bukan secret — cukup String
value = aws_db_instance.main.endpoint
}
resource "aws_ssm_parameter" "api_key" {
name = "/prod/external/api-key"
type = "SecureString" # Secret — dienkripsi dengan KMS
value = var.external_api_key
# Nilai masih masuk ke state — tapi dienkripsi di SSM
}
# Baca parameter dari SSM (di konfigurasi lain atau di aplikasi)
data "aws_ssm_parameter" "db_endpoint" {
name = "/prod/database/endpoint"
}
output "db_endpoint" {
value = data.aws_ssm_parameter.db_endpoint.value
}
Mengoper Secret via Environment Variable #
Secret yang dibutuhkan saat runtime (bukan saat provisioning) sebaiknya dioper via environment variable, bukan dikonfigurasi di Terraform.
# ANTI-PATTERN: Hardcode secret di resource
resource "aws_ecs_task_definition" "app" {
container_definitions = jsonencode([{
name = "app"
image = "myapp:latest"
environment = [
{
name = "DB_PASSWORD"
value = "MyP4ssword" # ✗ Masuk ke state sebagai plaintext
}
]
}])
}
# BENAR: Referensikan secret dari Secrets Manager, bukan nilai
resource "aws_ecs_task_definition" "app" {
container_definitions = jsonencode([{
name = "app"
image = "myapp:latest"
# Jangan gunakan 'environment' untuk secret
# Gunakan 'secrets' yang membaca dari Secrets Manager saat runtime
secrets = [
{
name = "DB_PASSWORD"
valueFrom = "arn:aws:secretsmanager:ap-southeast-1:123456789:secret:prod/db/password"
# ECS agent membaca nilai ini saat container start
# Nilai tidak pernah masuk ke Terraform state
}
]
}])
}
Mengamankan State yang Berisi Secret #
Jika state sudah terlanjur berisi secret (ini situasi yang umum untuk banyak konfigurasi yang sudah ada), pastikan akses ke state sangat terbatas.
# Backend S3 dengan enkripsi dan akses ketat
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "production/terraform.tfstate"
region = "ap-southeast-1"
# Enkripsi wajib
encrypt = true
kms_key_id = "arn:aws:kms:ap-southeast-1:123456789:key/abc-123"
# Akses via DynamoDB lock
dynamodb_table = "terraform-state-lock"
}
}
# IAM Policy untuk S3 state bucket:
# - Hanya TerraformApplyRole yang bisa read/write state
# - Tim engineering hanya bisa list bucket, tidak bisa baca state
# - Audit log semua akses via CloudTrail
flowchart TD
A["Secret in config"] --> B{"Method?"}
B -->|"In state"| C["Backend encryption"]
B -->|"Reference only"| D["Secrets Manager"]
D --> E["data source read"]
E --> F["Not in state\n✅"]
style A fill:#f59e0b,stroke:#d97706,color:#fff
style B fill:#f97316,stroke:#ea580c,color:#fff
style C fill:#ef4444,stroke:#dc2626,color:#fff
style D fill:#3b82f6,stroke:#1e40af,color:#fff
style E fill:#8b5cf6,stroke:#6d28d9,color:#fff
style F fill:#10b981,stroke:#059669,color:#fffEncrypt-at-Rest untuk Secret di State #
Secret di state file harus ter-encrypt at rest. Pastikan backend configuration mengaktifkan enkripsi.
# S3 backend dengan KMS encryption
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "production/terraform.tfstate"
region = "ap-southeast-1"
encrypt = true
kms_key_id = aws_kms_key.terraform_state.arn
dynamodb_table = "terraform-state-lock"
}
}
resource "aws_kms_key" "terraform_state" {
description = "KMS key for Terraform state"
deletion_window_in_days = 30
enable_key_rotation = true
}
# Verifikasi enkripsi aktif
aws s3api get-bucket-encryption --bucket my-terraform-state
# Harus mengembalikan SSEAlgorithm: aws:kms
# Verifikasi KMS key rotation aktif
aws kms get-key-rotation-status --key-id $KEY_ID
# Harus mengembalikan true
Secret Rotation Strategy #
Secret harus di-rotate secara berkala untuk meminimalkan risiko jika secret bocor.
# Strategi rotasi:
# 1. Rotasi otomatis menggunakan AWS Secrets Manager
resource "aws_secretsmanager_secret_rotation" "db" {
secret_id = aws_secretsmanager_secret.db.id
rotation_lambda_arn = aws_lambda_function.rotate.arn
rotation_rules {
automatically_after_days = 30
}
}
# 2. Rotasi manual (untuk secret yang tidak bisa auto-rotate)
# a. Generate secret baru
# b. Update di secret manager
# c. Deploy aplikasi dengan secret baru
# d. Verifikasi aplikasi berfungsi
# e. Disable secret lama
# f. Hapus secret lama setelah grace period
flowchart TD
A["Secret Manager
(rotation schedule)"] --> B["Trigger rotation
(setiap 30 hari)"]
B --> C["Lambda: generate
secret baru"]
C --> D["Update secret
di Secrets Manager"]
D --> E["App: baca
secret baru"]
E --> F["Verify: app
berfungsi"]
F --> G["Disable
secret lama"]
style A fill:#e3f2fd,stroke:#1565c0
style G fill:#ffebee,stroke:#c62828Secret Manager Integration #
# AWS Secrets Manager dengan Terraform
resource "aws_secretsmanager_secret" "app" {
name = "${local.name_prefix}/app-config"
description = "Application configuration secrets"
# Enable rotation
rotation_lambda_arn = aws_lambda_function.rotate.arn
rotation_rules {
automatically_after_days = 30
}
}
resource "aws_secretsmanager_secret_version" "app" {
secret_id = aws_secretsmanager_secret.app.id
secret_string = jsonencode({
database_url = "postgres://${var.db_user}:${random_password.db.result}@${aws_db_instance.main.address}:5432/app"
redis_url = "redis://${aws_elasticache_cluster.main.cache_nodes[0].address}:6379"
jwt_secret = random_password.jwt.result
})
}
Ringkasan #
sensitive = truehanya menyembunyikan dari terminal output, bukan dari state file — nilai sensitif tetap tersimpan plaintext di state.- Hindari generate secret di Terraform jika bisa — biarkan AWS Secrets Manager atau tools khusus yang mengelola lifecycle secret.
- Gunakan
manage_master_user_passworddi RDS — AWS mengelola password dan menyimpannya di Secrets Manager tanpa nilai masuk ke Terraform state.- SSM Parameter Store untuk konfigurasi non-secret yang perlu diakses banyak layanan; Secrets Manager untuk credential dan secret yang perlu enkripsi dan rotasi.
- ECS secrets vs environment — gunakan
secrets(referensi Secrets Manager) bukanenvironmentuntuk nilai sensitif di task definition; ECS agent membaca nilai saat runtime, tidak masuk ke state.- Amankan akses ke state backend dengan enkripsi KMS dan IAM policy yang ketat — ini adalah pertahanan terakhir karena state bisa berisi secret plaintext.
← Sebelumnya: Provider Authentication Berikutnya: Credential Rotation Strategy →