Skip to content

Commit 5be4c0c

Browse files
committed
ecs-background: support scheduling of ECS task via EventBridge
- BREAKING CHANGE: var.codepipeline_enabled (default: false) now controls provisioning of CodePipeline and CodeBuild resources. Previously this was enabled by setting var.container_image to null. - BREAKING CHANGE: the ssm parameter store access policy was changed to use a different path. This can be improved in future by dynamically accessing context values using label_order. - If var.container_image is null, an ECR repo will be created - expose ECR mutability via var.ecr_mutability
1 parent 4e6443e commit 5be4c0c

File tree

4 files changed

+329
-12
lines changed

4 files changed

+329
-12
lines changed

modules/ecs-background/main.tf

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ locals {
22

33
default_queue_name = "app"
44

5-
codepipeline_enabled = module.this.enabled && var.container_image == null
5+
codepipeline_enabled = module.this.enabled && var.codepipeline_enabled
6+
7+
ecr_enabled = module.this.enabled && var.container_image == null
68

79
codepipeline_group_events_map = {
810
all = [
@@ -62,12 +64,12 @@ module "ecr" {
6264
source = "cloudposse/ecr/aws"
6365
version = "0.42.1"
6466

65-
enabled = local.codepipeline_enabled
67+
enabled = local.ecr_enabled
6668

6769
use_fullname = true
6870
scan_images_on_push = true
6971
max_image_count = var.ecr_max_image_count
70-
image_tag_mutability = "MUTABLE"
72+
image_tag_mutability = var.ecr_image_tag_mutability
7173

7274
force_delete = var.ecr_force_delete
7375

@@ -82,8 +84,9 @@ module "container_label" {
8284
}
8385

8486
module "container" {
85-
source = "cloudposse/ecs-container-definition/aws"
86-
version = "0.61.2"
87+
source = "cloudposse/ecs-container-definition/aws"
88+
version = "0.61.2"
89+
8790
container_name = module.container_label.id
8891
container_image = var.container_image == null ? join(":", [module.ecr.repository_url, "latest"]) : var.container_image
8992
container_memory = var.container_memory
@@ -93,6 +96,8 @@ module "container" {
9396
stop_timeout = var.container_stop_timeout
9497
essential = true
9598

99+
container_definition = var.container_overrides
100+
96101
healthcheck = var.container_healthcheck
97102

98103
readonly_root_filesystem = false
@@ -130,7 +135,7 @@ module "container" {
130135

131136
module "ecs_task" {
132137
source = "cloudposse/ecs-alb-service-task/aws"
133-
version = "0.76.1"
138+
version = "0.78.0"
134139

135140
context = module.this.context
136141

@@ -210,7 +215,14 @@ data "aws_iam_policy_document" "ecs_task" {
210215
]
211216

212217
resources = [
213-
"arn:aws:ssm:${var.aws_region}:${var.aws_account_id}:parameter/${module.this.namespace}/${module.this.stage}/${var.ssm_param_store_app_key}/*"
218+
join("/", compact(["arn:aws:ssm:${var.aws_region}:${var.aws_account_id}:parameter",
219+
module.this.namespace,
220+
module.this.environment,
221+
module.this.stage,
222+
var.ssm_param_store_app_key,
223+
"*"
224+
]))
225+
214226
]
215227

216228
effect = "Allow"

modules/ecs-background/outputs.tf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,13 @@ output "ecs_task_definition_revision" {
2727
description = "ECS task definition revision"
2828
value = module.ecs_task.task_definition_revision
2929
}
30+
31+
output "ecr_repository_name" {
32+
description = "ECR repository name"
33+
value = module.ecr.repository_name
34+
}
35+
36+
output "ecr_repository_url" {
37+
description = "ECR repository URL"
38+
value = module.ecr.repository_url
39+
}

modules/ecs-background/schedule.tf

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# ECS Scheduled Task using EventBridge
2+
3+
locals {
4+
schedule_enabled = module.this.enabled && var.schedule_expression != null && var.schedule_expression != ""
5+
}
6+
7+
module "schedule_label" {
8+
source = "cloudposse/label/null"
9+
version = "0.25.0"
10+
attributes = ["scheduled-task"]
11+
enabled = local.schedule_enabled
12+
context = module.this.context
13+
}
14+
15+
resource "aws_iam_role" "scheduler" {
16+
name = module.schedule_label.id
17+
18+
count = local.schedule_enabled ? 1 : 0
19+
20+
assume_role_policy = jsonencode({
21+
Version = "2012-10-17"
22+
Statement = [
23+
{
24+
Effect = "Allow"
25+
Principal = {
26+
Service = ["scheduler.amazonaws.com"]
27+
}
28+
Action = "sts:AssumeRole"
29+
}
30+
]
31+
})
32+
}
33+
34+
resource "aws_iam_role_policy_attachment" "scheduler" {
35+
36+
count = local.schedule_enabled ? 1 : 0
37+
38+
policy_arn = join("", aws_iam_policy.scheduler[*].arn)
39+
role = join("", aws_iam_role.scheduler[*].name)
40+
}
41+
42+
resource "aws_iam_policy" "scheduler" {
43+
name = module.schedule_label.id
44+
45+
count = local.schedule_enabled ? 1 : 0
46+
47+
policy = jsonencode({
48+
Version = "2012-10-17"
49+
Statement = [
50+
{
51+
# allow scheduler to execute the task
52+
Effect = "Allow",
53+
Action = [
54+
"ecs:RunTask"
55+
]
56+
Resource = [module.ecs_task.task_definition_arn_without_revision]
57+
},
58+
{ # allow scheduler to set the IAM roles of your task
59+
Effect = "Allow",
60+
Action = [
61+
"iam:PassRole"
62+
]
63+
Resource = [module.ecs_task.task_role_arn, module.ecs_task.task_exec_role_arn]
64+
},
65+
]
66+
})
67+
}
68+
69+
resource "aws_scheduler_schedule" "scheduled_task" {
70+
name = module.schedule_label.id
71+
72+
count = local.schedule_enabled ? 1 : 0
73+
74+
group_name = "default"
75+
76+
flexible_time_window {
77+
mode = "OFF"
78+
}
79+
80+
schedule_expression = var.schedule_expression
81+
schedule_expression_timezone = var.schedule_expression_timezone
82+
83+
target {
84+
arn = var.ecs_cluster_arn
85+
# role that allows scheduler to start the task
86+
role_arn = join("", aws_iam_role.scheduler[*].arn)
87+
88+
ecs_parameters {
89+
# schedule always uses latest revision
90+
task_definition_arn = module.ecs_task.task_definition_arn_without_revision
91+
launch_type = var.ecs_launch_type
92+
dynamic "capacity_provider_strategy" {
93+
for_each = var.ecs_capacity_provider_strategies
94+
95+
content {
96+
base = capacity_provider_strategy.value.base
97+
capacity_provider = capacity_provider_strategy.value.capacity_provider
98+
weight = capacity_provider_strategy.value.weight
99+
}
100+
}
101+
102+
103+
network_configuration {
104+
assign_public_ip = var.assign_public_ip
105+
security_groups = var.ecs_security_group_ids
106+
subnets = var.subnet_ids
107+
}
108+
109+
propagate_tags = "TASK_DEFINITION"
110+
}
111+
112+
retry_policy {
113+
maximum_event_age_in_seconds = 600
114+
maximum_retry_attempts = 5
115+
}
116+
}
117+
}

0 commit comments

Comments
 (0)