Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions infra/deploy/database.tf
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ resource "aws_security_group" "rds" {
protocol = "tcp"
from_port = 5432
to_port = 5432

security_groups = [
aws_security_group.ecs_service.id
]
}

tags = {
Expand Down
202 changes: 202 additions & 0 deletions infra/deploy/ecs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
##########################################
# ECS Cluster for running app on Fargate #
##########################################

resource "aws_iam_policy" "task_execution_role_policy" {
name = "${local.prefix}-task-exec-role-policy"
path = "/"
description = "Allow ECS to retrieve images and add to logs."
policy = file("./templates/ecs/task-execution-role-policy.json")
}

resource "aws_iam_role" "task_execution_role" {
name = "${local.prefix}-task-execution-role"
assume_role_policy = file("./templates/ecs/task-assume-role-policy.json")
}

resource "aws_iam_role_policy_attachment" "task_execution_role" {
role = aws_iam_role.task_execution_role.name
policy_arn = aws_iam_policy.task_execution_role_policy.arn
}

resource "aws_iam_role" "app_task" {
name = "${local.prefix}-app-task"
assume_role_policy = file("./templates/ecs/task-assume-role-policy.json")
}

resource "aws_iam_policy" "task_ssm_policy" {
name = "${local.prefix}-task-ssm-role-policy"
path = "/"
description = "Policy to allow System Manager to execute in container"
policy = file("./templates/ecs/task-ssm-policy.json")
}

resource "aws_iam_role_policy_attachment" "task_ssm_policy" {
role = aws_iam_role.app_task.name
policy_arn = aws_iam_policy.task_ssm_policy.arn
}

resource "aws_cloudwatch_log_group" "ecs_task_logs" {
name = "${local.prefix}-api"
}

resource "aws_ecs_cluster" "main" {
name = "${local.prefix}-cluster"
}

resource "aws_ecs_task_definition" "api" {
family = "${local.prefix}-api"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = 256
memory = 512
execution_role_arn = aws_iam_role.task_execution_role.arn
task_role_arn = aws_iam_role.app_task.arn

container_definitions = jsonencode([
{
name = "api"
image = var.ecr_app_image
essential = true
memoryReservation = 256
user = "django-user"
environment = [
{
name = "DJANGO_SECRET_KEY"
value = var.django_secret_key
},
{
name = "DB_HOST"
value = aws_db_instance.main.address
},
{
name = "DB_NAME"
value = aws_db_instance.main.db_name
},
{
name = "DB_USER"
value = aws_db_instance.main.username
},
{
name = "DB_PASS"
value = aws_db_instance.main.password
},
{
name = "ALLOWED_HOSTS"
value = "*"
}
]
mountPoints = [
{
readOnly = false
containerPath = "/vol/web/static"
sourceVolume = "static"
}
],
logConfiguration = {
logDriver = "awslogs"
options = {
awslogs-group = aws_cloudwatch_log_group.ecs_task_logs.name
awslogs-region = data.aws_region.current.name
awslogs-stream-prefix = "api"
}
}
},
{
name = "proxy"
image = var.ecr_proxy_image
essential = true
memoryReservation = 256
user = "nginx"
portMappings = [
{
containerPort = 8000
hostPort = 8000
}
]
environment = [
{
name = "APP_HOST"
value = "127.0.0.1"
}
]
mountPoints = [
{
readOnly = true
containerPath = "/vol/static"
sourceVolume = "static"
}
]
logConfiguration = {
logDriver = "awslogs"
options = {
awslogs-group = aws_cloudwatch_log_group.ecs_task_logs.name
awslogs-region = data.aws_region.current.name
awslogs-stream-prefix = "proxy"
}
}
}
])

volume {
name = "static"
}

runtime_platform {
operating_system_family = "LINUX"
cpu_architecture = "X86_64"
}
}

resource "aws_security_group" "ecs_service" {
description = "Access rules for the ECS service."
name = "${local.prefix}-ecs-service"
vpc_id = aws_vpc.main.id

# Outbound access to endpoints
egress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

# RDS Connectivity
egress {
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = [
aws_subnet.private_a.cidr_block,
aws_subnet.private_b.cidr_block,
]
}

ingress {
from_port = 8000
to_port = 8000
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}

resource "aws_ecs_service" "api" {
name = "${local.prefix}-api"
cluster = aws_ecs_cluster.main.name
task_definition = aws_ecs_task_definition.api.family
desired_count = 1
launch_type = "FARGATE"
platform_version = "1.4.0"
enable_execute_command = true

network_configuration {
assign_public_ip = true

subnets = [
aws_subnet.public_a.id,
aws_subnet.public_b.id
]

security_groups = [aws_security_group.ecs_service.id]
}
}
13 changes: 13 additions & 0 deletions infra/deploy/templates/ecs/task-assume-role-policy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Action": ["sts:AssumeRole"]
}
]
}
17 changes: 17 additions & 0 deletions infra/deploy/templates/ecs/task-execution-role-policy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
15 changes: 15 additions & 0 deletions infra/deploy/templates/ecs/task-ssm-policy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssmmessages:CreateControlChannel",
"ssmmessages:CreateDataChannel",
"ssmmessages:OpenControlChannel",
"ssmmessages:OpenDataChannel"
],
"Resource": "*"
}
]
}
12 changes: 12 additions & 0 deletions infra/deploy/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,16 @@ variable "db_username" {

variable "db_password" {
description = "Password for the terraform database"
}

variable "ecr_proxy_image" {
description = "Path to the ECR repo with the proxy image"
}

variable "ecr_app_image" {
description = "Path to the ECR repo with the API image"
}

variable "django_secret_key" {
description = "Secret key for Django"
}
5 changes: 4 additions & 1 deletion infra/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ services:
- AWS_SESSION_TOKEN=${AWS_SESSION_TOKEN}
- AWS_DEFAULT_REGION=us-east-1
- TF_WORKSPACE=${TF_WORKSPACE}
- TF_VAR_db_password=${TF_VAR_db_password}
- TF_VAR_db_password=${TF_VAR_db_password}
- TF_VAR_django_secret_key=${TF_VAR_django_secret_key}
- TF_VAR_ecr_proxy_image=${TF_VAR_ecr_proxy_image}
- TF_VAR_ecr_app_image=${TF_VAR_ecr_app_image}
Loading