diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..8ce0778a0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Terraform +terraform.tfstate +terraform.tfstate.* +.terraform/ + diff --git a/terraform/aws/ecr.tf b/terraform/aws/ecr.tf new file mode 100644 index 000000000..dde933ed0 --- /dev/null +++ b/terraform/aws/ecr.tf @@ -0,0 +1,16 @@ +# ----------------------- +# ECR Repository for Backend +# ----------------------- +resource "aws_ecr_repository" "backend_repo" { + name = "devops-backend" + image_tag_mutability = "MUTABLE" + + image_scanning_configuration { + scan_on_push = true + } + + tags = { + Name = "devops-backend-repo" + } +} + diff --git a/terraform/aws/ecs.tf b/terraform/aws/ecs.tf new file mode 100644 index 000000000..25fb8e615 --- /dev/null +++ b/terraform/aws/ecs.tf @@ -0,0 +1,61 @@ +# ----------------------- +# ECS Cluster +# ----------------------- +resource "aws_ecs_cluster" "this" { + name = "${var.app_name}-cluster" +} + +# ----------------------- +# CloudWatch Log Group +# ----------------------- +resource "aws_cloudwatch_log_group" "ecs" { + name = "/ecs/${var.app_name}" + retention_in_days = 7 +} + +# ----------------------- +# ECS Task Definition +# ----------------------- +resource "aws_ecs_task_definition" "this" { + family = var.app_name + network_mode = "awsvpc" + requires_compatibilities = ["FARGATE"] + cpu = var.container_cpu + memory = var.container_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 = "${aws_ecr_repository.backend_repo.repository_url}:latest" + essential = true + + portMappings = [ + { + containerPort = var.container_port + hostPort = var.container_port + protocol = "tcp" + } + ] + + secrets = [ + { + name = "APP_SECRET" + valueFrom = aws_secretsmanager_secret.app_secret.arn + } + ] + + logConfiguration = { + logDriver = "awslogs" + options = { + awslogs-group = aws_cloudwatch_log_group.ecs.name + awslogs-region = var.aws_region + awslogs-stream-prefix = "ecs" + } + } + } + ]) +} + diff --git a/terraform/aws/iam.tf b/terraform/aws/iam.tf new file mode 100644 index 000000000..0a3ed7112 --- /dev/null +++ b/terraform/aws/iam.tf @@ -0,0 +1,66 @@ +# ----------------------- +# ECS Task Execution Role +# ----------------------- +resource "aws_iam_role" "ecs_task_execution_role" { + name = "ecs-task-execution-role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Principal = { + Service = "ecs-tasks.amazonaws.com" + } + Action = "sts:AssumeRole" + }] + }) +} + +# Attach AWS managed policy (ECR + CloudWatch) +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" +} + +# ----------------------- +# ECS Task Role (Application Role) +# ----------------------- +resource "aws_iam_role" "ecs_task_role" { + name = "ecs-task-role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Principal = { + Service = "ecs-tasks.amazonaws.com" + } + Action = "sts:AssumeRole" + }] + }) +} + +# ----------------------- +# Custom policy for Secrets Manager access +# ----------------------- +resource "aws_iam_policy" "secrets_manager_policy" { + name = "ecs-secrets-manager-policy" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Action = [ + "secretsmanager:GetSecretValue" + ] + Resource = "*" + }] + }) +} + +# Attach custom policy to ECS Task Role +resource "aws_iam_role_policy_attachment" "ecs_task_role_secrets_policy" { + role = aws_iam_role.ecs_task_role.name + policy_arn = aws_iam_policy.secrets_manager_policy.arn +} +