-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ECS infrastructure with the Flagsmith task definition (#4)
* ecs infra with flagsmith task * readme cosmetic change * run terraform fmt * general clean up, tightened IAM permissions, ALB listener & SG fix, Postgress 14 * set Fargate defaults, Django application settings
- Loading branch information
1 parent
1063b38
commit c38d6f9
Showing
21 changed files
with
716 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# Flagsmith AWS Starter kit | ||
|
||
## Deploying Flagsmith to AWS ECS (with whitenoise) | ||
|
||
### This is an example, how Flagsmith can be hosted, | ||
### This is not production ready solution | ||
|
||
## AWS infrastructure consists of: | ||
- IAM | ||
- Route53 subdomain | ||
- Networking: | ||
- VPC | ||
- Public and private subnets | ||
- Routing tables | ||
- Internet Gateway | ||
- Security Groups | ||
- Load Balancers, Listeners, and Target Groups | ||
- IAM Roles and Policies | ||
- ECS: | ||
- Task Definition with flagsmith:latest image | ||
- Cluster | ||
- Service | ||
- RDS | ||
- Secrets with Parameter Store | ||
- Logs | ||
|
||
## How to use this project | ||
|
||
1. Register AWS Route53 Hosted Zone \ | ||
for example ```yourdomain.com``` | ||
![Route53 hosted zone](img/route53.png) | ||
2. Generate certificate with AWS Certificate Manager \ | ||
with ```*.yourdomain.com``` pattern | ||
![Certificate](img/AWS_certificate_manager.png) | ||
3. Define your variables like hosted zone domain and more desired settings in **terraform.tfvars** file | ||
```bash | ||
route53_hosted_zone = "yourdomain.com" | ||
app_name = "flagsmith" | ||
app_environment = "dev" | ||
region = "eu-central-1" | ||
allowed_hosts = "*" | ||
``` | ||
|
||
Currently containers are run on Fargate SPOT instances: | ||
ecs_service.tf: | ||
```bash | ||
capacity_provider_strategy { | ||
capacity_provider = "FARGATE_SPOT" | ||
weight = 100 | ||
} | ||
capacity_provider_strategy { | ||
capacity_provider = "FARGATE" | ||
weight = 0 | ||
} | ||
``` | ||
4. Generate plan for infrastructure ```terraform plan -out flagsmith.tfplan``` | ||
5. Apply infrastructure by running ```terraform apply flagsmith.tfplan``` | ||
|
||
After a few minutes flagsmith will be available at your domain | ||
|
||
![Flagsmith online](img/flagsmith.png) | ||
|
||
|
||
you can delete a whole setup by running ```terraform destroy --auto-approve``` |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
terraform { | ||
required_version = ">=1.3.6" | ||
|
||
required_providers { | ||
aws = { | ||
source = "hashicorp/aws" | ||
version = "~>4.63.0" | ||
} | ||
random = { | ||
source = "hashicorp/random" | ||
version = "~> 3.5.1" | ||
} | ||
} | ||
} | ||
|
||
provider "aws" { | ||
region = var.region | ||
|
||
default_tags { | ||
tags = { | ||
Environment = var.app_environment | ||
Project = var.app_name | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# POC VPC | ||
|
||
data "aws_availability_zones" "available" { | ||
state = "available" | ||
} | ||
|
||
locals { | ||
azs = slice(data.aws_availability_zones.available.names, 0, 2) | ||
|
||
} | ||
|
||
module "vpc" { | ||
# https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/latest | ||
source = "terraform-aws-modules/vpc/aws" | ||
version = "3.18.1" | ||
|
||
name = "${var.app_name}-${var.app_environment}-vpc" | ||
cidr = var.vpc_cidr | ||
azs = local.azs | ||
private_subnets = [for k, v in local.azs : cidrsubnet(var.vpc_cidr, 8, k + 10)] | ||
public_subnets = [for k, v in local.azs : cidrsubnet(var.vpc_cidr, 8, k + 1)] | ||
|
||
enable_nat_gateway = true | ||
single_nat_gateway = true | ||
one_nat_gateway_per_az = false | ||
enable_dns_support = true | ||
enable_dns_hostnames = true | ||
|
||
|
||
# for auto service discovery | ||
# tags = { | ||
|
||
# } | ||
|
||
# public_subnet_tags = { | ||
|
||
# } | ||
|
||
# private_subnet_tags = { | ||
|
||
# } | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# ALB Security Group (Traffic Internet -> ALB) | ||
resource "aws_security_group" "load-balancer" { | ||
name = "load_balancer_security_group" | ||
description = "Controls access to the ALB" | ||
vpc_id = module.vpc.vpc_id | ||
|
||
# used for redirection to https | ||
ingress { | ||
from_port = 80 | ||
to_port = 80 | ||
protocol = "tcp" | ||
cidr_blocks = ["0.0.0.0/0"] | ||
} | ||
|
||
ingress { | ||
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"] | ||
} | ||
} | ||
|
||
# ECS Security group (traffic ALB -> ECS) | ||
resource "aws_security_group" "ecs" { | ||
name = "ecs_security_group" | ||
description = "Allows inbound access from the ALB only" | ||
vpc_id = module.vpc.vpc_id | ||
|
||
ingress { | ||
from_port = 8000 | ||
to_port = 8000 | ||
protocol = "tcp" | ||
security_groups = [aws_security_group.load-balancer.id] | ||
} | ||
|
||
egress { | ||
from_port = 0 | ||
to_port = 0 | ||
protocol = "-1" | ||
cidr_blocks = ["0.0.0.0/0"] | ||
} | ||
} | ||
|
||
# RDS Security Group (traffic ECS -> RDS) | ||
resource "aws_security_group" "rds" { | ||
name = "rds-security-group" | ||
description = "Allows inbound access from ECS only" | ||
vpc_id = module.vpc.vpc_id | ||
|
||
ingress { | ||
protocol = "tcp" | ||
from_port = "5432" | ||
to_port = "5432" | ||
security_groups = [aws_security_group.ecs.id] | ||
} | ||
|
||
egress { | ||
protocol = "-1" | ||
from_port = 0 | ||
to_port = 0 | ||
cidr_blocks = ["0.0.0.0/0"] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
data "aws_acm_certificate" "certificate" { | ||
domain = "*.${data.aws_route53_zone.selected.name}" | ||
statuses = ["ISSUED"] | ||
} | ||
|
||
# Load Balancer | ||
resource "aws_lb" "ecs-alb" { | ||
name = "${local.ecs_cluster_name}-alb" | ||
load_balancer_type = "application" | ||
internal = false | ||
security_groups = [aws_security_group.load-balancer.id] | ||
subnets = module.vpc.public_subnets | ||
} | ||
|
||
# Target group | ||
resource "aws_alb_target_group" "default-target-group" { | ||
name = "${var.app_environment}-${var.app_name}-tg" | ||
port = 8000 | ||
protocol = "HTTP" | ||
vpc_id = module.vpc.vpc_id | ||
target_type = "ip" | ||
|
||
health_check { | ||
path = var.health_check_path | ||
port = "traffic-port" | ||
healthy_threshold = 5 | ||
unhealthy_threshold = 2 | ||
timeout = 2 | ||
interval = 5 | ||
matcher = "200" | ||
} | ||
} | ||
|
||
# Listener, redirects HTTP to HTTPS | ||
resource "aws_alb_listener" "ecs-alb-http-listener" { | ||
load_balancer_arn = aws_lb.ecs-alb.id | ||
port = "80" | ||
protocol = "HTTP" | ||
|
||
default_action { | ||
type = "redirect" | ||
|
||
redirect { | ||
port = "443" | ||
protocol = "HTTPS" | ||
status_code = "HTTP_301" | ||
} | ||
} | ||
} | ||
|
||
# Listener (redirects traffic from the load balancer to the target group) | ||
# Check for latest ssl policy https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html#describe-ssl-policies | ||
resource "aws_alb_listener" "ecs-alb-https-listener" { | ||
load_balancer_arn = aws_lb.ecs-alb.id | ||
port = "443" | ||
protocol = "HTTPS" | ||
ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06" | ||
certificate_arn = data.aws_acm_certificate.certificate.arn | ||
depends_on = [aws_alb_target_group.default-target-group] | ||
|
||
default_action { | ||
type = "fixed-response" | ||
|
||
fixed_response { | ||
content_type = "text/plain" | ||
message_body = "Bad Request" | ||
status_code = "400" | ||
} | ||
} | ||
|
||
} | ||
# matches header with configured flagsmith subdomain | ||
resource "aws_alb_listener_rule" "host_header" { | ||
listener_arn = aws_alb_listener.ecs-alb-https-listener.arn | ||
priority = 10 | ||
|
||
condition { | ||
host_header { | ||
values = ["${var.app_name}.${data.aws_route53_zone.selected.name}"] | ||
} | ||
} | ||
action { | ||
type = "forward" | ||
target_group_arn = aws_alb_target_group.default-target-group.arn | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
resource "aws_iam_role" "ecs_host_role" { | ||
name = "${var.app_name}-ecs-host-role-${var.app_environment}" | ||
assume_role_policy = file("policies/ecs-task-role.json") | ||
} | ||
|
||
resource "aws_iam_role_policy" "ecs-host-role-policy" { | ||
name = "${var.app_name}-ecs-host-role-policy" | ||
|
||
policy = jsonencode({ | ||
"Version" : "2012-10-17", | ||
"Statement" : [ | ||
{ | ||
"Effect" : "Allow", | ||
"Action" : [ | ||
"ecr:BatchCheckLayerAvailability", | ||
"ecr:GetDownloadUrlForLayer", | ||
"ecr:BatchGetImage", | ||
"cloudwatch:PutMetricData", | ||
"logs:CreateLogStream", | ||
"logs:PutLogEvents", | ||
], | ||
"Resource" : "*" | ||
}, | ||
{ | ||
Effect = "Allow" | ||
Action = ["ssm:GetParameters"], | ||
Resource = ["arn:aws:ssm:${var.region}:${local.AWS_ACCOUNT_ID}:parameter/${var.app_name}/*"] | ||
} | ||
] | ||
} | ||
) | ||
role = aws_iam_role.ecs_host_role.id | ||
} | ||
|
||
resource "aws_iam_role" "ecs_task" { | ||
name = "${var.app_name}-ecs-task" | ||
assume_role_policy = file("policies/ecs-task-role.json") | ||
} | ||
|
||
resource "aws_iam_role_policy" "ecs_task" { | ||
name = "${var.app_name}-ecs-task-policy" | ||
policy = jsonencode({ | ||
Version = "2012-10-17" | ||
Statement = [ | ||
{ | ||
Effect = "Allow" | ||
Action = ["ssm:GetParameters"], | ||
Resource = ["arn:aws:ssm:${var.region}:${local.AWS_ACCOUNT_ID}:parameter/${var.app_name}/*"] | ||
} | ||
] | ||
}) | ||
role = aws_iam_role.ecs_task.id | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
resource "aws_cloudwatch_log_group" "django-log-group" { | ||
name = "/ecs/flagsmith" | ||
retention_in_days = var.log_retention_in_days | ||
} | ||
|
||
resource "aws_cloudwatch_log_stream" "django-log-stream" { | ||
name = "flagsmith-log-stream" | ||
log_group_name = aws_cloudwatch_log_group.django-log-group.name | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
data "aws_route53_zone" "selected" { | ||
name = var.route53_hosted_zone | ||
private_zone = false | ||
} | ||
|
||
resource "aws_route53_record" "subdomain" { | ||
zone_id = data.aws_route53_zone.selected.zone_id | ||
name = "${var.app_name}.${data.aws_route53_zone.selected.name}" | ||
type = "A" | ||
|
||
alias { | ||
name = aws_lb.ecs-alb.dns_name | ||
zone_id = aws_lb.ecs-alb.zone_id | ||
evaluate_target_health = true | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
resource "aws_ecs_cluster" "flagsmith" { | ||
name = local.ecs_cluster_name | ||
} |
Oops, something went wrong.