Skip to content

Instantly share code, notes, and snippets.

@tbvinh
Created December 17, 2025 06:29
Show Gist options
  • Select an option

  • Save tbvinh/ff03ea0f3ff821842ef25810cd9a8815 to your computer and use it in GitHub Desktop.

Select an option

Save tbvinh/ff03ea0f3ff821842ef25810cd9a8815 to your computer and use it in GitHub Desktop.
aws-script.sh
#!/bin/bash
# --- Cấu hình dự án ---
PROJECT_DIR="mva_terraform_project"
echo "Bắt đầu tạo cấu trúc dự án Terraform tại thư mục: $PROJECT_DIR"
# 1. Tạo thư mục gốc
mkdir -p $PROJECT_DIR
cd $PROJECT_DIR
# 2. Tạo cấu trúc thư mục modules
mkdir -p modules/{vpc,stateful,ecs-alb}
echo "Đang tạo các file gốc (.tf) và variables..."
# 3. Tạo các file gốc (.tf)
cat << EOF > main.tf
# File: main.tf
module "vpc" {
source = "./modules/vpc"
app_name = var.app_name
}
module "stateful" {
source = "./modules/stateful"
app_name = var.app_name
vpc_id = module.vpc.vpc_id
database_subnet_ids = module.vpc.database_subnet_ids
private_app_subnet_ids = module.vpc.private_app_subnet_ids
db_username = var.db_username
db_password = var.db_password
}
module "ecs_alb" {
source = "./modules/ecs-alb"
app_name = var.app_name
vpc_id = module.vpc.vpc_id
public_subnet_ids = module.vpc.public_subnet_ids
private_app_subnet_ids = module.vpc.private_app_subnet_ids
rds_endpoint = module.stateful.rds_endpoint
redis_endpoint = module.stateful.redis_endpoint
opensearch_endpoint = module.stateful.opensearch_endpoint
}
EOF
cat << EOF > provider.tf
# File: provider.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "ap-southeast-1"
}
EOF
cat << EOF > variables.tf
# File: variables.tf
variable "db_username" {
description = "Tên người dùng PostgreSQL"
default = "dbadmin"
}
variable "db_password" {
description = "Mật khẩu PostgreSQL"
default = "DevPassword123!"
sensitive = true
}
variable "app_name" {
description = "Tên prefix cho các tài nguyên"
default = "mva-app"
}
EOF
echo "Đang tạo nội dung module VPC..."
# 4. Nội dung Module VPC
cat << EOF > modules/vpc/main.tf
# File: modules/vpc/main.tf
variable "app_name" {}
locals {
# 2 AZs cho cấu hình tối giản
azs = ["ap-southeast-1a", "ap-southeast-1b"]
}
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = { Name = "\${var.app_name}-vpc" }
}
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.main.id
}
resource "aws_subnet" "public" {
count = length(local.azs)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 8, count.index)
availability_zone = local.azs[count.index]
map_public_ip_on_launch = true
tags = { Name = "\${var.app_name}-public-\${count.index}" }
}
resource "aws_subnet" "private_app" {
count = length(local.azs)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 8, count.index + 2)
availability_zone = local.azs[count.index]
tags = { Name = "\${var.app_name}-app-\${count.index}" }
}
resource "aws_subnet" "database" {
count = length(local.azs)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 8, count.index + 4)
availability_zone = local.azs[count.index]
tags = { Name = "\${var.app_name}-db-\${count.index}" }
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
}
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
}
EOF
cat << EOF > modules/vpc/outputs.tf
output "vpc_id" { value = aws_vpc.main.id }
output "public_subnet_ids" { value = aws_subnet.public[*].id }
output "private_app_subnet_ids" { value = aws_subnet.private_app[*].id }
output "database_subnet_ids" { value = aws_subnet.database[*].id }
EOF
echo "Đang tạo nội dung module Stateful (Dữ liệu)..."
# 5. Nội dung Module Stateful
cat << EOF > modules/stateful/variables.tf
variable "app_name" {}
variable "vpc_id" {}
variable "database_subnet_ids" { type = list(string) }
variable "private_app_subnet_ids" { type = list(string) }
variable "db_username" {}
variable "db_password" {}
EOF
cat << EOF > modules/stateful/main.tf
# File: modules/stateful/main.tf
resource "aws_security_group" "data_tier_sg" {
name = "\${var.app_name}-data-tier-sg"
vpc_id = var.vpc_id
ingress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = var.private_app_subnet_ids
}
egress { from_port = 0; to_port = 0; protocol = "-1"; cidr_blocks = ["0.0.0.0/0"] }
}
resource "aws_db_subnet_group" "rds_group" {
name = "\${var.app_name}-rds-group"
subnet_ids = var.database_subnet_ids
}
resource "aws_db_instance" "postgres" {
identifier = "\${var.app_name}-db"
instance_class = "db.t3.micro"
multi_az = false # Tắt HA
username = var.db_username
password = var.db_password
db_name = "appdb"
vpc_security_group_ids = [aws_security_group.data_tier_sg.id]
db_subnet_group_name = aws_db_subnet_group.rds_group.name
skip_final_snapshot = true
}
resource "aws_elasticache_subnet_group" "cache_group" {
name = "\${var.app_name}-cache-group"
subnet_ids = var.database_subnet_ids
}
resource "aws_elasticache_replication_group" "redis" {
replication_group_id = "\${var.app_name}-redis"
node_type = "cache.t3.micro"
num_cache_clusters = 1
automatic_failover_enabled = false # Tắt HA
subnet_group_name = aws_elasticache_subnet_group.cache_group.name
security_group_ids = [aws_security_group.data_tier_sg.id]
}
resource "aws_opensearch_domain" "es" {
domain_name = "\${var.app_name}-es"
engine_version = "OpenSearch_2.5"
cluster_config {
instance_type = "t3.small.search"
instance_count = 1
}
vpc_options {
subnet_ids = var.database_subnet_ids
security_group_ids = [aws_security_group.data_tier_sg.id]
}
access_policies = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { AWS = "*" }
Action = "es:*"
Resource = "arn:aws:es:*:*:domain/\${var.app_name}-es/*"
}]
})
}
output "rds_endpoint" { value = aws_db_instance.postgres.address }
output "redis_endpoint" { value = aws_elasticache_replication_group.redis.primary_endpoint_address }
output "opensearch_endpoint" { value = aws_opensearch_domain.es.endpoint }
EOF
echo "Đang tạo nội dung module ECS/ALB (Ứng dụng)..."
# 6. Nội dung Module ECS/ALB
cat << EOF > modules/ecs-alb/variables.tf
variable "app_name" {}
variable "vpc_id" {}
variable "public_subnet_ids" { type = list(string) }
variable "private_app_subnet_ids" { type = list(string) }
variable "rds_endpoint" {}
variable "redis_endpoint" {}
variable "opensearch_endpoint" {}
EOF
cat << EOF > modules/ecs-alb/task-definition.json.tpl
{
"containerDefinitions": [
{
"name": "nginx-container",
"image": "\${nginx_image_uri}",
"portMappings": [ { "containerPort": 80, "hostPort": 80 } ],
"essential": true
},
{
"name": "php-fpm-container",
"image": "\${php_fpm_image_uri}",
"portMappings": [ { "containerPort": 9000, "hostPort": 9000 } ],
"essential": true,
"environment": [
{"name": "DB_HOST", "value": "\${rds_host}"},
{"name": "DB_USER", "value": "dbadmin"},
{"name": "DB_PASSWORD", "value": "DevPassword123!"},
{"name": "REDIS_HOST", "value": "\${redis_host}"},
{"name": "OPENSEARCH_HOST", "value": "\${opensearch_host}"}
]
}
]
}
EOF
cat << EOF > modules/ecs-alb/main.tf
# File: modules/ecs-alb/main.tf
# --- 1. ECR Repositories ---
resource "aws_ecr_repository" "nginx_repo" { name = "\${var.app_name}-nginx-repo" }
resource "aws_ecr_repository" "php_fpm_repo" { name = "\${var.app_name}-php-fpm-repo" }
# --- 2. ECS Cluster ---
resource "aws_ecs_cluster" "main" { name = "\${var.app_name}-cluster" }
# --- 3. Task Definition ---
data "template_file" "php_nginx_task" {
template = file("\${path.module}/task-definition.json.tpl")
vars = {
rds_host = var.rds_endpoint
redis_host = var.redis_endpoint
opensearch_host = var.opensearch_endpoint
nginx_image_uri = aws_ecr_repository.nginx_repo.repository_url
php_fpm_image_uri = aws_ecr_repository.php_fpm_repo.repository_url
}
}
resource "aws_ecs_task_definition" "php_nginx" {
family = "\${var.app_name}-php-nginx-task"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = 1024
memory = 2048
container_definitions = data.template_file.php_nginx_task.rendered
# Cần thêm execution_role_arn và task_role_arn (đã đơn giản hóa)
}
# --- 4. Security Groups ---
resource "aws_security_group" "alb_sg" {
name = "\${var.app_name}-alb-sg"
vpc_id = var.vpc_id
ingress { from_port = 80; to_port = 80; protocol = "tcp"; cidr_blocks = ["0.0.0.0/0"] }
egress { from_port = 0; to_port = 0; protocol = "-1"; cidr_blocks = ["0.0.0.0/0"] }
}
resource "aws_security_group" "ecs_sg" {
name = "\${var.app_name}-ecs-sg"
vpc_id = var.vpc_id
# Cho phép ALB truy cập ECS Task trên cổng 80
ingress { from_port = 80; to_port = 80; protocol = "tcp"; security_groups = [aws_security_group.alb_sg.id] }
egress { from_port = 0; to_port = 0; protocol = "-1"; cidr_blocks = ["0.0.0.0/0"] }
}
# --- 5. ALB và Target Groups ---
resource "aws_lb" "main" {
name = "\${var.app_name}-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.alb_sg.id]
subnets = var.public_subnet_ids
}
resource "aws_lb_target_group" "tg_app" { name = "\${var.app_name}-tg-app"; port = 80; vpc_id = var.vpc_id; target_type = "ip" }
resource "aws_lb_target_group" "tg_ws" { name = "\${var.app_name}-tg-ws"; port = 80; vpc_id = var.vpc_id; target_type = "ip" }
# Listener
resource "aws_lb_listener" "http_listener" {
load_balancer_arn = aws_lb.main.arn
port = 80
protocol = "HTTP"
default_action { type = "forward"; target_group_arn = aws_lb_target_group.tg_app.arn }
}
# Giả định www.domain.com trỏ đến TG-APP
# Giả định ws.domain.com trỏ đến TG-WS (WebSockets)
resource "aws_lb_listener_rule" "ws_rule" {
listener_arn = aws_lb_listener.http_listener.arn
priority = 10
action { type = "forward"; target_group_arn = aws_lb_target_group.tg_ws.arn }
condition { host_header { values = ["ws.domain.com"] } }
}
# --- 6. ECS Service ---
resource "aws_ecs_service" "main" {
name = "\${var.app_name}-service"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.php_nginx.arn
desired_count = 1 # TỐI GIẢN: Chỉ 1 Task
launch_type = "FARGATE"
network_configuration {
subnets = var.private_app_subnet_ids
security_groups = [aws_security_group.ecs_sg.id]
assign_public_ip = false
}
load_balancer {
target_group_arn = aws_lb_target_group.tg_app.arn
container_name = "nginx-container"
container_port = 80
}
}
EOF
echo "Hoàn tất tạo dự án Terraform tại \$(pwd)"
echo "--- Các bước tiếp theo ---"
echo "1. Chuyển vào thư mục: cd $PROJECT_DIR"
echo "2. Chỉnh sửa mật khẩu DB trong variables.tf nếu cần."
echo "3. Chạy lệnh: terraform init"
echo "4. Chạy lệnh: terraform apply"
echo "5. Sau khi thử nghiệm, chạy lệnh: terraform destroy (để tránh phí)."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment