variable "environment" { description = "Deployment environment" type = string } variable "vpc_cidr" { description = "CIDR block for VPC" type = string } variable "az_count" { description = "Number of availability zones" type = number } variable "project_name" { description = "Project name" type = string } variable "kms_key_arn" { description = "KMS key ARN for log encryption" type = string default = "" } resource "aws_vpc" "main" { cidr_block = var.vpc_cidr enable_dns_support = true enable_dns_hostnames = true tags = { Name = "${var.project_name}-${var.environment}-vpc" } } data "aws_availability_zones" "available" { state = "available" } resource "aws_subnet" "public" { count = var.az_count vpc_id = aws_vpc.main.id cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index) availability_zone = data.aws_availability_zones.available.names[count.index] map_public_ip_on_launch = false tags = { Name = "${var.project_name}-${var.environment}-public-${data.aws_availability_zones.available.names[count.index]}" "kubernetes.io/role/elb" = "1" } } resource "aws_subnet" "private" { count = var.az_count vpc_id = aws_vpc.main.id cidr_block = cidrsubnet(var.vpc_cidr, 8, var.az_count + count.index) availability_zone = data.aws_availability_zones.available.names[count.index] tags = { Name = "${var.project_name}-${var.environment}-private-${data.aws_availability_zones.available.names[count.index]}" "kubernetes.io/role/internal-elb" = "1" } } resource "aws_internet_gateway" "main" { vpc_id = aws_vpc.main.id tags = { Name = "${var.project_name}-${var.environment}-igw" } } resource "aws_eip" "nat" { count = var.az_count domain = "vpc" tags = { Name = "${var.project_name}-${var.environment}-nat-${count.index}" } } resource "aws_nat_gateway" "main" { count = var.az_count allocation_id = aws_eip.nat[count.index].id subnet_id = aws_subnet.public[count.index].id tags = { Name = "${var.project_name}-${var.environment}-nat-${count.index}" } depends_on = [aws_internet_gateway.main] } 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 = { Name = "${var.project_name}-${var.environment}-public-rt" } } resource "aws_route_table" "private" { count = var.az_count 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 = { Name = "${var.project_name}-${var.environment}-private-rt-${count.index}" } } resource "aws_route_table_association" "public" { count = var.az_count subnet_id = aws_subnet.public[count.index].id route_table_id = aws_route_table.public.id } resource "aws_route_table_association" "private" { count = var.az_count subnet_id = aws_subnet.private[count.index].id route_table_id = aws_route_table.private[count.index].id } resource "aws_security_group" "alb" { name_prefix = "${var.project_name}-${var.environment}-alb" vpc_id = aws_vpc.main.id ingress { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] description = "HTTPS from internet" } ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] description = "HTTP from internet (redirect)" } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "${var.project_name}-${var.environment}-alb-sg" } } resource "aws_security_group" "ecs" { name_prefix = "${var.project_name}-${var.environment}-ecs" vpc_id = aws_vpc.main.id ingress { from_port = 3000 to_port = 3003 protocol = "tcp" security_groups = [aws_security_group.alb.id] description = "Service ports from ALB only" } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "${var.project_name}-${var.environment}-ecs-sg" } } resource "aws_security_group" "rds" { name_prefix = "${var.project_name}-${var.environment}-rds" vpc_id = aws_vpc.main.id ingress { from_port = 5432 to_port = 5432 protocol = "tcp" security_groups = [aws_security_group.ecs.id] description = "PostgreSQL from ECS" } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "${var.project_name}-${var.environment}-rds-sg" } } resource "aws_security_group" "elasticache" { name_prefix = "${var.project_name}-${var.environment}-elasticache" vpc_id = aws_vpc.main.id ingress { from_port = 6379 to_port = 6379 protocol = "tcp" security_groups = [aws_security_group.ecs.id] description = "Redis from ECS" } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "${var.project_name}-${var.environment}-elasticache-sg" } } resource "aws_flow_log" "main" { iam_role_arn = aws_iam_role.flow_log.arn log_destination = aws_cloudwatch_log_group.flow_log.arn vpc_id = aws_vpc.main.id traffic_type = "ALL" tags = { Name = "${var.project_name}-${var.environment}-flow-log" } } resource "aws_iam_role" "flow_log" { name = "${var.project_name}-${var.environment}-flow-log-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "vpc-flow-logs.amazonaws.com" } } ] }) } resource "aws_iam_role_policy" "flow_log" { name = "${var.project_name}-${var.environment}-flow-log-policy" role = aws_iam_role.flow_log.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", "logs:DescribeLogGroups", "logs:DescribeLogStreams" ] Effect = "Allow" Resource = [aws_cloudwatch_log_group.flow_log.arn] } ] }) } resource "aws_cloudwatch_log_group" "flow_log" { name = "/${var.project_name}/${var.environment}/vpc-flow-log" retention_in_days = var.environment == "production" ? 30 : 7 kms_key_id = var.kms_key_arn != "" ? var.kms_key_arn : null tags = { Name = "${var.project_name}-${var.environment}-flow-log" } } output "vpc_id" { description = "VPC ID" value = aws_vpc.main.id } output "private_subnet_ids" { description = "Private subnet IDs" value = aws_subnet.private[*].id } output "public_subnet_ids" { description = "Public subnet IDs" value = aws_subnet.public[*].id } output "alb_security_group_id" { description = "ALB security group ID" value = aws_security_group.alb.id } output "ecs_security_group_id" { description = "ECS security group ID" value = aws_security_group.ecs.id } output "rds_security_group_id" { description = "RDS security group ID" value = aws_security_group.rds.id } output "elasticache_security_group_id" { description = "ElastiCache security group ID" value = aws_security_group.elasticache.id }