From 33e6eeb0b7b6dd9167caa29c61195a28809dbc53 Mon Sep 17 00:00:00 2001 From: BEW111 Date: Mon, 2 Sep 2024 15:14:47 -0400 Subject: [PATCH] add custom vpc, more vars --- infrastructure/backend/main.tf | 318 ++++++++++++++++++---------- infrastructure/backend/variables.tf | 4 + 2 files changed, 211 insertions(+), 111 deletions(-) diff --git a/infrastructure/backend/main.tf b/infrastructure/backend/main.tf index 70ba6fe2..f754bba4 100644 --- a/infrastructure/backend/main.tf +++ b/infrastructure/backend/main.tf @@ -26,10 +26,10 @@ resource "aws_ecs_cluster" "cluster" { # TODO: this only needs to be created once; should we keep it in here or create # it manually? -resource "aws_cloudwatch_log_group" "ecs_app_family_log_group" { - name = "/ecs/app-family" - // retention_in_days = 90 -} +# resource "aws_cloudwatch_log_group" "ecs_app_family_log_group" { +# name = "/ecs/app-family" +# // retention_in_days = 90 +# } resource "aws_ecs_task_definition" "app" { family = "app-family" @@ -53,11 +53,7 @@ resource "aws_ecs_task_definition" "app" { } ], environment = [ - // TODO: we may end up needing to have two env vars here, one for the - // alb url (for cors), and one for the redirect (the actual domain name) - // - Right now we're just using `FRONTEND_URL` in the same places in - // the backend - { "name" : "FRONTEND_URL", "value" : "https://hackboilerplate.com" }, + { "name" : "FRONTEND_URL", "value" : var.frontend_url }, { "name" : "ATLAS_URI", "value" : var.atlas_uri }, { "name" : "COOKIE_SECRET", "value" : var.cookie_secret }, { "name" : "SENDGRID_API_KEY", "value" : var.sendgrid_api_key }, @@ -75,31 +71,130 @@ resource "aws_ecs_task_definition" "app" { ]) } -// VPC configuration -// TODO: decide if we should use a different VPC -data "aws_vpc" "default" { - default = true +/* VPC CONFIGURATION */ +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" + enable_dns_hostnames = true + enable_dns_support = true + + tags = { + # TODO: set a variable to name this + Name = "hack-main-vpc" + } +} + +data "aws_availability_zones" "available" { + state = "available" +} + +resource "aws_subnet" "public" { + count = 2 + vpc_id = aws_vpc.main.id + cidr_block = "10.0.${count.index + 1}.0/24" + map_public_ip_on_launch = true + availability_zone = data.aws_availability_zones.available.names[count.index] + + tags = { + # TODO: set a variable to name these + Name = "Public Hack Project Subnet ${count.index + 1}" + } +} + +resource "aws_internet_gateway" "main" { + vpc_id = aws_vpc.main.id + + tags = { + Name = "main-igw" + } +} + +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 = "public-rt" + } +} + +resource "aws_route_table_association" "public" { + count = 2 + subnet_id = aws_subnet.public[count.index].id + route_table_id = aws_route_table.public.id } -data "aws_security_group" "default" { - vpc_id = data.aws_vpc.default.id - name = "default" +resource "aws_security_group" "alb_sg" { + # TODO: set a better name for this (e.g. based on a variable name) + name = "alb-sg" + description = "Security group for ALB" + vpc_id = aws_vpc.main.id + + ingress { + description = "HTTP from anywhere" + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + description = "HTTPS from anywhere" + from_port = 443 + to_port = 443 + 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"] + } + + tags = { + Name = "alb-sg" + } } -data "aws_subnets" "default" { - filter { - name = "vpc-id" - values = [data.aws_vpc.default.id] +resource "aws_security_group" "ecs_sg" { + # TODO: set a better name for this (e.g. based on a variable) + name = "hack-project-ecs-sg" + description = "Security group for ECS tasks" + vpc_id = aws_vpc.main.id + + ingress { + description = "Allow traffic from ALB" + from_port = 4000 + to_port = 4000 + 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"] + } + + tags = { + Name = "ecs-sg" } } -// Load balancer configuration + +/* Load balancer configuration */ resource "aws_lb" "app" { name = "${var.cluster_name}-alb" internal = false load_balancer_type = "application" - security_groups = [data.aws_security_group.default.id] - subnets = data.aws_subnets.default.ids + security_groups = [aws_security_group.alb_sg.id] + subnets = aws_subnet.public[*].id enable_deletion_protection = false } @@ -108,7 +203,7 @@ resource "aws_lb_target_group" "backend_tg" { name = "${var.cluster_name}-backend-tg" port = 4000 protocol = "HTTP" - vpc_id = data.aws_vpc.default.id + vpc_id = aws_vpc.main.id target_type = "ip" health_check { @@ -152,36 +247,36 @@ resource "aws_lb_listener_rule" "backend" { } } -resource "aws_lb_listener" "app_listener_secure" { - load_balancer_arn = aws_lb.app.arn - port = "443" - protocol = "HTTPS" - - default_action { - type = "fixed-response" - fixed_response { - content_type = "text/plain" - message_body = "Not Found" - status_code = "404" - } - } -} - -resource "aws_lb_listener_rule" "backend_secure" { - listener_arn = aws_lb_listener.app_listener_secure.arn - priority = 200 - - action { - type = "forward" - target_group_arn = aws_lb_target_group.backend_tg.arn - } - - condition { - path_pattern { - values = ["/api/*"] - } - } -} +# resource "aws_lb_listener" "app_listener_secure" { +# load_balancer_arn = aws_lb.app.arn +# port = "443" +# protocol = "HTTPS" + +# default_action { +# type = "fixed-response" +# fixed_response { +# content_type = "text/plain" +# message_body = "Not Found" +# status_code = "404" +# } +# } +# } + +# resource "aws_lb_listener_rule" "backend_secure" { +# listener_arn = aws_lb_listener.app_listener_secure.arn +# priority = 200 + +# action { +# type = "forward" +# target_group_arn = aws_lb_target_group.backend_tg.arn +# } + +# condition { +# path_pattern { +# values = ["/api/*"] +# } +# } +# } // ECS service resource "aws_ecs_service" "app_service" { @@ -192,8 +287,9 @@ resource "aws_ecs_service" "app_service" { launch_type = "FARGATE" network_configuration { - subnets = data.aws_subnets.default.ids + subnets = aws_subnet.public[*].id assign_public_ip = true + security_groups = [aws_security_group.ecs_sg.id] } load_balancer { @@ -203,71 +299,71 @@ resource "aws_ecs_service" "app_service" { } } - -# Get existing IAM info -resource "aws_iam_role" "ecs_task_execution_role" { +/* 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 = [ +# { +# Action = "sts:AssumeRole", +# Effect = "Allow", +# Principal = { +# Service = "ecs-tasks.amazonaws.com" +# } +# } +# ] +# }) +# } + +# TODO: since the role/policy do not get deleted, these need to get created separately at some point +# so they can be referenced here as `data` resources. we should decide where we actually do +# create them +data "aws_iam_role" "ecs_task_execution_role" { 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" - } - } - ] - }) +data "aws_iam_policy" "cloudwatch_logs_policy" { + arn = "arn:aws:iam::${var.aws_account_id}:policy/ECSLogsPolicy" } resource "aws_iam_role_policy_attachment" "ecs_task_execution_policy_attachment" { - role = aws_iam_role.ecs_task_execution_role.name + role = data.aws_iam_role.ecs_task_execution_role.name policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" } -resource "aws_iam_policy" "cloudwatch_logs_policy" { - name = "ECSLogsPolicy" - description = "Allow ECS Task Execution Role to push logs to CloudWatch" - - policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - { - Effect = "Allow", - Action = [ - "logs:CreateLogStream", - "logs:CreateLogGroup" - ], - Resource = "arn:aws:logs:*:*:*" - }, - { - Effect = "Allow", - Action = [ - "logs:PutLogEvents" - ], - Resource = [ - "arn:aws:logs:*:*:log-group:/ecs/*:log-stream:*", - "arn:aws:logs:*:*:log-group:/ecs/*" - ] - } - ] - }) -} +# resource "aws_iam_policy" "cloudwatch_logs_policy" { +# name = "ECSLogsPolicy" +# description = "Allow ECS Task Execution Role to push logs to CloudWatch" + +# policy = jsonencode({ +# Version = "2012-10-17", +# Statement = [ +# { +# Effect = "Allow", +# Action = [ +# "logs:CreateLogStream", +# "logs:CreateLogGroup" +# ], +# Resource = "arn:aws:logs:*:*:*" +# }, +# { +# Effect = "Allow", +# Action = [ +# "logs:PutLogEvents" +# ], +# Resource = [ +# "arn:aws:logs:*:*:log-group:/ecs/*:log-stream:*", +# "arn:aws:logs:*:*:log-group:/ecs/*" +# ] +# } +# ] +# }) +# } resource "aws_iam_role_policy_attachment" "cloudwatch_logs_policy_attachment" { - role = aws_iam_role.ecs_task_execution_role.name - policy_arn = aws_iam_policy.cloudwatch_logs_policy.arn + role = data.aws_iam_role.ecs_task_execution_role.name + policy_arn = data.aws_iam_policy.cloudwatch_logs_policy.arn } - -data "aws_iam_role" "ecs_task_execution_role" { - name = "ecs_task_execution_role" - depends_on = [aws_iam_role.ecs_task_execution_role] -} - -data "aws_iam_policy" "cloudwatch_logs_policy" { - arn = "arn:aws:iam::${var.aws_account_id}:policy/ECSLogsPolicy" - depends_on = [aws_iam_policy.cloudwatch_logs_policy] -} diff --git a/infrastructure/backend/variables.tf b/infrastructure/backend/variables.tf index 1d092c3c..8a0a5e94 100644 --- a/infrastructure/backend/variables.tf +++ b/infrastructure/backend/variables.tf @@ -45,3 +45,7 @@ variable "sendgrid_api_key" { variable "sendgrid_email_address" { type = string } + +variable "frontend_url" { + type = string +}