Skip to content

Commit 6918848

Browse files
authored
[feature] Add ECS modules to cztack (#132)
1 parent 4185303 commit 6918848

28 files changed

+2205
-0
lines changed

aws-ecs-job-fargate/README.md

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# ECS Job on Fargate
2+
3+
This creates an ECS service running on Fargate with no load balancer in front of it. Good for
4+
background worker daemon sort of things.
5+
6+
## Terraform managed task definition vs czecs
7+
8+
If the user sets `var.manage_task_definition = true`, Terraform will manage the lifecycle
9+
of the container definition; any external changes are reset on the next Terraform run.
10+
11+
If var.manage_task_definition = false, the user is expected to manage the
12+
container definition external to Terraform (e.g. using [czecs](https://github.com/chanzuckerberg/czecs)). Upon creation,
13+
Terraform will use a stub definition, but from that point forward will ignore any
14+
changes to the definition, allowing external task definition management.
15+
16+
<!-- START -->
17+
## Inputs
18+
19+
| Name | Description | Type | Default | Required |
20+
|------|-------------|:----:|:-----:|:-----:|
21+
| cluster\_id | | string | n/a | yes |
22+
| container\_name | Name of the container. Must match name in task definition. If omitted, defaults to name derived from project/env/service. | string | `null` | no |
23+
| cpu | CPU units for Fargate task. Used if task_definition provided, or for initial stub task if externally managed. | number | `256` | no |
24+
| deployment\_maximum\_percent | (Optional) The upper limit (as a percentage of the service's desiredCount) of the number of running tasks that can be running in a service during a deployment. Not valid when using the DAEMON scheduling strategy. | number | `200` | no |
25+
| deployment\_minimum\_healthy\_percent | (Optional) The lower limit (as a percentage of the service's desiredCount) of the number of running tasks that must remain running and healthy in a service during a deployment. | number | `100` | no |
26+
| desired\_count | | number | n/a | yes |
27+
| env | Env for tagging and naming. See [doc](../README.md#consistent-tagging). | string | n/a | yes |
28+
| memory | Memory in megabytes for Fargate task. Used if task_definition provided, or for initial stub task if externally managed. | number | `512` | no |
29+
| project | Project for tagging and naming. See [doc](../README.md#consistent-tagging) | string | n/a | yes |
30+
| registry\_secretsmanager\_arn | ARN for AWS Secrets Manager secret for credentials to private registry | string | `null` | no |
31+
| security\_group\_ids | Security group to use for the Fargate task. | list | `<list>` | no |
32+
| service | Service for tagging and naming. See [doc](../README.md#consistent-tagging). | string | n/a | yes |
33+
| task\_definition | JSON to describe task. If omitted, defaults to a stub task that is expected to be managed outside of Terraform. | string | `null` | no |
34+
| task\_role\_arn | | string | n/a | yes |
35+
| task\_subnets | Subnets to launch Fargate task in. | list | `<list>` | no |
36+
37+
## Outputs
38+
39+
| Name | Description |
40+
|------|-------------|
41+
| ecs\_service\_arn | ARN for the ECS service. |
42+
| ecs\_task\_definition\_family | The family of the task definition defined for the given/generated container definition. |
43+
| task\_execution\_role\_arn | Task execution role for Fargate task. |
44+
45+
<!-- END -->

aws-ecs-job-fargate/iam.tf

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
data "aws_iam_policy_document" "execution_role" {
2+
statement {
3+
principals {
4+
type = "Service"
5+
identifiers = ["ecs-tasks.amazonaws.com"]
6+
}
7+
8+
actions = ["sts:AssumeRole"]
9+
}
10+
}
11+
12+
# Always create and attach, Fargate requires task definition to have execution role ARN to support log driver awslogs.
13+
resource "aws_iam_role" "task_execution_role" {
14+
name = "${local.name}-execution-role"
15+
assume_role_policy = data.aws_iam_policy_document.execution_role.json
16+
}
17+
18+
# TODO(mbarrien): We can probably narrow this down to allowing access to only
19+
# the specific ECR arn if applicable, and the specific cloudwatch log group.
20+
# Either pass both identifiers in, or pass the entire role ARN as an argument
21+
resource "aws_iam_role_policy_attachment" "task_execution_role" {
22+
count = var.registry_secretsmanager_arn != null ? 1 : 0
23+
role = aws_iam_role.task_execution_role.name
24+
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
25+
}
26+
27+
data "aws_iam_policy_document" "registry_secretsmanager" {
28+
count = var.registry_secretsmanager_arn != null ? 1 : 0
29+
30+
statement {
31+
actions = [
32+
"kms:Decrypt",
33+
]
34+
35+
resources = [var.registry_secretsmanager_arn]
36+
}
37+
38+
statement {
39+
actions = [
40+
"secretsmanager:GetSecretValue",
41+
]
42+
43+
# Limit to only current version of the secret
44+
condition {
45+
test = "ForAnyValue:StringEquals"
46+
variable = "secretsmanager:VersionStage"
47+
values = ["AWSCURRENT"]
48+
}
49+
50+
resources = [var.registry_secretsmanager_arn]
51+
}
52+
}
53+
54+
resource "aws_iam_role_policy" "task_execution_role_secretsmanager" {
55+
count = var.registry_secretsmanager_arn != null ? 1 : 0
56+
role = aws_iam_role.task_execution_role.name
57+
policy = data.aws_iam_policy_document.registry_secretsmanager[0].json
58+
}

aws-ecs-job-fargate/main.tf

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
locals {
2+
name = "${var.project}-${var.env}-${var.service}"
3+
4+
container_name = var.container_name == null ? local.name : var.container_name
5+
6+
task_definition = "${aws_ecs_task_definition.job.family}:${aws_ecs_task_definition.job.revision}"
7+
8+
tags = {
9+
managedBy = "terraform"
10+
Name = local.name
11+
project = var.project
12+
env = var.env
13+
service = var.service
14+
owner = var.owner
15+
}
16+
}
17+
18+
# Only one of the following is active at a time, depending on var.manage_task_definition
19+
resource "aws_ecs_service" "job" {
20+
name = local.name
21+
cluster = var.cluster_id
22+
count = var.manage_task_definition ? 1 : 0
23+
launch_type = "FARGATE"
24+
25+
task_definition = local.task_definition
26+
desired_count = var.desired_count
27+
deployment_maximum_percent = var.deployment_maximum_percent
28+
deployment_minimum_healthy_percent = var.deployment_minimum_healthy_percent
29+
scheduling_strategy = "REPLICA"
30+
31+
network_configuration {
32+
subnets = var.task_subnets
33+
security_groups = var.security_group_ids
34+
}
35+
36+
tags = local.tags
37+
}
38+
39+
resource "aws_ecs_service" "unmanaged-job" {
40+
name = local.name
41+
cluster = var.cluster_id
42+
count = var.manage_task_definition ? 0 : 1
43+
launch_type = "FARGATE"
44+
45+
task_definition = local.task_definition
46+
desired_count = var.desired_count
47+
deployment_maximum_percent = var.deployment_maximum_percent
48+
deployment_minimum_healthy_percent = var.deployment_minimum_healthy_percent
49+
scheduling_strategy = "REPLICA"
50+
51+
network_configuration {
52+
subnets = var.task_subnets
53+
security_groups = var.security_group_ids
54+
}
55+
56+
lifecycle {
57+
ignore_changes = [task_definition]
58+
}
59+
60+
tags = local.tags
61+
}
62+
63+
# Default container definition if var.manage_task_definition == false
64+
# Defaults to a minimal hello-world implementation; should be updated separately from
65+
# Terraform, e.g. using ecs deploy or czecs
66+
locals {
67+
template = <<TEMPLATE
68+
[
69+
{
70+
"name": "${local.container_name}",
71+
"image": "library/busybox:1.29",
72+
"command": ["sh", "-c", "while true; do { echo -e 'HTTP/1.1 200 OK\r\n\nRunning stub server'; date; } | nc -l -p 8080; done"]
73+
}
74+
]
75+
TEMPLATE
76+
}
77+
78+
resource "aws_ecs_task_definition" "job" {
79+
family = local.name
80+
container_definitions = var.manage_task_definition ? var.task_definition : local.template
81+
task_role_arn = var.task_role_arn
82+
requires_compatibilities = ["FARGATE"]
83+
cpu = var.cpu
84+
memory = var.memory
85+
network_mode = "awsvpc"
86+
execution_role_arn = aws_iam_role.task_execution_role.arn
87+
}

aws-ecs-job-fargate/outputs.tf

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
output "ecs_service_arn" {
2+
description = "ARN for the ECS service."
3+
4+
# Awful hack modified from https://github.com/hashicorp/terraform/issues/16726
5+
# Since we know exactly one of the following has count > 0, we just concatenate all the mostly empty lists, and return the first element.
6+
value = concat(aws_ecs_service.unmanaged-job.*.id, aws_ecs_service.job.*.id)[0]
7+
}
8+
9+
output "ecs_task_definition_family" {
10+
description = "The family of the task definition defined for the given/generated container definition."
11+
value = aws_ecs_task_definition.job.family
12+
}
13+
14+
output "task_execution_role_arn" {
15+
description = "Task execution role for Fargate task."
16+
value = aws_iam_role.task_execution_role.arn
17+
}

aws-ecs-job-fargate/variables.tf

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
variable "project" {
2+
type = string
3+
description = "Project for tagging and naming. See [doc](../README.md#consistent-tagging)"
4+
}
5+
6+
variable "service" {
7+
type = string
8+
description = "Service for tagging and naming. See [doc](../README.md#consistent-tagging)."
9+
}
10+
11+
variable "env" {
12+
type = string
13+
description = "Env for tagging and naming. See [doc](../README.md#consistent-tagging)."
14+
}
15+
16+
variable "owner" {
17+
type = string
18+
description = "Owner for tagging and naming. See [doc](../README.md#consistent-tagging)."
19+
}
20+
21+
variable "cluster_id" {
22+
type = string
23+
}
24+
25+
variable "desired_count" {
26+
type = number
27+
}
28+
29+
variable "deployment_maximum_percent" {
30+
description = "(Optional) The upper limit (as a percentage of the service's desiredCount) of the number of running tasks that can be running in a service during a deployment. Not valid when using the DAEMON scheduling strategy."
31+
type = number
32+
default = 200
33+
}
34+
35+
variable "deployment_minimum_healthy_percent" {
36+
description = "(Optional) The lower limit (as a percentage of the service's desiredCount) of the number of running tasks that must remain running and healthy in a service during a deployment."
37+
type = number
38+
default = 100
39+
}
40+
41+
variable "task_role_arn" {
42+
type = string
43+
}
44+
45+
variable "task_definition" {
46+
description = "JSON to describe task. If omitted, defaults to a stub task that is expected to be managed outside of Terraform."
47+
type = string
48+
default = null
49+
}
50+
51+
variable "cpu" {
52+
description = "CPU units for Fargate task. Used if task_definition provided, or for initial stub task if externally managed."
53+
type = number
54+
default = 256
55+
}
56+
57+
variable "memory" {
58+
description = "Memory in megabytes for Fargate task. Used if task_definition provided, or for initial stub task if externally managed."
59+
type = number
60+
default = 512
61+
}
62+
63+
variable "task_subnets" {
64+
description = "Subnets to launch Fargate task in."
65+
type = list(string)
66+
default = []
67+
}
68+
69+
variable "security_group_ids" {
70+
description = "Security group to use for the Fargate task."
71+
type = list(string)
72+
default = []
73+
}
74+
75+
variable "container_name" {
76+
description = "Name of the container. Must match name in task definition. If omitted, defaults to name derived from project/env/service."
77+
type = string
78+
default = null
79+
}
80+
81+
variable "registry_secretsmanager_arn" {
82+
description = "ARN for AWS Secrets Manager secret for credentials to private registry"
83+
type = string
84+
default = null
85+
}
86+
87+
variable "manage_task_definition" {
88+
description = "If false, Terraform will not touch the task definition for the ECS service after initial creation"
89+
type = bool
90+
default = true
91+
}

aws-ecs-job/README.md

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# ECS Job
2+
3+
This creates an ECS service with no load balancer in front of it. Good for
4+
background worker daemon sort of things.
5+
6+
## Terraform managed task definition vs czecs
7+
8+
If the user sets `var.manage_task_definition = true`, Terraform will manage the lifecycle
9+
of the container definition; any external changes are reset on the next Terraform run.
10+
11+
If var.manage_task_definition = false, the user is expected to manage the
12+
container definition external to Terraform (e.g. using [czecs](https://github.com/chanzuckerberg/czecs)). Upon creation,
13+
Terraform will use a stub definition, but from that point forward will ignore any
14+
changes to the definition, allowing external task definition management.
15+
16+
<!-- START -->
17+
## Inputs
18+
19+
| Name | Description | Type | Default | Required |
20+
|------|-------------|:----:|:-----:|:-----:|
21+
| cluster\_id | | string | n/a | yes |
22+
| container\_name | Name of the container. Must match name in task definition. If omitted, defaults to name derived from project/env/service. | string | `null` | no |
23+
| deployment\_maximum\_percent | (Optional) The upper limit (as a percentage of the service's desiredCount) of the number of running tasks that can be running in a service during a deployment. Not valid when using the DAEMON scheduling strategy. | number | `200` | no |
24+
| deployment\_minimum\_healthy\_percent | (Optional) The lower limit (as a percentage of the service's desiredCount) of the number of running tasks that must remain running and healthy in a service during a deployment. | number | `100` | no |
25+
| desired\_count | | number | n/a | yes |
26+
| env | Env for tagging and naming. See [doc](../README.md#consistent-tagging). | string | n/a | yes |
27+
| project | Project for tagging and naming. See [doc](../README.md#consistent-tagging) | string | n/a | yes |
28+
| scheduling\_strategy | Scheduling strategy for the service: REPLICA or DAEMON. | string | `"REPLICA"` | no |
29+
| service | Service for tagging and naming. See [doc](../README.md#consistent-tagging). | string | n/a | yes |
30+
| task\_definition | JSON to describe task. If omitted, defaults to a stub task that is expected to be managed outside of Terraform. | string | `null` | no |
31+
| task\_role\_arn | | string | n/a | yes |
32+
33+
## Outputs
34+
35+
| Name | Description |
36+
|------|-------------|
37+
| ecs\_service\_arn | ARN for the ECS service. |
38+
| ecs\_task\_definition\_family | The family of the task definition defined for the given/generated container definition. |
39+
40+
<!-- END -->

0 commit comments

Comments
 (0)