article

Terraform - สร้างและจัดการ Infrastructure ด้วย Code

16 min read

Terraform คืออะไร?

Terraform เป็น Infrastructure as Code (IaC) Tool ที่ช่วยให้เราสร้าง จัดการ และรื้อถอน Infrastructure ผ่าน Configuration Files แทนการทำผ่าน UI หรือ CLI แบบ Manual

ข้อดีของ Infrastructure as Code

  • Version Control: เก็บ Infrastructure ใน Git
  • Reproducible: สร้าง Environment เหมือนกันได้หลายครั้ง
  • Collaborative: ทีมงานทำงานร่วมกันได้
  • Automation: รวมเข้ากับ CI/CD Pipeline
  • Documentation: Code เป็น Documentation

Terraform Core Concepts

1. Provider

Provider คือ Plugin ที่ใช้สื่อสารกับ Cloud Services

# AWS Provider
provider "aws" {
  region  = "ap-southeast-1"
  profile = "default"
}

# Azure Provider
provider "azurerm" {
  features {}
  subscription_id = var.subscription_id
}

# Google Cloud Provider
provider "google" {
  project = var.project_id
  region  = "asia-southeast1"
  zone    = "asia-southeast1-a"
}

# Multiple AWS Providers (Multi-Region)
provider "aws" {
  alias  = "us_east"
  region = "us-east-1"
}

provider "aws" {
  alias  = "ap_southeast"  
  region = "ap-southeast-1"
}

2. Resources

Resource คือ Infrastructure Components ที่ต้องการสร้าง

# AWS EC2 Instance
resource "aws_instance" "web_server" {
  ami           = "ami-0c02fb55956c7d316" # Amazon Linux 2
  instance_type = "t3.micro"
  
  vpc_security_group_ids = [aws_security_group.web_sg.id]
  subnet_id              = aws_subnet.public.id
  
  user_data = <<-EOF
    #!/bin/bash
    yum update -y
    yum install -y httpd
    systemctl start httpd
    systemctl enable httpd
    echo "<h1>Hello from Terraform!</h1>" > /var/www/html/index.html
  EOF
  
  tags = {
    Name        = "WebServer"
    Environment = var.environment
    Project     = var.project_name
  }
}

# AWS RDS Database
resource "aws_db_instance" "postgres" {
  identifier     = "${var.project_name}-postgres"
  engine         = "postgres"
  engine_version = "13.7"
  instance_class = "db.t3.micro"
  
  allocated_storage     = 20
  max_allocated_storage = 100
  storage_encrypted     = true
  
  db_name  = var.database_name
  username = var.database_username
  password = var.database_password
  
  vpc_security_group_ids = [aws_security_group.database_sg.id]
  db_subnet_group_name   = aws_db_subnet_group.main.name
  
  backup_retention_period = 7
  backup_window          = "03:00-04:00"
  maintenance_window     = "sun:04:00-sun:05:00"
  
  skip_final_snapshot = true
  deletion_protection = false
  
  tags = {
    Name = "${var.project_name}-postgres"
  }
}

3. Variables

Variables ทำให้ Configuration มีความยืดหยุ่น

# variables.tf
variable "environment" {
  description = "Environment name (dev, staging, prod)"
  type        = string
  default     = "dev"
}

variable "project_name" {
  description = "Name of the project"
  type        = string
  validation {
    condition     = can(regex("^[a-z0-9-]+$", var.project_name))
    error_message = "Project name must contain only lowercase letters, numbers, and hyphens."
  }
}

variable "instance_types" {
  description = "Map of instance types by environment"
  type        = map(string)
  default = {
    dev     = "t3.micro"
    staging = "t3.small"
    prod    = "t3.medium"
  }
}

variable "database_config" {
  description = "Database configuration"
  type = object({
    engine         = string
    engine_version = string
    instance_class = string
    allocated_storage = number
  })
  default = {
    engine         = "postgres"
    engine_version = "13.7"
    instance_class = "db.t3.micro"
    allocated_storage = 20
  }
}

variable "allowed_cidr_blocks" {
  description = "List of CIDR blocks allowed to access resources"
  type        = list(string)
  default     = ["10.0.0.0/8"]
}

variable "tags" {
  description = "Common tags for all resources"
  type        = map(string)
  default = {
    Terraform = "true"
    Owner     = "DevOps Team"
  }
}

4. Outputs

Outputs ส่งออกข้อมูลที่สำคัญ

# outputs.tf
output "web_server_public_ip" {
  description = "Public IP of the web server"
  value       = aws_instance.web_server.public_ip
}

output "web_server_public_dns" {
  description = "Public DNS of the web server"
  value       = aws_instance.web_server.public_dns
}

output "database_endpoint" {
  description = "Database endpoint"
  value       = aws_db_instance.postgres.endpoint
  sensitive   = true
}

output "load_balancer_dns" {
  description = "Load balancer DNS name"
  value       = aws_lb.main.dns_name
}

output "vpc_id" {
  description = "ID of the VPC"
  value       = aws_vpc.main.id
}

Real-World Example: Complete AWS Infrastructure

Project Structure

terraform/
├── environments/
│   ├── dev/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── terraform.tfvars
│   ├── staging/
│   └── prod/
├── modules/
│   ├── networking/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   ├── compute/
│   ├── database/
│   └── monitoring/
├── shared/
│   ├── s3-backend.tf
│   └── provider.tf
└── scripts/
    ├── deploy.sh
    └── destroy.sh

Networking Module

# modules/networking/main.tf
resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true
  
  tags = merge(var.common_tags, {
    Name = "${var.project_name}-vpc"
  })
}

# Internet Gateway
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
  
  tags = merge(var.common_tags, {
    Name = "${var.project_name}-igw"
  })
}

# Public Subnets
resource "aws_subnet" "public" {
  count = length(var.availability_zones)
  
  vpc_id                  = aws_vpc.main.id
  cidr_block              = cidrsubnet(var.vpc_cidr, 4, count.index)
  availability_zone       = var.availability_zones[count.index]
  map_public_ip_on_launch = true
  
  tags = merge(var.common_tags, {
    Name = "${var.project_name}-public-${count.index + 1}"
    Type = "Public"
  })
}

# Private Subnets
resource "aws_subnet" "private" {
  count = length(var.availability_zones)
  
  vpc_id            = aws_vpc.main.id
  cidr_block        = cidrsubnet(var.vpc_cidr, 4, count.index + length(var.availability_zones))
  availability_zone = var.availability_zones[count.index]
  
  tags = merge(var.common_tags, {
    Name = "${var.project_name}-private-${count.index + 1}"
    Type = "Private"
  })
}

# NAT Gateways
resource "aws_eip" "nat" {
  count  = length(var.availability_zones)
  domain = "vpc"
  
  depends_on = [aws_internet_gateway.main]
  
  tags = merge(var.common_tags, {
    Name = "${var.project_name}-nat-eip-${count.index + 1}"
  })
}

resource "aws_nat_gateway" "main" {
  count = length(var.availability_zones)
  
  allocation_id = aws_eip.nat[count.index].id
  subnet_id     = aws_subnet.public[count.index].id
  
  depends_on = [aws_internet_gateway.main]
  
  tags = merge(var.common_tags, {
    Name = "${var.project_name}-nat-${count.index + 1}"
  })
}

# Route Tables
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id
  
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }
  
  tags = merge(var.common_tags, {
    Name = "${var.project_name}-public-rt"
  })
}

resource "aws_route_table" "private" {
  count  = length(var.availability_zones)
  vpc_id = aws_vpc.main.id
  
  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.main[count.index].id
  }
  
  tags = merge(var.common_tags, {
    Name = "${var.project_name}-private-rt-${count.index + 1}"
  })
}

# Route Table Associations
resource "aws_route_table_association" "public" {
  count = length(aws_subnet.public)
  
  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}

resource "aws_route_table_association" "private" {
  count = length(aws_subnet.private)
  
  subnet_id      = aws_subnet.private[count.index].id
  route_table_id = aws_route_table.private[count.index].id
}

Compute Module

# modules/compute/main.tf

# Application Load Balancer
resource "aws_lb" "main" {
  name               = "${var.project_name}-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb.id]
  subnets            = var.public_subnet_ids
  
  enable_deletion_protection = var.enable_deletion_protection
  
  tags = var.common_tags
}

# Target Group
resource "aws_lb_target_group" "main" {
  name     = "${var.project_name}-tg"
  port     = 80
  protocol = "HTTP"
  vpc_id   = var.vpc_id
  
  health_check {
    enabled             = true
    healthy_threshold   = 2
    unhealthy_threshold = 2
    timeout             = 5
    interval            = 30
    path                = "/health"
    matcher             = "200"
    port                = "traffic-port"
    protocol            = "HTTP"
  }
  
  tags = var.common_tags
}

# ALB Listener
resource "aws_lb_listener" "main" {
  load_balancer_arn = aws_lb.main.arn
  port              = "80"
  protocol          = "HTTP"
  
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.main.arn
  }
}

# Launch Template
resource "aws_launch_template" "main" {
  name_prefix   = "${var.project_name}-"
  image_id      = var.ami_id
  instance_type = var.instance_type
  
  vpc_security_group_ids = [aws_security_group.web.id]
  
  user_data = base64encode(templatefile("${path.module}/user_data.sh", {
    project_name = var.project_name
    database_url = var.database_url
  }))
  
  tag_specifications {
    resource_type = "instance"
    tags = merge(var.common_tags, {
      Name = "${var.project_name}-instance"
    })
  }
  
  lifecycle {
    create_before_destroy = true
  }
}

# Auto Scaling Group
resource "aws_autoscaling_group" "main" {
  name                = "${var.project_name}-asg"
  vpc_zone_identifier = var.private_subnet_ids
  target_group_arns   = [aws_lb_target_group.main.arn]
  health_check_type   = "ELB"
  health_check_grace_period = 300
  
  min_size         = var.min_size
  max_size         = var.max_size
  desired_capacity = var.desired_capacity
  
  launch_template {
    id      = aws_launch_template.main.id
    version = "$Latest"
  }
  
  dynamic "tag" {
    for_each = var.common_tags
    content {
      key                 = tag.key
      value               = tag.value
      propagate_at_launch = true
    }
  }
  
  tag {
    key                 = "Name"
    value               = "${var.project_name}-asg-instance"
    propagate_at_launch = true
  }
}

# Auto Scaling Policies
resource "aws_autoscaling_policy" "scale_up" {
  name                   = "${var.project_name}-scale-up"
  scaling_adjustment     = 1
  adjustment_type        = "ChangeInCapacity"
  cooldown              = 300
  autoscaling_group_name = aws_autoscaling_group.main.name
}

resource "aws_autoscaling_policy" "scale_down" {
  name                   = "${var.project_name}-scale-down"  
  scaling_adjustment     = -1
  adjustment_type        = "ChangeInCapacity"
  cooldown              = 300
  autoscaling_group_name = aws_autoscaling_group.main.name
}

# CloudWatch Alarms
resource "aws_cloudwatch_metric_alarm" "cpu_high" {
  alarm_name          = "${var.project_name}-cpu-high"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = "2"
  metric_name         = "CPUUtilization"
  namespace           = "AWS/EC2"
  period              = "300"
  statistic           = "Average"
  threshold           = "80"
  alarm_description   = "This metric monitors ec2 cpu utilization"
  alarm_actions       = [aws_autoscaling_policy.scale_up.arn]
  
  dimensions = {
    AutoScalingGroupName = aws_autoscaling_group.main.name
  }
}

resource "aws_cloudwatch_metric_alarm" "cpu_low" {
  alarm_name          = "${var.project_name}-cpu-low"
  comparison_operator = "LessThanThreshold"
  evaluation_periods  = "2"
  metric_name         = "CPUUtilization"
  namespace           = "AWS/EC2"
  period              = "300"
  statistic           = "Average"
  threshold           = "20"
  alarm_description   = "This metric monitors ec2 cpu utilization"
  alarm_actions       = [aws_autoscaling_policy.scale_down.arn]
  
  dimensions = {
    AutoScalingGroupName = aws_autoscaling_group.main.name
  }
}

State Management

Remote State with S3

# shared/s3-backend.tf
terraform {
  required_version = ">= 1.0"
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
  
  backend "s3" {
    bucket         = "my-terraform-state-bucket"
    key            = "environments/dev/terraform.tfstate"
    region         = "ap-southeast-1"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"
  }
}

# S3 Bucket for State
resource "aws_s3_bucket" "terraform_state" {
  bucket = "my-terraform-state-bucket"
  
  lifecycle {
    prevent_destroy = true
  }
}

resource "aws_s3_bucket_versioning" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_encryption" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id
  
  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }
}

resource "aws_s3_bucket_public_access_block" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id
  
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

# DynamoDB Table for State Locking
resource "aws_dynamodb_table" "terraform_state_lock" {
  name           = "terraform-state-lock"
  billing_mode   = "PAY_PER_REQUEST"
  hash_key       = "LockID"
  
  attribute {
    name = "LockID"
    type = "S"
  }
  
  tags = {
    Name = "Terraform State Lock Table"
  }
}

Workspace Management

# สร้าง Workspace สำหรับ environments ต่างๆ
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod

# เปลี่ยน Workspace
terraform workspace select dev
terraform workspace select prod

# ดู Workspace ปัจจุบัน
terraform workspace show

# ดู Workspace ทั้งหมด
terraform workspace list

Environment-Specific Configuration

# environments/dev/terraform.tfvars
project_name = "myapp-dev"
environment  = "dev"
region      = "ap-southeast-1"

vpc_cidr = "10.0.0.0/16"
availability_zones = ["ap-southeast-1a", "ap-southeast-1b"]

# Instance configuration
instance_type = "t3.micro"
min_size      = 1
max_size      = 2
desired_capacity = 1

# Database configuration
database_config = {
  engine         = "postgres"
  engine_version = "13.7"
  instance_class = "db.t3.micro"
  allocated_storage = 20
}

# Security
allowed_cidr_blocks = ["0.0.0.0/0"] # Dev environment - open access

tags = {
  Environment = "dev"
  Project     = "myapp"
  Owner       = "DevOps Team"
  Terraform   = "true"
}
# environments/prod/terraform.tfvars
project_name = "myapp-prod"
environment  = "prod"
region      = "ap-southeast-1"

vpc_cidr = "10.1.0.0/16"
availability_zones = ["ap-southeast-1a", "ap-southeast-1b", "ap-southeast-1c"]

# Instance configuration  
instance_type = "t3.large"
min_size      = 2
max_size      = 10
desired_capacity = 3

# Database configuration
database_config = {
  engine         = "postgres"
  engine_version = "13.7"
  instance_class = "db.r5.large"
  allocated_storage = 100
}

# Security - restrictive access
allowed_cidr_blocks = ["10.0.0.0/8", "172.16.0.0/12"]

tags = {
  Environment = "prod"
  Project     = "myapp"
  Owner       = "DevOps Team"
  Terraform   = "true"
  Backup      = "daily"
}

Advanced Terraform Patterns

1. Data Sources

# ใช้ AMI ล่าสุดของ Amazon Linux
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]
  
  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
  
  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

# ใช้ Availability Zones ที่มีอยู่
data "aws_availability_zones" "available" {
  state = "available"
}

# ใช้ Route53 Hosted Zone ที่มีอยู่
data "aws_route53_zone" "main" {
  name         = var.domain_name
  private_zone = false
}

# การใช้งาน
resource "aws_instance" "web" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t3.micro"
  
  availability_zone = data.aws_availability_zones.available.names[0]
}

2. Conditional Resources

# สร้าง NAT Gateway เฉพาะใน Production
resource "aws_nat_gateway" "main" {
  count = var.environment == "prod" ? length(var.availability_zones) : 0
  
  allocation_id = aws_eip.nat[count.index].id
  subnet_id     = aws_subnet.public[count.index].id
}

# สร้าง RDS Multi-AZ เฉพาะใน Production
resource "aws_db_instance" "postgres" {
  # ... other configuration
  
  multi_az = var.environment == "prod" ? true : false
  backup_retention_period = var.environment == "prod" ? 7 : 1
  deletion_protection = var.environment == "prod" ? true : false
}

3. For Each Loops

# สร้าง Security Groups หลายตัว
variable "security_groups" {
  type = map(object({
    description = string
    ingress = list(object({
      from_port   = number
      to_port     = number
      protocol    = string
      cidr_blocks = list(string)
    }))
  }))
  
  default = {
    web = {
      description = "Security group for web servers"
      ingress = [
        {
          from_port   = 80
          to_port     = 80
          protocol    = "tcp"
          cidr_blocks = ["0.0.0.0/0"]
        },
        {
          from_port   = 443
          to_port     = 443
          protocol    = "tcp"  
          cidr_blocks = ["0.0.0.0/0"]
        }
      ]
    }
    database = {
      description = "Security group for database"
      ingress = [
        {
          from_port   = 5432
          to_port     = 5432
          protocol    = "tcp"
          cidr_blocks = ["10.0.0.0/16"]
        }
      ]
    }
  }
}

resource "aws_security_group" "main" {
  for_each = var.security_groups
  
  name        = "${var.project_name}-${each.key}-sg"
  description = each.value.description
  vpc_id      = aws_vpc.main.id
  
  dynamic "ingress" {
    for_each = each.value.ingress
    content {
      from_port   = ingress.value.from_port
      to_port     = ingress.value.to_port
      protocol    = ingress.value.protocol
      cidr_blocks = ingress.value.cidr_blocks
    }
  }
  
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  tags = merge(var.common_tags, {
    Name = "${var.project_name}-${each.key}-sg"
  })
}

4. Local Values

locals {
  # Common tags
  common_tags = merge(var.tags, {
    Environment = var.environment
    Project     = var.project_name
    ManagedBy   = "Terraform"
    CreatedAt   = formatdate("YYYY-MM-DD", timestamp())
  })
  
  # Environment-specific configuration
  instance_type = var.environment == "prod" ? "t3.large" : "t3.micro"
  
  # Naming convention
  name_prefix = "${var.project_name}-${var.environment}"
  
  # CIDR calculations
  public_subnets = [
    for i in range(length(var.availability_zones)) :
    cidrsubnet(var.vpc_cidr, 4, i)
  ]
  
  private_subnets = [
    for i in range(length(var.availability_zones)) :
    cidrsubnet(var.vpc_cidr, 4, i + length(var.availability_zones))
  ]
}

CI/CD Integration

GitHub Actions

# .github/workflows/terraform.yml
name: Terraform

on:
  push:
    branches: [ main, develop ]
    paths: [ 'terraform/**' ]
  pull_request:
    branches: [ main ]
    paths: [ 'terraform/**' ]

env:
  TF_VERSION: '1.6.0'
  AWS_REGION: 'ap-southeast-1'

jobs:
  terraform:
    name: Terraform
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        environment: [dev, staging, prod]
    
    steps:
    - name: Checkout
      uses: actions/checkout@v3
    
    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v2
      with:
        terraform_version: ${{ env.TF_VERSION }}
    
    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v2
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ${{ env.AWS_REGION }}
    
    - name: Terraform Format Check
      run: |
        cd terraform/environments/${{ matrix.environment }}
        terraform fmt -check -recursive
    
    - name: Terraform Init
      run: |
        cd terraform/environments/${{ matrix.environment }}
        terraform init
    
    - name: Terraform Validate
      run: |
        cd terraform/environments/${{ matrix.environment }}
        terraform validate
    
    - name: Terraform Plan
      run: |
        cd terraform/environments/${{ matrix.environment }}
        terraform plan -out=tfplan
      
    - name: Terraform Apply (Auto-approve for dev)
      if: github.ref == 'refs/heads/develop' && matrix.environment == 'dev'
      run: |
        cd terraform/environments/${{ matrix.environment }}
        terraform apply -auto-approve tfplan
    
    - name: Terraform Apply (Manual approval for prod)
      if: github.ref == 'refs/heads/main' && matrix.environment == 'prod'
      uses: hashicorp/terraform-github-actions/apply@v0.4.0
      with:
        tf_actions_working_dir: terraform/environments/${{ matrix.environment }}
        tf_actions_comment: true
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Deployment Script

#!/bin/bash
# scripts/deploy.sh

set -e

ENVIRONMENT=${1:-dev}
ACTION=${2:-plan}

if [[ ! "$ENVIRONMENT" =~ ^(dev|staging|prod)$ ]]; then
    echo "Error: Environment must be dev, staging, or prod"
    exit 1
fi

if [[ ! "$ACTION" =~ ^(plan|apply|destroy)$ ]]; then
    echo "Error: Action must be plan, apply, or destroy"
    exit 1
fi

echo "🚀 Deploying to $ENVIRONMENT environment..."

cd "terraform/environments/$ENVIRONMENT"

# Initialize Terraform
echo "📦 Initializing Terraform..."
terraform init -upgrade

# Validate configuration
echo "✅ Validating configuration..."
terraform validate

# Format check
echo "🎨 Checking format..."
terraform fmt -check -recursive || {
    echo "❌ Format check failed. Run 'terraform fmt -recursive' to fix."
    exit 1
}

# Security scan (optional)
if command -v tfsec &> /dev/null; then
    echo "🔒 Running security scan..."
    tfsec .
fi

case $ACTION in
    plan)
        echo "📋 Creating execution plan..."
        terraform plan -out=tfplan
        ;;
    apply)
        if [ ! -f tfplan ]; then
            echo "❌ No plan file found. Run plan first."
            exit 1
        fi
        
        if [ "$ENVIRONMENT" = "prod" ]; then
            read -p "⚠️  Are you sure you want to apply to PRODUCTION? (yes/no): " confirm
            if [ "$confirm" != "yes" ]; then
                echo "❌ Deployment cancelled."
                exit 1
            fi
        fi
        
        echo "🚀 Applying changes..."
        terraform apply tfplan
        
        echo "📊 Showing outputs..."
        terraform output
        ;;
    destroy)
        read -p "⚠️  Are you sure you want to DESTROY $ENVIRONMENT environment? (yes/no): " confirm
        if [ "$confirm" != "yes" ]; then
            echo "❌ Destroy cancelled."
            exit 1
        fi
        
        echo "💥 Destroying infrastructure..."
        terraform destroy -auto-approve
        ;;
esac

echo "✅ $ACTION completed successfully for $ENVIRONMENT environment!"

Best Practices

1. Security

# ใช้ AWS Secrets Manager สำหรับ sensitive data
resource "aws_secretsmanager_secret" "database_password" {
  name = "${var.project_name}-${var.environment}-db-password"
  description = "Database password for ${var.project_name}"
  
  replica {
    region = "us-west-2"
  }
}

resource "aws_secretsmanager_secret_version" "database_password" {
  secret_id     = aws_secretsmanager_secret.database_password.id
  secret_string = var.database_password
}

# ใช้ data source แทน hardcode
data "aws_secretsmanager_secret_version" "database_password" {
  secret_id = aws_secretsmanager_secret.database_password.id
}

resource "aws_db_instance" "main" {
  # ...
  password = data.aws_secretsmanager_secret_version.database_password.secret_string
}

2. Resource Lifecycle Management

resource "aws_s3_bucket" "important_data" {
  bucket = "${var.project_name}-important-data"
  
  lifecycle {
    prevent_destroy = true
    
    ignore_changes = [
      # Ignore changes to tags, e.g. because a management agent
      # updates these based on some ruleset managed elsewhere.
      tags,
    ]
  }
}

resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = var.instance_type
  
  lifecycle {
    create_before_destroy = true
  }
}

3. State File Management

# Backup state files
aws s3 sync s3://my-terraform-state-bucket s3://my-terraform-state-backup-bucket

# Import existing resources
terraform import aws_instance.web i-1234567890abcdef0

# Remove resources from state without destroying
terraform state rm aws_instance.old_instance

# Move resources in state
terraform state mv aws_instance.old aws_instance.new

# Show current state
terraform state list
terraform state show aws_instance.web

4. Monitoring และ Alerting

# CloudWatch Dashboard
resource "aws_cloudwatch_dashboard" "main" {
  dashboard_name = "${var.project_name}-dashboard"
  
  dashboard_body = jsonencode({
    widgets = [
      {
        type   = "metric"
        x      = 0
        y      = 0
        width  = 12
        height = 6
        
        properties = {
          metrics = [
            ["AWS/ApplicationELB", "RequestCount", "LoadBalancer", aws_lb.main.arn_suffix],
            ["AWS/ApplicationELB", "TargetResponseTime", "LoadBalancer", aws_lb.main.arn_suffix],
            ["AWS/ApplicationELB", "HTTPCode_Target_2XX_Count", "LoadBalancer", aws_lb.main.arn_suffix]
          ]
          period = 300
          stat   = "Sum"
          region = var.aws_region
          title  = "Application Load Balancer Metrics"
        }
      }
    ]
  })
}

# SNS Topic for Alerts
resource "aws_sns_topic" "alerts" {
  name = "${var.project_name}-alerts"
}

resource "aws_sns_topic_subscription" "email" {
  topic_arn = aws_sns_topic.alerts.arn
  protocol  = "email"
  endpoint  = var.alert_email
}

สรุป

Terraform เป็นเครื่องมือที่ทรงพลังสำหรับการจัดการ Infrastructure:

  1. Infrastructure as Code: ทำให้ Infrastructure มี Version Control และ Reproducible
  2. State Management: ใช้ Remote State และ State Locking เพื่อความปลอดภัย
  3. Modularity: แยก Code เป็น Modules เพื่อการใช้งานซ้ำ
  4. Environment Separation: แยก Environment ชัดเจนด้วย Workspace หรือ Directory
  5. CI/CD Integration: รวมเข้ากับ Pipeline สำหรับ Automation
  6. Security: ใช้ Secrets Management และ IAM Roles อย่างเหมาะสม
  7. Monitoring: ติดตาม State Changes และ Resource Usage

การใช้ Terraform อย่างถูกต้องจะช่วยให้เราจัดการ Infrastructure ได้อย่างมีประสิทธิภาพ ลดความผิดพลาด และเพิ่มความเร็วในการ Deploy!