Skip to content

Commit

Permalink
Allow for DMP-specific ECS Task definitions
Browse files Browse the repository at this point in the history
  • Loading branch information
mcbhenwood committed Nov 2, 2023
1 parent e5e336c commit 132fcd7
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 0 deletions.
12 changes: 12 additions & 0 deletions resource-groups/dmp-ecs-fargate-task-definition/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# DMP ECS Task Definition

Collection of resources to specify a Task Definition in Elastic Container Service. This version differs slightly from [the original ECS Task Definition resource group](../ecs-fargate-task-definition) in ways which are designed to make it easier to adopt within the already mature DMP Terraform environments.

Includes (non-exhaustive):

* Task Definition
* Task Role and JSON document describing the IAM permission to pass the Task Role

There is a lot of commonality with the original ECS Task Definition resource group but the decision was taken _not_ to refactor them into a single module because it would introduce a lot of tightly-structured logic into a construct which (tbh) already stretches the idea of clear declarative code a little bit.

So we're bolting this module alongside that and when (if?) DMP is ever shut down, it can safely be removed.
83 changes: 83 additions & 0 deletions resource-groups/dmp-ecs-fargate-task-definition/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
locals {
/* Note that in the cases of interval, retries, hostPort, protocol and volumesFrom we have added these even
though they are implicitly inferred by AWS. Reason being that Terraform forces a cycle of the ECS Service
definition each time if they are omitted, and this is expensive in lots of ways.
*/
container_definitions = [
for name, vars in var.container_definitions : {
name = name
command = vars.override_command # If null, does not override Dockerfile original command
cpu = vars.cpu
entrypoint = lookup(var.override_entrypoints, name, null)
environment = vars.environment_variables
essential = vars.essential != null ? vars.essential : true
healthCheck = vars.healthcheck_command == null ? null : {
command = ["CMD-SHELL", vars.healthcheck_command]
interval = 30
retries = 3
startPeriod = 10
timeout = 10
}
image = vars.image
logConfiguration = {
"logDriver" : "awslogs",
"options" : {
"awslogs-group" : vars.log_group_name,
"awslogs-region" : var.aws_region,
"awslogs-stream-prefix" : "container"
}
}
memory = vars.memory
mountPoints = [
for mount in vars.mounts :
{
containerPath : mount["mount_point"],
readOnly : mount["read_only"],
sourceVolume : mount["volume_name"]
}
]
portMappings = vars.port == null ? null : [
{
containerPort = vars.port
hostPort = vars.port
protocol = "tcp"
}
]
secrets = vars.secret_environment_variables
volumesFrom = []
}
]
}

resource "aws_ecs_task_definition" "task" {
family = var.family_name
container_definitions = jsonencode(local.container_definitions)
cpu = var.task_cpu
execution_role_arn = var.ecs_execution_role_arn
memory = var.task_memory
network_mode = "awsvpc" # Fixed for Fargate
requires_compatibilities = ["FARGATE"]
runtime_platform {
cpu_architecture = "X86_64"
operating_system_family = "LINUX"
}
task_role_arn = aws_iam_role.task_role.arn

dynamic "volume" {
for_each = var.volumes
iterator = volume

content {
efs_volume_configuration {
authorization_config {
access_point_id = volume.value["access_point_id"]
iam = "DISABLED"
}
file_system_id = volume.value["file_system_id"]
transit_encryption = "ENABLED"
}

name = volume.value["volume_name"]
}
}
}
24 changes: 24 additions & 0 deletions resource-groups/dmp-ecs-fargate-task-definition/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
output "pass_task_role_policy_document_json" {
description = "JSON describing an IAM policy which allows passage of the task role"
value = data.aws_iam_policy_document.pass_task_role.json
}

output "task_definition_arn" {
description = "ARN of the task definition"
value = aws_ecs_task_definition.task.arn
}

output "task_family_name" {
description = "Family name for the task"
value = aws_ecs_task_definition.task.family
}

output "task_role_arn" {
description = "ARN of the IAM role assigned to all tasks run under this task definition"
value = aws_iam_role.task_role.arn
}

output "task_role_name" {
description = "Name of the IAM role assigned to all tasks run under this task definition"
value = aws_iam_role.task_role.name
}
8 changes: 8 additions & 0 deletions resource-groups/dmp-ecs-fargate-task-definition/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">=5.7.0"
}
}
}
42 changes: 42 additions & 0 deletions resource-groups/dmp-ecs-fargate-task-definition/task_role.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
resource "aws_iam_role" "task_role" {
name = "${var.family_name}-ecs-task"
description = "Role to be assumed by the ${var.family_name} tasks during general operation"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ecs-tasks.amazonaws.com"
}
Condition = {
ArnLike = {
"aws:SourceArn" = "arn:aws:ecs:${var.aws_region}:${var.aws_account_id}:*"
}
}
}
]
})
}


data "aws_iam_policy_document" "pass_task_role" {
version = "2012-10-17"

statement {
sid = "Pass${replace(var.family_name, "/[-_]/", "")}TaskRole"
actions = [
"iam:GetRole",
"iam:PassRole"
]
effect = "Allow"
resources = [
aws_iam_role.task_role.arn
]
}
depends_on = [
aws_iam_role.task_role
]
}
76 changes: 76 additions & 0 deletions resource-groups/dmp-ecs-fargate-task-definition/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
variable "aws_account_id" {
type = string
description = "ID of the account into which deployments are performed"
}

variable "aws_region" {
type = string
description = "Region for resource deployment"
}

variable "container_definitions" {
type = map(object({
# CPU to allocate to the container (where a value of 1024 == 1vCPU)
cpu = number
# Environment variables to be made available to the container
environment_variables = list(map(string))
# Indicates whether or not the container is essential to the task
essential = bool
# Command to run within container to verify process health
healthcheck_command = string
# Canonical Docker image name - OR - URL of the image repo
image = string
# Full name of an existing CloudWatch Log Group for the container
log_group_name = string
# Memory to allocate to the container (where a value of 1024 == 1GB)
memory = number
# List of config value objects for volumes to be mounted from the container
mounts = list(object({
mount_point = string
read_only = bool
volume_name = string
}))
# Startup command to override that which is specified in the original Dockerfile of the container
override_command = list(string)
# Port to which the container expects to bind its listener
port = number
# Environment variables to be looked up as secrets and then made available to the container
secret_environment_variables = list(map(string))
}))
}

variable "ecs_execution_role_arn" {
type = string
description = "ARN of the role which is assumed by the ECS execution processes"
}

variable "family_name" {
type = string
description = "The name to give to the task definition, across all revisions"
}

variable "override_entrypoints" {
type = map(list(string))
description = "Forced override of entrypoint for the container"
default = {}
}

variable "task_cpu" {
type = number
description = "CPU to allocate to each task (where a value of 1024 == 1vCPU) - Must be >= total of all containers' CPU"
}

variable "task_memory" {
type = number
description = "Memory to allocate to each task (where a value of 1024 == 1GB) - Must be >= total of all containers' memory"
}

variable "volumes" {
type = list(object({
access_point_id = string
file_system_id = string
volume_name = string
}))
description = "List of volumes made available to the task's container(s)"
default = []
}

0 comments on commit 132fcd7

Please sign in to comment.