Terraform #

Dalam arsitektur awan modern, penerapan Serverless dan Infrastructure as Code (IaC) adalah dua pilar yang hampir tidak dapat dipisahkan. Serverless menawarkan keunggulan berupa pembebasan tugas pengelolaan server fisik, sedangkan IaC memberikan kepastian bahwa seluruh resource serverless tersebut didefinisikan secara deklaratif, konsisten, dapat direproduksi di berbagai lingkungan (environment), dan tercatat dalam sistem kontrol versi (Git).

Di lingkungan AWS, meskipun Amazon menyediakan peralatan native seperti AWS CloudFormation dan AWS Serverless Application Model (SAM), Terraform buatan HashiCorp tetap menjadi pilihan utama bagi banyak organisasi tingkat enterprise. Terraform menawarkan fleksibilitas multi-provider, sintaksis HashiCorp Configuration Language (HCL) yang ekspresif, serta ekosistem yang sangat luas. Artikel ini akan membahas secara mendalam bagaimana mengoperasikan Terraform untuk membangun dan mengelola arsitektur serverless di AWS secara profesional.


Mengapa Terraform Relevan untuk Serverless? #

Ada kesalahpahaman umum yang menganggap bahwa karena serverless “tidak memiliki server”, maka kita tidak memerlukan alat IaC yang kompleks. Faktanya, arsitektur serverless justru menghasilkan jumlah resource yang jauh lebih banyak dibandingkan arsitektur tradisional.

Sebuah API serverless sederhana setidaknya membutuhkan:

  • Satu fungsi Lambda.
  • Satu IAM Execution Role dengan kebijakan keamanan (policy) yang presisi.
  • Satu log group di Amazon CloudWatch.
  • Rute-rute (routes) di Amazon API Gateway.
  • Izin eksplisit bagi API Gateway untuk memanggil fungsi Lambda tersebut.

Jika kita membuat seluruh komponen ini secara manual melalui AWS Web Console, sistem kita akan menjadi sangat rentan terhadap kesalahan manusia (human error), sulit didokumentasikan, dan mustahil untuk direplikasi ke lingkungan baru (seperti staging atau production) secara identik. Terraform mengotomatiskan seluruh pembuatan resource ini dan mengelola ketergantungan (dependency) antar-resource secara otomatis.

flowchart TD
    TF["Terraform Code (HCL)"] -->|terraform apply| Backend["S3 State Backend + DynamoDB Lock"]
    TF -->|Menciptakan| Role["AWS IAM Role (Execution Role)"]
    TF -->|Menciptakan| Lambda["AWS Lambda Function"]
    TF -->|Menciptakan| APIGW["Amazon API Gateway (HTTP API)"]
    TF -->|Menciptakan| Permission["AWS Lambda Permission (Allow API Gateway)"]
    
    APIGW -->|Rute Request| Lambda
    Lambda -->|Berjalan di bawah| Role

Manajemen State di Skala Produksi #

Terraform menggunakan berkas status (state file) untuk mencatat pemetaan antara resource nyata di AWS dengan kode HCL yang kita tulis. Untuk kolaborasi tim di skala produksi, kita wajib mengonfigurasi Remote State Backend yang aman dan mendukung penguncian status (state locking).

Konfigurasi Remote State (S3 & DynamoDB Lock) #

Kita harus menyimpan file state di dalam Amazon S3 yang dienkripsi, dan menggunakan Amazon DynamoDB untuk menangani mekanisme locking (mencegah dua developer menjalankan terraform apply secara bersamaan yang dapat merusak file state).

# BENAR: Konfigurasi remote backend aman di tingkat proyek
terraform {
  required_version = ">= 1.5.0"
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }

  backend "s3" {
    bucket         = "mycompany-terraform-states"
    key            = "serverless-app/prod/terraform.tfstate"
    region         = "ap-southeast-1"
    encrypt        = true
    dynamodb_table = "terraform-lock-table" //  Digunakan untuk state locking
  }
}

Contoh Kode HCL Lengkap: API Gateway & Lambda #

Berikut adalah contoh konfigurasi Terraform yang fungsional dan siap pakai untuk mendeploy arsitektur API Gateway HTTP API -> AWS Lambda (Node.js) -> CloudWatch Logs.

1. File: lambda.tf (Mendefinisikan Fungsi dan IAM Role) #

# Mengarsipkan kode sumber JavaScript menjadi file ZIP secara otomatis
data "archive_file" "lambda_zip" {
  type        = "zip"
  source_dir  = "${path.module}/src"
  output_path = "${path.module}/dist/lambda.zip"
}

# Membuat Execution Role untuk Lambda
resource "aws_iam_role" "lambda_exec" {
  name = "app-lambda-execution-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action    = "sts:AssumeRole"
      Effect    = "Allow"
      Principal = {
        Service = "lambda.amazonaws.com"
      }
    }]
  })
}

# Menempelkan policy standar untuk penulisan log ke CloudWatch
resource "aws_iam_role_policy_attachment" "lambda_logs" {
  role       = aws_iam_role.lambda_exec.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

# Membuat Fungsi Lambda
resource "aws_lambda_function" "api_handler" {
  filename         = data.archive_file.lambda_zip.output_path
  source_code_hash = data.archive_file.lambda_zip.output_base64sha256
  function_name    = "app-api-handler"
  role             = aws_iam_role.lambda_exec.arn
  handler          = "index.handler"
  runtime          = "nodejs20.x"
  timeout          = 10
  memory_size      = 256

  environment {
    variables = {
      NODE_ENV = "production"
    }
  }

  # Memastikan log group dibuat sebelum fungsi dideploy
  depends_on = [
    aws_iam_role_policy_attachment.lambda_logs,
    aws_cloudwatch_log_group.lambda_log_group
  ]
}

# Membuat Log Group CloudWatch secara eksplisit untuk mengatur retensi log
resource "aws_cloudwatch_log_group" "lambda_log_group" {
  name              = "/aws/lambda/app-api-handler"
  retention_in_days = 7 //  Mencegah tagihan bengkak akibat penyimpanan log tanpa batas
}

2. File: api_gateway.tf (Mendefinisikan Gateway dan Izin) #

# Membuat API Gateway HTTP API (Lebih murah dan cepat dari REST API)
resource "aws_apigatewayv2_api" "http_api" {
  name          = "app-http-api"
  protocol_type = "HTTP"
}

# Membuat Stage untuk deployment (dev, prod, dll)
resource "aws_apigatewayv2_stage" "prod" {
  api_id      = aws_apigatewayv2_api.http_api.id
  name        = "prod"
  auto_deploy = true

  access_log_settings {
    destination_arn = aws_cloudwatch_log_group.api_gw.arn
    format          = jsonencode({
      requestId      = "$context.requestId"
      ip             = "$context.identity.sourceIp"
      requestTime    = "$context.requestTime"
      httpMethod     = "$context.httpMethod"
      routeKey       = "$context.routeKey"
      status         = "$context.status"
      protocol       = "$context.protocol"
      responseLength = "$context.responseLength"
    })
  }
}

# Membuat Log Group CloudWatch untuk API Gateway Access Logs
resource "aws_cloudwatch_log_group" "api_gw" {
  name              = "/aws/api-gw/app-http-api"
  retention_in_days = 7
}

# Mengonfigurasi Integrasi antara API Gateway dengan Fungsi Lambda
resource "aws_apigatewayv2_integration" "lambda" {
  api_id           = aws_apigatewayv2_api.http_api.id
  integration_type = "AWS_PROXY" //  Lambda Proxy Integration

  integration_method = "POST"
  integration_uri    = aws_lambda_function.api_handler.invoke_arn
  payload_format_version = "2.0"
}

# Membuat Route default (Menangkap seluruh request HTTP)
resource "aws_apigatewayv2_route" "any" {
  api_id    = aws_apigatewayv2_api.http_api.id
  route_key = "ANY /{proxy+}"
  target    = "integrations/${aws_apigatewayv2_integration.lambda.id}"
}

# ✓ PENTING: Memberikan izin tertulis bagi API Gateway untuk memanggil Lambda
resource "aws_lambda_permission" "api_gw" {
  statement_id  = "AllowAPIGatewayInvoke"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.api_handler.function_name
  principal     = "apigateway.amazonaws.com"

  # Membatasi izin pemanggilan hanya dari API Gateway spesifik kita
  source_arn = "${aws_apigatewayv2_api.http_api.execution_arn}/*/*"
}

# Output untuk mencetak URL API Gateway setelah terraform apply selesai
output "api_url" {
  value       = "${aws_apigatewayv2_stage.prod.invoke_url}/"
  description = "URL endpoint publik HTTP API"
}

Perbandingan: Terraform vs. AWS SAM / Serverless Framework #

Banyak tim sering bingung memilih antara menggunakan Terraform dengan perkakas khusus serverless lainnya.

Dimensi Evaluasi Terraform AWS SAM / Serverless Framework
Cakupan Pengelolaan Seluruh infrastruktur cloud (VPC, RDS, IAM, S3, dll) Fokus utama pada resource aplikasi serverless
Kecepatan Iterasi Kode Sedang (Proses kompresi zip & upload dikelola manual/HCL) Sangat cepat (Memiliki perintah deploy kode instan)
Abstraksi Sintaksis Rendah (Menulis semua komponen secara eksplisit) Tinggi (Menulis beberapa baris YAML menghasilkan banyak resource)
State Management Wajib dikelola sendiri (S3 backend) Dikelola otomatis oleh CloudFormation di latar belakang

Rekomendasi Pendekatan Campuran (Hybrid Approach) #

Untuk proyek skala besar, pendekatan terbaik adalah pendekatan campuran:

  1. Gunakan Terraform untuk membangun infrastruktur dasar (foundation) yang bersifat semi-permanen dan jarang berubah (seperti VPC, Amazon RDS Database, S3 Bucket, Amazon SQS Queue).
  2. Gunakan Serverless Framework atau AWS SAM untuk mendeploy aplikasi tingkat atas yang sering mengalami perubahan kode (seperti fungsi Lambda dan rute API Gateway). Kita dapat mengambil referensi ARN infrastruktur dasar dari Terraform menggunakan data source SSM Parameter Store.

Ringkasan #

  • Terraform mempermudah pengelolaan serverless dengan mendefinisikan infrastruktur yang kompleks secara deklaratif dan konsisten (IaC).
  • Selalu gunakan Remote State Backend berbasis Amazon S3 dengan enkripsi aktif, serta DynamoDB untuk menangani mekanisme state locking secara aman.
  • Konfigurasikan retensi CloudWatch Log Group secara eksplisit di Terraform untuk mencegah pembengkakan biaya akibat penyimpanan log tanpa batas.
  • Berikan izin eksplisit via aws_lambda_permission agar API Gateway memiliki hak akses untuk memicu eksekusi fungsi Lambda.
  • Terapkan pendekatan hibrida: gunakan Terraform untuk fondasi jaringan/database, dan gunakan SAM/Serverless Framework untuk deployment kode Lambda harian yang dinamis.

← Sebelumnya: API Gateway   Berikutnya: Cloud Functions →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact