Skip to content

Commit 0eb163b

Browse files
authored
feat: add AWS terraform deployment config files
* feat: add middle eastern/ north african as a new ethnicity to data * feat:add terraform config files * fix: fix port assignments in ecs module * Update img name in container definition * feat: get mongo password from docuemntdb module and pass it to ecs contianer definition, module cleanup * fix: fix mismatch between ECS and ALB health check endpoints * feat: update health check path * fead: pass allow_all_hosts env var to container definition * update mongo_params value passed to container definition * update: update passwords format passed to container definition * feat: pass mongo_uri env variable to container definition from module documentdb * remove unused mongo env variables * feat: add dummy timestamp env variable to container definition * feat: add comments * delete tf plan * Revert "feat: add middle eastern/ north african as a new ethnicity to data" This reverts commit 9f3deb6.
1 parent c55a727 commit 0eb163b

File tree

16 files changed

+785
-0
lines changed

16 files changed

+785
-0
lines changed

terraform/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.terraform/
2+
.terraform.lock.hcl

terraform/documentdb/main.tf

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# DocumentDB Password
2+
resource "random_password" "bwwc_documentdb_password" {
3+
length = 20
4+
special = false
5+
min_special = 0
6+
}
7+
8+
9+
# Store the generated password in AWS Secrets Manager
10+
resource "aws_secretsmanager_secret" "bwwc_documentdb_password_secret" {
11+
name = "bwwc-documentdb-password"
12+
}
13+
14+
# Save the secret value as a new version in Secrets Manager
15+
resource "aws_secretsmanager_secret_version" "documentdb_password_secret_value" {
16+
secret_id = aws_secretsmanager_secret.bwwc_documentdb_password_secret.id
17+
secret_string = jsonencode({
18+
password = random_password.bwwc_documentdb_password.result
19+
})
20+
}
21+
22+
# DocumentDB Subnet Group
23+
resource "aws_docdb_subnet_group" "bwwc_documentdb_subnet_group" {
24+
name = "bwwc-documentdb-subnet-group"
25+
description = "Subnet group for bwwc API DocumentDB Cluster"
26+
subnet_ids = var.private_subnet_ids
27+
}
28+
29+
# DocumentDB Security Group
30+
resource "aws_security_group" "bwwc_documentdb_sg" {
31+
name = "bwwc-documentdb-sg"
32+
description = "Security Group for bwwc API DocumentDB Cluster"
33+
vpc_id = var.vpc_id
34+
35+
ingress {
36+
from_port = 27017
37+
to_port = 27017
38+
protocol = "tcp"
39+
cidr_blocks = ["10.0.0.0/16"]
40+
}
41+
42+
egress {
43+
from_port = 0
44+
to_port = 0
45+
protocol = "-1"
46+
cidr_blocks = ["0.0.0.0/0"]
47+
}
48+
}
49+
50+
# DocumentDB Cluster
51+
resource "aws_docdb_cluster" "bwwc_documentdb" {
52+
enabled_cloudwatch_logs_exports = ["audit"]
53+
deletion_protection = true
54+
cluster_identifier = "bwwc-documentdb"
55+
engine = "docdb"
56+
apply_immediately = true
57+
skip_final_snapshot = true
58+
engine_version = "5.0.0"
59+
master_username = "bwwc"
60+
61+
# Use password stored in Secrets Manager
62+
master_password = jsondecode(aws_secretsmanager_secret_version.documentdb_password_secret_value.secret_string).password
63+
64+
db_subnet_group_name = aws_docdb_subnet_group.bwwc_documentdb_subnet_group.name
65+
vpc_security_group_ids = [aws_security_group.bwwc_documentdb_sg.id]
66+
allow_major_version_upgrade = true
67+
storage_encrypted = true
68+
db_cluster_parameter_group_name = aws_docdb_cluster_parameter_group.bwwc_group.name
69+
}
70+
71+
# Cluster instances
72+
resource "aws_docdb_cluster_instance" "bwwc_documentdb" {
73+
count = 2
74+
# Setting promotion tier to 0 makes the instance eligible to become a writer.
75+
promotion_tier = 0
76+
cluster_identifier = aws_docdb_cluster.bwwc_documentdb.cluster_identifier
77+
identifier_prefix = "${aws_docdb_cluster.bwwc_documentdb.cluster_identifier}-"
78+
instance_class = "db.t3.medium"
79+
engine = aws_docdb_cluster.bwwc_documentdb.engine
80+
depends_on = [aws_docdb_cluster.bwwc_documentdb]
81+
}
82+
83+
# Custom DocumentDB group for custom settings
84+
resource "aws_docdb_cluster_parameter_group" "bwwc_group" {
85+
family = "docdb5.0"
86+
name = "bwwc-param-group"
87+
description = "docdb cluster parameter group"
88+
89+
parameter {
90+
name = "tls"
91+
value = "disabled"
92+
}
93+
}

terraform/documentdb/outputs.tf

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
output "mongo_host" {
2+
description = "The host (endpoint) of the MongoDB DocumentDB Cluster"
3+
value = aws_docdb_cluster.bwwc_documentdb.endpoint
4+
}
5+
6+
output "mongo_password" {
7+
description = "DocumentDB password retrieved from Secrets Manager"
8+
value = aws_secretsmanager_secret.bwwc_documentdb_password_secret.arn
9+
sensitive = true
10+
}
11+
12+
output "mongo_uri" {
13+
value = "mongodb://bwwc:${jsondecode(aws_secretsmanager_secret_version.documentdb_password_secret_value.secret_string).password}@${aws_docdb_cluster.bwwc_documentdb.endpoint}:27017/?retryWrites=false"
14+
sensitive = true
15+
}

terraform/documentdb/variables.tf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
variable "vpc_id" {
2+
description = "ID of the VPC where ECS resources will be deployed"
3+
type = string
4+
}
5+
6+
variable "private_subnet_ids" {
7+
description = "List of private subnet IDs for ECS services"
8+
type = list(string)
9+
}

terraform/ecs/main.tf

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
provider "aws" {
2+
region = "us-east-1"
3+
}
4+
5+
resource "aws_ecs_cluster" "bwwc_cluster" {
6+
name = "bwwc-cluster"
7+
}
8+
9+
# Create a CloudWatch Log Group for the backend service logs
10+
resource "aws_cloudwatch_log_group" "ecs_log_group_backend" {
11+
name = "/ecs/bwwc-backend"
12+
retention_in_days = 14
13+
}
14+
15+
# Define a custom IAM policy allowing ECS tasks to write logs to CloudWatch
16+
resource "aws_iam_policy" "cloudwatch_logs_policy" {
17+
name = "ecs-cloudwatch-logs-policy"
18+
description = "Policy to allow ECS tasks to write logs to CloudWatch"
19+
20+
policy = jsonencode({
21+
Version = "2012-10-17",
22+
Statement = [
23+
{
24+
Effect = "Allow",
25+
Action = [
26+
"logs:CreateLogStream",
27+
"logs:PutLogEvents"
28+
],
29+
Resource = [
30+
"${aws_cloudwatch_log_group.ecs_log_group_backend.arn}:*"
31+
]
32+
}
33+
]
34+
})
35+
}
36+
37+
# Attach the CloudWatch logs policy to the ECS task execution role
38+
resource "aws_iam_role_policy_attachment" "cloudwatch_logs_policy_attachment" {
39+
role = aws_iam_role.ecs_task_execution_role.name
40+
policy_arn = aws_iam_policy.cloudwatch_logs_policy.arn
41+
}
42+
43+
# Define the ECS Fargate task for the backend service
44+
resource "aws_ecs_task_definition" "backend" {
45+
family = "bwwc-backend"
46+
network_mode = "awsvpc"
47+
requires_compatibilities = ["FARGATE"]
48+
cpu = "512"
49+
memory = "1024"
50+
execution_role_arn = aws_iam_role.ecs_task_execution_role.arn
51+
52+
runtime_platform {
53+
cpu_architecture = "X86_64"
54+
}
55+
56+
container_definitions = jsonencode([
57+
{
58+
name = "bwwc-backend",
59+
image = "multiparty/bwwc-backend:main",
60+
memory = 512,
61+
cpu = 256,
62+
essential = true,
63+
portMappings = [{ containerPort = 8000, hostPort = 8000 }], #application is listening on port 8000
64+
healthCheck = {
65+
command = ["CMD-SHELL", "wget -q -O - http://localhost:8000/api/bwwc/healthz || exit 1"]
66+
interval = 30
67+
timeout = 5
68+
retries = 3
69+
startPeriod = 60
70+
},
71+
environment = [
72+
{ name = "SECRET_KEY", value = "django-insecure-x$ee)y$zn8!egy6(9olf6maf2tt0%wtn&qd_qzlo_v9f_-!5)@" },
73+
{ name = "BASE_URL", value = "http://${aws_lb.bwwc_lb.dns_name}" },
74+
{ name = "PRIME", value = "180252380737439" },
75+
{ name = "THRESHOLD", value = "3" },
76+
{ name = "POSTGRES_HOST", value = var.postgres_host },
77+
{ name = "POSTGRES_USERNAME", value = var.postgres_username },
78+
{ name = "POSTGRES_DATABASE", value = var.postgres_database },
79+
{ name = "POSTGRES_PORT", value = "5432" },
80+
{ name = "MONGO_URI", value = var.mongo_uri },
81+
{ name = "DJANGO_ALLOWED_HOSTS", value = "${aws_lb.bwwc_lb.dns_name},localhost,127.0.0.1" },
82+
{ name = "ALLOW_ALL_HOSTS", value = "true" },
83+
{ name = "DUMMY_TRIGGER", value = timestamp() }
84+
],
85+
secrets = [
86+
{ name = "POSTGRES_PASSWORD", valueFrom = "${var.postgres_password}:password::" }
87+
],
88+
logConfiguration = {
89+
logDriver = "awslogs"
90+
options = {
91+
"awslogs-group" = aws_cloudwatch_log_group.ecs_log_group_backend.name
92+
"awslogs-region" = "us-east-1"
93+
"awslogs-stream-prefix" = "bwwc-backend"
94+
}
95+
}
96+
}
97+
])
98+
}
99+
100+
# Create the ECS service to run the backend task
101+
resource "aws_ecs_service" "backend" {
102+
name = "bwwc-backend-service"
103+
cluster = aws_ecs_cluster.bwwc_cluster.id
104+
task_definition = aws_ecs_task_definition.backend.arn
105+
launch_type = "FARGATE"
106+
desired_count = 1
107+
108+
network_configuration {
109+
subnets = var.private_subnet_ids
110+
security_groups = [aws_security_group.fargate_sg.id]
111+
assign_public_ip = false
112+
}
113+
114+
load_balancer {
115+
target_group_arn = aws_lb_target_group.backend.arn
116+
container_name = "bwwc-backend"
117+
container_port = 8000 # Must match container definition's port above
118+
}
119+
120+
# Grace period to allow the container to pass health checks before being marked unhealthy
121+
health_check_grace_period_seconds = 60
122+
}
123+
124+
resource "aws_security_group" "lb_sg" {
125+
name = "lb-sg"
126+
description = "Security group for the ALB"
127+
vpc_id = var.vpc_id
128+
129+
ingress {
130+
description = "Allow HTTP traffic"
131+
from_port = 80
132+
to_port = 80
133+
protocol = "tcp"
134+
cidr_blocks = ["0.0.0.0/0"]
135+
}
136+
137+
ingress {
138+
description = "Allow HTTPS traffic"
139+
from_port = 443
140+
to_port = 443
141+
protocol = "tcp"
142+
cidr_blocks = ["0.0.0.0/0"] # Allow traffic from anywhere
143+
}
144+
145+
egress {
146+
description = "Allow all outbound traffic"
147+
from_port = 0
148+
to_port = 0
149+
protocol = "-1"
150+
cidr_blocks = ["0.0.0.0/0"]
151+
}
152+
}
153+
154+
# Security group for the ALB to allow HTTP and HTTPS from the internet
155+
resource "aws_security_group" "fargate_sg" {
156+
name = "fargate-sg"
157+
description = "Allow inbound traffic to backend only from ALB"
158+
vpc_id = var.vpc_id
159+
160+
ingress {
161+
from_port = 8000
162+
to_port = 8000
163+
protocol = "tcp"
164+
security_groups = [aws_security_group.lb_sg.id]
165+
}
166+
167+
egress {
168+
from_port = 0
169+
to_port = 0
170+
protocol = "-1"
171+
cidr_blocks = ["0.0.0.0/0"]
172+
}
173+
}
174+
175+
# Security group for the ECS tasks to allow traffic only from the ALB
176+
resource "aws_lb" "bwwc_lb" {
177+
name = "bwwc-lb"
178+
internal = false
179+
load_balancer_type = "application"
180+
security_groups = [aws_security_group.lb_sg.id]
181+
subnets = var.public_subnet_ids
182+
}
183+
184+
# Application Load Balancer for routing traffic to the ECS service
185+
resource "aws_lb_target_group" "backend" {
186+
name = "bwwc-backend-tg"
187+
port = 8000 # must match container definition port
188+
protocol = "HTTP"
189+
vpc_id = var.vpc_id
190+
target_type = "ip"
191+
192+
health_check {
193+
path = "/api/bwwc/healthz"
194+
interval = 30
195+
timeout = 5
196+
healthy_threshold = 2
197+
unhealthy_threshold = 2
198+
matcher = "200"
199+
}
200+
201+
lifecycle {
202+
create_before_destroy = true
203+
}
204+
}
205+
206+
# Listener to forward HTTP traffic from the ALB to the target group
207+
resource "aws_lb_listener" "backend" {
208+
load_balancer_arn = aws_lb.bwwc_lb.arn
209+
port = 80
210+
protocol = "HTTP"
211+
default_action {
212+
type = "forward"
213+
target_group_arn = aws_lb_target_group.backend.arn
214+
}
215+
}
216+
217+
# IAM policy allowing ECS tasks to retrieve secrets from Secrets Manager
218+
resource "aws_iam_policy" "secrets_access" {
219+
name = "ECSSecretsAccess"
220+
description = "Allow ECS tasks to retrieve secrets from AWS Secrets Manager"
221+
policy = jsonencode({
222+
Version = "2012-10-17",
223+
Statement = [
224+
{
225+
Effect = "Allow",
226+
Action = ["secretsmanager:GetSecretValue"],
227+
Resource = "arn:aws:secretsmanager:us-east-1:135854645631:secret:*"
228+
}
229+
]
230+
})
231+
}
232+
233+
# IAM role that ECS tasks assume to interact with AWS services
234+
resource "aws_iam_role" "ecs_task_execution_role" {
235+
name = "ecsTaskExecutionRole"
236+
237+
assume_role_policy = jsonencode({
238+
Version = "2012-10-17",
239+
Statement = [
240+
{
241+
Effect = "Allow",
242+
Principal = {
243+
Service = "ecs-tasks.amazonaws.com"
244+
},
245+
Action = "sts:AssumeRole"
246+
}
247+
]
248+
})
249+
}
250+
251+
# Attach AWS-managed ECS task execution policy to the execution role
252+
resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_policy" {
253+
role = aws_iam_role.ecs_task_execution_role.name
254+
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
255+
}
256+
257+
# Attach custom secrets access policy to the execution role
258+
resource "aws_iam_role_policy_attachment" "ecs_task_secrets" {
259+
role = aws_iam_role.ecs_task_execution_role.name
260+
policy_arn = aws_iam_policy.secrets_access.arn
261+
}

terraform/ecs/outputs.tf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
output "bwwc_lb_dns_name" {
2+
value = aws_lb.bwwc_lb.dns_name
3+
}

0 commit comments

Comments
 (0)