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.
Apa 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
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 →