Skip to content

Commit 855e44a

Browse files
committed
build(tf): add terraform ecs
Signed-off-by: Jayne Doe <[email protected]>
1 parent b4ee2ac commit 855e44a

File tree

14 files changed

+229
-103
lines changed

14 files changed

+229
-103
lines changed

.github/workflows/aws.yml

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ on:
44
env:
55
required: true
66
type: string
7+
deploy_version:
8+
required: true
9+
type: string
710
secrets:
811
AWS_ACCESS_KEY_ID:
912
required: true
@@ -29,37 +32,19 @@ jobs:
2932
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
3033
aws-region: eu-west-2
3134

32-
- name: Login to Amazon ECR
33-
id: login-ecr
34-
uses: aws-actions/amazon-ecr-login@v1
35-
36-
- name: Build, tag, and push image to Amazon ECR
37-
id: build-image
38-
env:
39-
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
40-
ECR_REPOSITORY: j4numbers/personal-website
41-
IMAGE_TAG: ${{ github.sha }}
42-
run: |
43-
# Build a docker container and
44-
# push it to ECR so that it can
45-
# be deployed to ECS.
46-
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
47-
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
48-
echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
49-
5035
- name: HashiCorp - Setup Terraform
5136
uses: hashicorp/[email protected]
5237
with:
5338
# The version of Terraform CLI to install. Defaults to `latest`.
54-
terraform_version: "> 0.15.5"
39+
terraform_version: "> 1.1.0"
5540

5641
- name: Validate and plan deployment Terraform
5742
id: terraform-plan-validate
5843
env:
5944
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
6045
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
6146

62-
TF_VAR_deploy_version: ${{ github.sha }}
47+
TF_VAR_deploy_version: ${{ inputs.deploy_version }}
6348
TF_VAR_vpc_id: ${{ secrets.DEPLOY_VPC_ID }}
6449
TF_VAR_application_debug_mode: "false"
6550

.github/workflows/npm-build-and-test.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ jobs:
115115
npm run generate-certs
116116
npm run test:unit
117117
118-
deploy:
118+
deploy-ecr:
119119
if: github.ref_name == 'main'
120120
needs:
121121
- test
@@ -126,3 +126,15 @@ jobs:
126126
secrets:
127127
AWS_ACCESS_KEY_ID: ${{ secrets.aws_access_key_id }}
128128
AWS_SECRET_ACCESS_KEY: ${{ secrets.aws_secret_access_key }}
129+
130+
deploy-terraform:
131+
if: github.ref_name == 'main'
132+
needs:
133+
- deploy-ecr
134+
uses: ./.github/workflows/aws.yml
135+
with:
136+
env: production
137+
deploy_version: ${{ github.sha }}
138+
secrets:
139+
AWS_ACCESS_KEY_ID: ${{ secrets.aws_access_key_id }}
140+
AWS_SECRET_ACCESS_KEY: ${{ secrets.aws_secret_access_key }}

tf/site/asg.tf

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
resource "aws_launch_configuration" "website_asg_lc" {
2+
name = "${var.application_name}-asg-lc"
3+
image_id = data.aws_ami.base_ami.id
4+
iam_instance_profile = aws_iam_instance_profile.website_ecs_instance_profile.name
5+
security_groups = [aws_security_group.personal_website_sg.id]
6+
user_data = data.template_file.user_data.rendered
7+
instance_type = "t2.micro"
8+
key_name = "ssh-key-aws-override"
9+
}
10+
11+
resource "aws_autoscaling_group" "website_asg" {
12+
name = "${var.application_name}-asg"
13+
vpc_zone_identifier = data.aws_subnet_ids.root_vpc_subnets.ids
14+
launch_configuration = aws_launch_configuration.website_asg_lc.name
15+
16+
desired_capacity = 1
17+
max_size = 1
18+
min_size = 1
19+
20+
health_check_grace_period = 300
21+
health_check_type = "EC2"
22+
}

tf/site/assets/ecs/personal-website.json

Lines changed: 0 additions & 15 deletions
This file was deleted.

tf/site/assets/user_data.bash

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/bin/bash
2+
3+
echo "ECS_CLUSTER=${env}-${application_name}" >> /etc/ecs/ecs.config;
4+
echo "ECS_BACKEND_HOST=" >> /etc/ecs/ecs.config;

tf/site/cloudwatch.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
resource "aws_cloudwatch_log_group" "ecs_website_log_group" {
2+
name = "/ecs/${var.application_name}/logs"
3+
retention_in_days = 5
4+
}

tf/site/data.tf

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,49 @@
1+
data "aws_iam_policy_document" "base_ec2_policy" {
2+
statement {
3+
actions = ["sts:AssumeRole"]
4+
5+
principals {
6+
identifiers = ["ec2.amazonaws.com"]
7+
type = "Service"
8+
}
9+
}
10+
}
11+
12+
data "aws_iam_policy_document" "base_ecs_policy" {
13+
statement {
14+
actions = ["sts:AssumeRole"]
15+
16+
principals {
17+
identifiers = ["ecs-tasks.amazonaws.com"]
18+
type = "Service"
19+
}
20+
}
21+
}
22+
23+
data "aws_ecr_repository" "website_registry" {
24+
name = "j4numbers/personal-website"
25+
}
26+
27+
data "aws_ami" "base_ami" {
28+
owners = ["amazon"]
29+
30+
most_recent = true
31+
32+
filter {
33+
name = "AMI name"
34+
values = ["amzn2-ami-ecs*"]
35+
}
36+
}
37+
38+
data "template_file" "user_data" {
39+
template = file("${path.module}/assets/user_data.tmpl.bash")
40+
41+
vars = {
42+
env = local.dev-env
43+
application_name = var.application_name
44+
}
45+
}
46+
147
data "aws_vpc" "root_vpc" {
248
count = 1
349

@@ -8,17 +54,17 @@ data "aws_vpc" "root_vpc" {
854
}
955

1056
data "aws_subnet_ids" "root_vpc_subnets" {
11-
vpc_id = "${data.aws_vpc.root_vpc.id}"
57+
vpc_id = data.aws_vpc.root_vpc.id
1258
}
1359

1460
data "aws_route53_zone" "hosted_zone" {
15-
name = "${var.dns_hosted_zones[local.dev-env]}"
61+
name = var.dns_hosted_zones[local.dev-env]
1662
private_zone = false
1763
}
1864

1965
data "aws_subnet" "personal_website_subnet" {
2066
filter {
2167
name = "tag:Name"
22-
values = ["${var.subnet[local.dev-env]}"]
68+
values = [var.subnet[local.dev-env]]
2369
}
2470
}

tf/site/ecs.tf

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,52 @@
11
resource "aws_ecs_cluster" "personal_website_cluster" {
2-
name = "${local.dev-env}-personal-website"
2+
name = "${local.dev-env}-${var.application_name}"
33
}
44

55
resource "aws_ecs_task_definition" "personal_website_task" {
6-
container_definitions = "${file("./assets/ecs/personal-website.json")}"
6+
family = "${var.application_name}-definition"
77

8-
family = "service"
9-
network_mode = "host"
8+
container_definitions = jsonencode([
9+
{
10+
name: "${var.application_name}-service",
11+
image: "${data.aws_ecr_repository.website_registry.repository_url}:${var.deploy_version}",
12+
environment: [],
13+
secrets: [],
14+
logConfiguration: {
15+
"logDriver": "awslogs",
16+
"options": {
17+
"awslogs-group": "/ecs/${var.application_name}/logs",
18+
"awslogs-region": "eu-west-2",
19+
"awslogs-stream-prefix": "ecs"
20+
}
21+
},
22+
cpu : 10,
23+
memory : 512,
24+
essential : true,
25+
portMappings : [
26+
{
27+
"containerPort": 8080,
28+
"hostPort": 8080
29+
}
30+
]
31+
}
32+
])
33+
34+
network_mode = "host"
35+
requires_compatibilities = ["EC2"]
36+
execution_role_arn = aws_iam_role.website_ecs_task_executor.arn
1037
}
1138

1239
resource "aws_ecs_service" "personal_website_image" {
13-
name = "personal-website"
14-
cluster = "${aws_ecs_cluster.personal_website_cluster.id}"
15-
task_definition = "${aws_ecs_task_definition.personal_website_task.arn}"
40+
name = "${var.application_name}-service"
41+
cluster = aws_ecs_cluster.personal_website_cluster.arn
42+
task_definition = aws_ecs_task_definition.personal_website_task.family
43+
force_new_deployment = true
1644

1745
desired_count = 1
1846

1947
load_balancer {
20-
target_group_arn = "${aws_lb_target_group.central_lb_targets.arn}"
21-
container_name = "personal-website"
22-
container_port = "${var.application_port}"
48+
target_group_arn = aws_lb_target_group.central_lb_targets.arn
49+
container_name = var.application_name
50+
container_port = var.application_port
2351
}
2452
}

tf/site/elb.tf

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
resource "aws_acm_certificate" "central_lb_cert" {
2-
domain_name = "j4numbers.co.uk"
2+
domain_name = var.dns_hosted_zones[local.dev-env]
33
validation_method = "DNS"
44

55
lifecycle {
@@ -8,45 +8,41 @@ resource "aws_acm_certificate" "central_lb_cert" {
88
}
99

1010
resource "aws_lb" "central_load_balancer" {
11-
name = "${local.dev-env}-central-alb"
11+
name = "${local.dev-env}-${var.application_name}-central-alb"
1212
internal = false
1313
load_balancer_type = "application"
14-
security_groups = ["${aws_security_group.personal_website.id}"]
15-
subnets = ["${data.aws_subnet_ids.root_vpc_subnets.ids}"]
14+
security_groups = [aws_security_group.personal_website_sg.id]
15+
subnets = [data.aws_subnet_ids.root_vpc_subnets.ids]
1616

1717
enable_deletion_protection = false
1818
enable_http2 = true
19-
20-
tags {
21-
Environment = "${local.dev-env}"
22-
}
2319
}
2420

2521
resource "aws_lb_target_group" "central_lb_targets" {
26-
name = "${local.dev-env}-personal-website"
27-
port = "${var.application_port}"
22+
name = "${local.dev-env}-${var.application_name}"
23+
port = var.application_port
2824
protocol = "TCP"
29-
vpc_id = "${data.aws_subnet.personal_website_subnet.0.vpc_id}"
25+
vpc_id = data.aws_subnet.personal_website_subnet[0].vpc_id
3026
deregistration_delay = 60
3127

3228
health_check {
3329
healthy_threshold = 2
3430
unhealthy_threshold = 2
3531
interval = 10
36-
port = "${var.application_port}"
32+
port = var.application_port
3733
protocol = "TCP"
3834
}
3935
}
4036

4137
resource "aws_lb_listener" "https_listener" {
42-
load_balancer_arn = "${aws_lb.central_load_balancer.arn}"
38+
load_balancer_arn = aws_lb.central_load_balancer.arn
4339
port = 8080
4440
protocol = "HTTPS"
4541
ssl_policy = "ELBSecurityPolicy-2016-08"
46-
certificate_arn = "${aws_acm_certificate.central_lb_cert.arn}"
42+
certificate_arn = aws_acm_certificate.central_lb_cert.arn
4743

4844
default_action {
4945
type = "forward"
50-
target_group_arn = "${aws_lb_target_group.central_lb_targets.arn}"
46+
target_group_arn = aws_lb_target_group.central_lb_targets.arn
5147
}
5248
}

tf/site/iam.tf

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
resource "aws_iam_role" "website_ecs_task_executor" {
2+
name = "${var.application_name}-ecs-executor"
3+
description = "A startup executor for ECS containers"
4+
5+
assume_role_policy = data.aws_iam_policy_document.base_ecs_policy.json
6+
}
7+
8+
resource "aws_iam_role_policy_attachment" "ecs_task_executor_default" {
9+
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
10+
role = aws_iam_role.website_ecs_task_executor.name
11+
}
12+
13+
resource "aws_iam_role_policy_attachment" "ssm_read_only_policy" {
14+
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess"
15+
role = aws_iam_role.website_ecs_task_executor.name
16+
}
17+
18+
resource "aws_iam_role" "website_base_ec2_asg" {
19+
name = "${var.application_name}-base-ecs-agent"
20+
description = "IAM role for ECS ASG"
21+
22+
assume_role_policy = data.aws_iam_policy_document.base_ec2_policy.json
23+
}
24+
25+
resource "aws_iam_role_policy_attachment" "ecs_agent" {
26+
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
27+
role = aws_iam_role.website_base_ec2_asg.name
28+
}
29+
30+
resource "aws_iam_instance_profile" "website_ecs_instance_profile" {
31+
name = "${var.application_name}-ecs-instance-profile"
32+
role = aws_iam_role.website_base_ec2_asg.name
33+
}

0 commit comments

Comments
 (0)