Skip to content

Commit 9b00852

Browse files
bootstrap service by service
1 parent 3648752 commit 9b00852

File tree

16 files changed

+514
-101
lines changed

16 files changed

+514
-101
lines changed

README.md

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,38 @@
11
# ECS Services Module
22

3-
Terraform ECS services
3+
Terraform ECS services bootstrap
44

55
---
66

7-
![](https://github.com/terraform-module/terraform-aws-ecs-services/workflows/release/badge.svg)
8-
![](https://github.com/terraform-module/terraform-aws-ecs-services/workflows/commit-check/badge.svg)
9-
![](https://github.com/terraform-module/terraform-aws-ecs-services/workflows/labeler/badge.svg)
10-
11-
[![](https://img.shields.io/github/license/terraform-module/terraform-aws-ecs-services)](https://github.com/terraform-module/terraform-aws-ecs-services)
12-
![](https://img.shields.io/github/v/tag/terraform-module/terraform-aws-ecs-services)
13-
![](https://img.shields.io/issues/github/terraform-module/terraform-aws-ecs-services)
14-
![](https://img.shields.io/github/issues/terraform-module/terraform-aws-ecs-services)
15-
![](https://img.shields.io/github/issues-closed/terraform-module/terraform-aws-ecs-services)
16-
[![](https://img.shields.io/github/languages/code-size/terraform-module/terraform-aws-ecs-services)](https://github.com/terraform-module/terraform-aws-ecs-services)
17-
[![](https://img.shields.io/github/repo-size/terraform-module/terraform-aws-ecs-services)](https://github.com/terraform-module/terraform-aws-ecs-services)
18-
![](https://img.shields.io/github/languages/top/terraform-module/terraform-aws-ecs-services?color=green&logo=terraform&logoColor=blue)
19-
![](https://img.shields.io/github/commit-activity/m/terraform-module/terraform-aws-ecs-services)
20-
![](https://img.shields.io/github/contributors/terraform-module/terraform-aws-ecs-services)
21-
![](https://img.shields.io/github/last-commit/terraform-module/terraform-aws-ecs-services)
22-
[![Maintenance](https://img.shields.io/badge/Maintenu%3F-oui-green.svg)](https://GitHub.com/terraform-module/terraform-aws-ecs-services/graphs/commit-activity)
23-
[![GitHub forks](https://img.shields.io/github/forks/terraform-module/terraform-aws-ecs-services.svg?style=social&label=Fork)](https://github.com/terraform-module/terraform-aws-ecs-services)
7+
![](https://github.com/terraform-module/terraform-aws-ecs-bootstrap/workflows/release/badge.svg)
8+
![](https://github.com/terraform-module/terraform-aws-ecs-bootstrap/workflows/commit-check/badge.svg)
9+
![](https://github.com/terraform-module/terraform-aws-ecs-bootstrap/workflows/labeler/badge.svg)
10+
11+
[![](https://img.shields.io/github/license/terraform-module/terraform-aws-ecs-bootstrap)](https://github.com/terraform-module/terraform-aws-ecs-bootstrap)
12+
![](https://img.shields.io/github/v/tag/terraform-module/terraform-aws-ecs-bootstrap)
13+
![](https://img.shields.io/issues/github/terraform-module/terraform-aws-ecs-bootstrap)
14+
![](https://img.shields.io/github/issues/terraform-module/terraform-aws-ecs-bootstrap)
15+
![](https://img.shields.io/github/issues-closed/terraform-module/terraform-aws-ecs-bootstrap)
16+
[![](https://img.shields.io/github/languages/code-size/terraform-module/terraform-aws-ecs-bootstrap)](https://github.com/terraform-module/terraform-aws-ecs-bootstrap)
17+
[![](https://img.shields.io/github/repo-size/terraform-module/terraform-aws-ecs-bootstrap)](https://github.com/terraform-module/terraform-aws-ecs-bootstrap)
18+
![](https://img.shields.io/github/languages/top/terraform-module/terraform-aws-ecs-bootstrap?color=green&logo=terraform&logoColor=blue)
19+
![](https://img.shields.io/github/commit-activity/m/terraform-module/terraform-aws-ecs-bootstrap)
20+
![](https://img.shields.io/github/contributors/terraform-module/terraform-aws-ecs-bootstrap)
21+
![](https://img.shields.io/github/last-commit/terraform-module/terraform-aws-ecs-bootstrap)
22+
[![Maintenance](https://img.shields.io/badge/Maintenu%3F-oui-green.svg)](https://GitHub.com/terraform-module/terraform-aws-ecs-bootstrap/graphs/commit-activity)
23+
[![GitHub forks](https://img.shields.io/github/forks/terraform-module/terraform-aws-ecs-bootstrap.svg?style=social&label=Fork)](https://github.com/terraform-module/terraform-aws-ecs-bootstrap)
2424

2525
---
2626

2727
## Usage example
2828

29-
IMPORTANT: The master branch is used in source just as an example. In your code, do not pin to master because there may be breaking changes between releases. Instead pin to the release tag (e.g. ?ref=tags/x.y.z) of one of our [latest releases](https://github.com/terraform-module/terraform-aws-ecs-services/releases).
29+
IMPORTANT: The master branch is used in source just as an example. In your code, do not pin to master because there may be breaking changes between releases. Instead pin to the release tag (e.g. ?ref=tags/x.y.z) of one of our [latest releases](https://github.com/terraform-module/terraform-aws-ecs-bootstrap/releases).
3030

3131
See `examples` directory for working examples to reference:
3232

3333
```hcl
34-
module "ecs-services" {
35-
source = "terraform-module/ecs-services/aws"
34+
module "ecs-bootstrap" {
35+
source = "terraform-module/ecs-bootstrap/aws"
3636
version = "~> 1"
3737
}
3838
```
@@ -41,15 +41,14 @@ module "ecs-services" {
4141

4242
See `examples` directory for working examples to reference
4343

44-
- [Complete ECS](https://github.com/terraform-module/terraform-aws-ecs-services/tree/master/examples)
44+
- [Complete ECS](https://github.com/terraform-module/terraform-aws-ecs-bootstrap/tree/master/examples)
4545

4646
## Assumptions
4747

4848
## Available features
4949

50-
- Create ECS tasks
51-
- Create ECS services
52-
- Memory based autoscaling
50+
- Create/Update ECS tasks
51+
- Create/Update ECS services
5352
- CPU based autoscaling
5453

5554
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
@@ -114,17 +113,23 @@ Submit a pull request
114113

115114
# Authors
116115

117-
Currently maintained by [Ivan Katliarchuk](https://github.com/ivankatliarchuk) and these [awesome contributors](https://github.com/terraform-module/terraform-aws-ecs-services/graphs/contributors).
116+
Currently maintained by [Ivan Katliarchuk](https://github.com/ivankatliarchuk) and these [awesome contributors](https://github.com/terraform-module/terraform-aws-ecs-bootstrap/graphs/contributors).
118117

119118
[![ForTheBadge uses-git](http://ForTheBadge.com/images/badges/uses-git.svg)](https://GitHub.com/)
120119

121120
## Terraform Registry
122121

123-
- [Module](https://registry.terraform.io/modules/terraform-module/ecs-services/aws)
122+
- [Module](https://registry.terraform.io/modules/terraform-module/ecs-bootstrap/aws)
124123

125124
## Resources
126125

127126
- [TFLint Rules](https://github.com/terraform-linters/tflint/tree/master/docs/rules)
128127
- [Terraform modules](https://registry.terraform.io/namespaces/terraform-module)
129128
- [Blog: ECS with Fargate and Terraform](https://engineering.finleap.com/posts/2020-02-20-ecs-fargate-terraform/)
130129
- [Tfm: example](https://github.com/finleap/tf-ecs-fargate-tmpl)
130+
131+
## TODO
132+
133+
- [ ] Tags per resource
134+
- [ ] Pass default values
135+
- [ ] Strongly typed objects

ecs.tf

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
resource "aws_ecs_task_definition" "this" {
2+
count = try(var.service.create, false) ? 1 : 0
3+
4+
family = try(var.service.family, format("%s-task", var.name_prefix))
5+
network_mode = try(var.service.network_mode, local.defaults.network_mode)
6+
requires_compatibilities = try(var.service.compatibilities, [local.defaults.compatibilities])
7+
cpu = try(var.service.cpu, local.defaults.cpu)
8+
memory = try(var.service.memory, local.defaults.memory)
9+
execution_role_arn = aws_iam_role.task_execution_role[0].arn
10+
task_role_arn = aws_iam_role.task_role[0].arn
11+
container_definitions = jsonencode(var.service.container_definitions)
12+
13+
tags = merge(var.tags, {
14+
Name = format("%s-task", var.name_prefix)
15+
})
16+
}
17+
18+
resource "aws_ecs_service" "this" {
19+
count = try(var.service.create, false) ? 1 : 0
20+
21+
name = local.service_name
22+
cluster = local.cluster_id
23+
task_definition = aws_ecs_task_definition.this[count.index].family
24+
desired_count = try(var.service.desired_count, local.defaults.desired_count)
25+
deployment_minimum_healthy_percent = try(var.service.deployment_minimum_healthy_percent, local.defaults.deployment_minimum_healthy_percent)
26+
deployment_maximum_percent = try(var.service.deployment_maximum_percent, local.defaults.deployment_maximum_percent)
27+
# InvalidParameterException: Health check grace period is only valid for services configured to use load balancers
28+
health_check_grace_period_seconds = try(var.lb.create, false) ? try(var.service.health_check_grace_period_seconds, local.defaults.health_check_grace_period_seconds) : null
29+
enable_execute_command = local.defaults.enable_execute_command
30+
launch_type = local.defaults.compatibilities
31+
scheduling_strategy = try(var.service.scheduling_strategy, local.defaults.scheduling_strategy)
32+
33+
network_configuration {
34+
security_groups = aws_security_group.this.*.id
35+
subnets = var.subnets
36+
}
37+
38+
dynamic "service_registries" {
39+
for_each = var.service.visibility == "private" ? { for k, v in aws_service_discovery_service.this : k => v } : {}
40+
content {
41+
registry_arn = service_registries.value.arn
42+
}
43+
}
44+
45+
dynamic "load_balancer" {
46+
for_each = try(var.lb.create, false) ? aws_lb_target_group.this : []
47+
48+
content {
49+
target_group_arn = load_balancer.value.arn
50+
container_name = var.service.container_definitions[count.index].name
51+
container_port = var.service.exposed_port
52+
}
53+
}
54+
55+
tags = merge(var.tags, {
56+
Name = local.service_name
57+
})
58+
59+
depends_on = [
60+
aws_cloudwatch_log_group.this,
61+
aws_service_discovery_service.this
62+
]
63+
64+
# Optional: Allow external changes without Terraform plan difference
65+
# we ignore task_definition changes as the revision changes on deploy
66+
# of a new version of the application
67+
# desired_count is ignored as it can change due to autoscaling policy
68+
lifecycle {
69+
ignore_changes = [desired_count]
70+
}
71+
}

iam.tf

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
# This role required is due to the fact that the tasks will be executed “serverless” with the Fargate configuration.
2-
# This means there’s no EC2 instances involved, meaning the permissions that usually go to the EC2 instances have to go somewhere else: the Fargate service. This enables the service to e.g. pull the image from ECR, spin up or deregister tasks etc. AWS provides you with a predefined policy for this, so I just attached this to my role:
3-
resource "aws_iam_role" "ecs_task_execution_role" {
4-
for_each = { for k, v in var.services : k => v if v.create }
5-
6-
name = "${each.key}-ecs-task-execution-role"
7-
tags = merge(var.tags, try(each.value.tags, null))
1+
################################################################################
2+
# IAM Actions
3+
################################################################################
4+
resource "aws_iam_role" "task_execution_role" {
5+
count = try(var.iam.create, false) ? 1 : 0
6+
name = format("%s-ecs-task-execution-role", var.name_prefix)
7+
tags = var.tags
88

99
assume_role_policy = <<EOF
1010
{
@@ -23,12 +23,16 @@ resource "aws_iam_role" "ecs_task_execution_role" {
2323
EOF
2424
}
2525

26-
# This role provide permissions what AWS services the task has access to
27-
resource "aws_iam_role" "ecs_task_role" {
28-
for_each = { for k, v in var.services : k => v if v.create }
26+
resource "aws_iam_role_policy_attachment" "task_execution_role_policy_attachment" {
27+
for_each = { for k, v in local.iam_role_policies : k => v if var.iam.create }
28+
role = aws_iam_role.task_execution_role[0].id
29+
policy_arn = each.value
30+
}
2931

30-
name = "${each.key}-ecs-task-role"
31-
tags = merge(var.tags, try(each.value.tags, null))
32+
resource "aws_iam_role" "task_role" {
33+
count = try(var.iam.create, false) ? 1 : 0
34+
name = format("%s-ecs-task-role", var.name_prefix)
35+
tags = var.tags
3236

3337
assume_role_policy = <<EOF
3438
{
@@ -45,13 +49,31 @@ resource "aws_iam_role" "ecs_task_role" {
4549
]
4650
}
4751
EOF
52+
depends_on = [aws_iam_role.task_execution_role]
53+
}
54+
55+
resource "aws_iam_role_policy" "task_additional_policies_attach" {
56+
for_each = { for k, v in var.iam.additional_policies : k => v if var.iam.create }
4857

49-
depends_on = [aws_iam_role.ecs_task_execution_role]
58+
name = format("%s-%s-ecs-task-service-permissions", var.name_prefix, each.key)
59+
role = aws_iam_role.task_role[0].name
60+
policy = each.value
5061
}
5162

52-
# Why: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html
53-
resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_policy_attachment" {
54-
for_each = { for k, v in aws_iam_role.ecs_task_execution_role : k => v }
55-
role = each.value.name
56-
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
63+
################################################################################
64+
# ECS Autoscaling
65+
################################################################################
66+
resource "aws_iam_role" "autoscaling" {
67+
count = try(var.scaling.create, false) && try(var.scaling.create_iam_role, false) ? 1 : 0
68+
69+
name = format("%s-appautoscaling-role", local.service_name)
70+
assume_role_policy = file("${path.module}/templates/autoscaling-role.json")
71+
}
72+
73+
resource "aws_iam_role_policy" "autoscaling" {
74+
count = try(var.scaling.create, false) && try(var.scaling.create_iam_role, false) ? 1 : 0
75+
76+
name = format("%s-appautoscaling-policy", local.service_name)
77+
policy = file("${path.module}/templates/autoscaling-policy.json")
78+
role = aws_iam_role.autoscaling[count.index].id
5779
}

lb.tf

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
resource "random_string" "tg" {
2+
length = 3
3+
special = false
4+
5+
keepers = {
6+
# Generate a new pet name each time we switch a port
7+
lb_port = var.lb.port
8+
}
9+
}
10+
11+
resource "aws_lb_target_group" "this" {
12+
count = try(var.lb.create, false) ? 1 : 0
13+
# "name" cannot be longer than 32 characters
14+
name = substr(format("%s-%s-tg-ecs", var.name_prefix, random_string.tg.id), 0, 32)
15+
port = var.lb.port
16+
protocol = "HTTP"
17+
vpc_id = var.vpc_id
18+
target_type = "ip"
19+
20+
deregistration_delay = try(var.lb.deregistration_delay, local.defaults.deregistration_delay)
21+
22+
dynamic "health_check" {
23+
iterator = el
24+
for_each = { for k, v in { onlyone = var.lb.health_check } : k => v }
25+
26+
content {
27+
enabled = lookup(el.value, "enabled", true)
28+
interval = lookup(el.value, "interval", 30)
29+
path = lookup(el.value, "path", "/")
30+
port = lookup(el.value, "port", "traffic-port")
31+
healthy_threshold = lookup(el.value, "healthy_threshold", 3)
32+
unhealthy_threshold = lookup(el.value, "unhealthy_threshold", 2)
33+
timeout = lookup(el.value, "timeout", 3)
34+
protocol = lookup(el.value, "protocol", "HTTP")
35+
matcher = lookup(el.value, "matcher", 200)
36+
}
37+
}
38+
39+
tags = merge(var.tags, {
40+
Name = format("%s-tg-ecs", var.name_prefix)
41+
})
42+
43+
lifecycle {
44+
create_before_destroy = true
45+
}
46+
}
47+
48+
resource "aws_lb_listener_rule" "this" {
49+
count = try(var.lb.create, false) ? 1 : 0
50+
51+
listener_arn = var.lb.listener_arn
52+
priority = var.lb.priority
53+
54+
dynamic "action" {
55+
for_each = aws_lb_target_group.this
56+
57+
content {
58+
type = "forward"
59+
target_group_arn = action.value.arn
60+
}
61+
}
62+
63+
dynamic "condition" {
64+
for_each = var.lb.lb_rules
65+
66+
content {
67+
host_header {
68+
values = formatlist("%s.*", condition.value)
69+
}
70+
}
71+
}
72+
73+
lifecycle {
74+
create_before_destroy = true
75+
}
76+
77+
tags = var.tags
78+
}

locals.tf

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,32 @@
11
locals {
2+
cluster_id = var.cluster_id
3+
cluster_name = var.cluster_name
4+
service_name = format("%s-svc", var.name_prefix)
5+
26
defaults = {
3-
cpu = 256
4-
memory = 512
5-
desired_count = 2
7+
cpu = 256
8+
memory = 512
9+
10+
enable_execute_command = true
11+
deregistration_delay = 30
12+
retention_in_days = 1
13+
14+
deployment_minimum_healthy_percent = 100
15+
deployment_maximum_percent = 200
16+
health_check_grace_period_seconds = 10
17+
18+
compatibilities = "FARGATE"
19+
network_mode = "awsvpc"
20+
scheduling_strategy = "REPLICA"
21+
}
22+
23+
defaults_sds = {
24+
routing_policy = "MULTIVALUE"
25+
failure_threshold = 2
26+
}
627

7-
deregistration_delay = 30
8-
network_mode = "awsvpc"
9-
compatibilities = ["FARGATE"]
28+
iam_role_policies = {
29+
AmazonEC2ContainerServiceforEC2Role = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
30+
AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
1031
}
1132
}

main.tf

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

0 commit comments

Comments
 (0)