terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 6.27" } } } provider "aws" { region = var.aws_region } # Get current AWS account data "aws_caller_identity" "current" {} # Reference existing VPC data "aws_vpc" "existing" { id = var.vpc_id } # Reference existing public subnets data "aws_subnet" "public" { count = length(var.public_subnet_ids) id = var.public_subnet_ids[count.index] } # Reference existing private subnets (for ECS tasks) data "aws_subnet" "private" { count = length(var.private_subnet_ids) id = var.private_subnet_ids[count.index] } # Security Group for ALB (in public subnets) resource "aws_security_group" "alb" { name = "${var.app_name}-alb-sg" description = "Allow inbound traffic to ALB" vpc_id = data.aws_vpc.existing.id ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["3.14.44.224/32"] description = "Allow HTTP from internet" } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] description = "Allow all outbound" } tags = { Name = "${var.app_name}-alb-sg" } } # Security Group for ECS Tasks (in private subnets) resource "aws_security_group" "ecs_tasks" { name = "${var.app_name}-ecs-tasks-sg" description = "Allow inbound traffic from ALB" vpc_id = data.aws_vpc.existing.id ingress { from_port = 8000 to_port = 8000 protocol = "tcp" security_groups = [aws_security_group.alb.id] description = "Allow traffic from ALB" } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] description = "Allow all outbound" } tags = { Name = "${var.app_name}-ecs-tasks-sg" } } # Application Load Balancer (in public subnets) resource "aws_lb" "main" { name = "${var.app_name}-alb" internal = false load_balancer_type = "application" security_groups = [aws_security_group.alb.id] subnets = var.public_subnet_ids enable_deletion_protection = false tags = { Name = "${var.app_name}-alb" } } # Target Group resource "aws_lb_target_group" "app" { name = "${var.app_name}-tg" port = 8000 protocol = "HTTP" vpc_id = data.aws_vpc.existing.id target_type = "ip" health_check { enabled = true healthy_threshold = 2 interval = 30 matcher = "200" path = "/health" port = "traffic-port" protocol = "HTTP" timeout = 5 unhealthy_threshold = 3 } deregistration_delay = 30 tags = { Name = "${var.app_name}-tg" } } # ALB Listener resource "aws_lb_listener" "app" { load_balancer_arn = aws_lb.main.arn port = "80" protocol = "HTTP" default_action { type = "forward" target_group_arn = aws_lb_target_group.app.arn } } # ECS Cluster resource "aws_ecs_cluster" "main" { name = "${var.app_name}-cluster" tags = { Name = "${var.app_name}-cluster" } } # CloudWatch Log Group resource "aws_cloudwatch_log_group" "app" { name = "/ecs/${var.app_name}" retention_in_days = 7 tags = { Name = "${var.app_name}-logs" } } # ECS Task Execution Role resource "aws_iam_role" "ecs_task_execution_role" { name = "${var.app_name}-ecs-task-execution-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "ecs-tasks.amazonaws.com" } }] }) } resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_policy" { role = aws_iam_role.ecs_task_execution_role.name policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" } resource "aws_iam_role_policy" "bedrock_policy" { name = "${var.app_name}-bedrock-policy" role = aws_iam_role.ecs_task_role.id policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Action = [ "bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream", "bedrock:GetInferenceProfile" ] Resource = "*" }] }) } resource "aws_iam_role_policy" "s3_policy" { name = "${var.app_name}-s3-policy" role = aws_iam_role.ecs_task_role.id policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Action = [ "s3:GetObject" ] Resource = "arn:aws:s3:::upflux-doc-analyzer/*" }] }) } resource "aws_iam_role_policy" "textract_policy" { name = "${var.app_name}-textract-policy" role = aws_iam_role.ecs_task_role.id policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Action = [ "textract:DetectDocumentText", "textract:StartDocumentTextDetection", "textract:GetDocumentTextDetection" ] Resource = "*" }] }) } # ECS Task Definition resource "aws_ecs_task_definition" "app" { family = var.app_name network_mode = "awsvpc" requires_compatibilities = ["FARGATE"] cpu = var.fargate_cpu memory = var.fargate_memory execution_role_arn = aws_iam_role.ecs_task_execution_role.arn task_role_arn = aws_iam_role.ecs_task_role.arn container_definitions = jsonencode([{ name = var.app_name image = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.aws_region}.amazonaws.com/${var.ecr_repository_name}:${var.image_tag}" portMappings = [{ containerPort = 8000 hostPort = 8000 protocol = "tcp" }] logConfiguration = { logDriver = "awslogs" options = { "awslogs-group" = aws_cloudwatch_log_group.app.name "awslogs-region" = var.aws_region "awslogs-stream-prefix" = "ecs" } } healthCheck = { command = ["CMD-SHELL", "curl -f http://localhost:8000/health || exit 1"] interval = 30 timeout = 5 retries = 3 startPeriod = 60 } }]) tags = { Name = "${var.app_name}-task" } } # ECS Service (tasks in private subnets) resource "aws_ecs_service" "app" { name = "${var.app_name}-service" cluster = aws_ecs_cluster.main.id task_definition = aws_ecs_task_definition.app.arn desired_count = var.app_count launch_type = "FARGATE" network_configuration { security_groups = [aws_security_group.ecs_tasks.id] subnets = var.private_subnet_ids # ECS tasks in private subnets assign_public_ip = false # No public IP needed with NAT gateway } load_balancer { target_group_arn = aws_lb_target_group.app.arn container_name = var.app_name container_port = 8000 } depends_on = [aws_lb_listener.app] tags = { Name = "${var.app_name}-service" } } #ECS Task Role (for application to call AWS services) resource "aws_iam_role" "ecs_task_role" { name = "${var.app_name}-ecs-task-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "ecs-tasks.amazonaws.com" } }] }) }