Skip to content

Commit

Permalink
Merge pull request #20 from DNXLabs/feature/efs_and_dynamic_ports
Browse files Browse the repository at this point in the history
dynamic ports and enable efs
  • Loading branch information
brunodasilvalenga authored Aug 23, 2023
2 parents 063c99c + 158bff9 commit b7f4fa9
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 37 deletions.
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,21 @@ In addition you have the option to create or not :
| cloudwatch\_logs\_export | Whether to mark the log group to export to an S3 bucket (needs terraform-aws-log-exporter to be deployed in the account/region) | `bool` | `false` | no |
| cloudwatch\_logs\_retention | Specifies the number of days you want to retain log events in the specified log group. Possible values are: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, and 3653. | `number` | `120` | no |
| cluster\_name | n/a | `string` | `"Name of existing ECS Cluster to deploy this app to"` | no |
| codedeploy\_deployment\_config\_name | Specifies the deployment configuration for CodeDeploy | `string` | `"CodeDeployDefault.ECSAllAtOnce"` | no |
| codedeploy\_role\_arn | Existing IAM CodeDeploy role ARN created by ECS cluster module | `any` | `null` | no |
| codedeploy\_wait\_time\_for\_cutover | Time in minutes to route the traffic to the new application deployment | `number` | `0` | no |
| codedeploy\_wait\_time\_for\_termination | Time in minutes to terminate the new deployment | `number` | `0` | no |
| container\_port | Port your container listens (used in the placeholder task definition) | `string` | `"8080"` | no |
| cpu | Hard limit for CPU for the container | `string` | `"0"` | no |
| create\_iam\_codedeployrole | Create Codedeploy IAM Role for ECS or not. | `bool` | `true` | no |
| deployment\_controller | Type of deployment controller. Valid values: CODE\_DEPLOY, ECS, EXTERNAL. | `string` | `"CODE_DEPLOY"` | no |
| efs\_mapping | A map of efs volume ids and paths to mount into the default task definition | `map(string)` | `{}` | no |
| fargate\_spot | Set true to use FARGATE\_SPOT capacity provider by default (only when launch\_type=FARGATE) | `bool` | `false` | no |
| hosted\_zone | Hosted Zone to create DNS record for this app | `string` | `""` | no |
| hostname | Hostname to create DNS record for this app | `string` | `""` | no |
| hostname\_create | Optional parameter to create or not a Route53 record | `string` | `"true"` | no |
| image | Docker image to deploy (can be a placeholder) | `string` | `"dnxsolutions/nginx-hello:latest"` | no |
| launch\_type | The launch type on which to run your service. The valid values are EC2 and FARGATE. Defaults to EC2. | `string` | `"EC2"` | no |
| launch\_type | The launch type on which to run your service. The valid values are EC2 and FARGATE. Defaults to EC2. | `string` | `"FARGATE"` | no |
| memory | Hard memory of the container | `string` | `"512"` | no |
| name | Name of your ECS service | `any` | n/a | yes |
| network\_mode | The Docker networking mode to use for the containers in the task. The valid values are none, bridge, awsvpc, and host. (REQUIRED IF 'LAUCH\_TYPE' IS FARGATE) | `any` | `null` | no |
Expand All @@ -88,13 +95,14 @@ In addition you have the option to create or not :
| nlb\_subnets\_ids | The subnets associated with the task or service. (REQUIRED IF 'LAUCH\_TYPE' IS FARGATE) | `any` | `null` | no |
| ordered\_placement\_strategy | Service level strategy rules that are taken into consideration during task placement. List from top to bottom in order of precedence. The maximum number of ordered\_placement\_strategy blocks is 5. | <pre>list(object({<br> field = string<br> expression = string<br> }))</pre> | `[]` | no |
| placement\_constraints | Rules that are taken into consideration during task placement. Maximum number of placement\_constraints is 10. | <pre>list(object({<br> type = string<br> expression = string<br> }))</pre> | `[]` | no |
| port | Port for target group to listen | `string` | `"80"` | no |
| ports | Port for target group to listen | <pre>list(object({<br> port = number<br> protocol = string<br> }))</pre> | <pre>[<br> {<br> "port": 80,<br> "protocol": "tcp"<br> }<br>]</pre> | no |
| security\_group\_ecs\_nodes\_inbound\_cidrs | ECS Nodes inbound allowed CIDRs for the security group. | `list(string)` | <pre>[<br> "0.0.0.0/0"<br>]</pre> | no |
| security\_groups | The security groups associated with the task or service | `any` | `null` | no |
| service\_health\_check\_grace\_period\_seconds | Time until your container starts serving requests | `number` | `0` | no |
| service\_role\_arn | Existing service role ARN created by ECS cluster module | `any` | n/a | yes |
| subnets | The subnets associated with the task or service. (REQUIRED IF 'LAUCH\_TYPE' IS FARGATE) | `any` | `null` | no |
| task\_role\_arn | Existing task role ARN created by ECS cluster module | `any` | n/a | yes |
| ulimits | Container ulimit settings. This is a list of maps, where each map should contain "name", "hardLimit" and "softLimit" | <pre>list(object({<br> name = string<br> hardLimit = number<br> softLimit = number<br> }))</pre> | `null` | no |
| vpc\_id | VPC ID to deploy this app to | `any` | n/a | yes |

## Outputs
Expand Down
62 changes: 59 additions & 3 deletions _variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,18 @@ variable "nlb_arn" {
description = "Networking LoadBalance ARN - Required if nlb=false or nlb_internal=false"
}

variable "port" {
default = "80"
variable "ports" {
default = [
{
port = 80
protocol = "tcp"
}
]
description = "Port for target group to listen"
type = list(object({
port = number
protocol = string
}))
}

variable "container_port" {
Expand Down Expand Up @@ -129,7 +138,7 @@ variable "placement_constraints" {
}

variable "launch_type" {
default = "EC2"
default = "FARGATE"
description = "The launch type on which to run your service. The valid values are EC2 and FARGATE. Defaults to EC2."
}

Expand Down Expand Up @@ -195,4 +204,51 @@ variable "security_group_ecs_nodes_inbound_cidrs" {
type = list(string)
default = ["0.0.0.0/0"]
description = "ECS Nodes inbound allowed CIDRs for the security group."
}

variable "efs_mapping" {
type = map(string)
description = "A map of efs volume ids and paths to mount into the default task definition"
default = {}
}

variable "ulimits" {
type = list(object({
name = string
hardLimit = number
softLimit = number
}))
description = "Container ulimit settings. This is a list of maps, where each map should contain \"name\", \"hardLimit\" and \"softLimit\""
default = null
}

variable "deployment_controller" {
default = "CODE_DEPLOY"
description = "Type of deployment controller. Valid values: CODE_DEPLOY, ECS, EXTERNAL."
}

variable "codedeploy_wait_time_for_cutover" {
default = 0
description = "Time in minutes to route the traffic to the new application deployment"
}

variable "codedeploy_wait_time_for_termination" {
default = 0
description = "Time in minutes to terminate the new deployment"
}

variable "codedeploy_deployment_config_name" {
default = "CodeDeployDefault.ECSAllAtOnce"
description = "Specifies the deployment configuration for CodeDeploy"
}

variable "create_iam_codedeployrole" {
type = bool
default = true
description = "Create Codedeploy IAM Role for ECS or not."
}

variable "codedeploy_role_arn" {
default = null
description = "Existing IAM CodeDeploy role ARN created by ECS cluster module"
}
13 changes: 8 additions & 5 deletions ecs-service.tf
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ resource "aws_ecs_service" "default" {
health_check_grace_period_seconds = var.service_health_check_grace_period_seconds
enable_execute_command = true

load_balancer {
target_group_arn = aws_lb_target_group.ecs_default_tcp.arn
container_name = var.name
container_port = var.container_port
dynamic load_balancer {
for_each = {for port in var.ports : port.port => port}
content {
target_group_arn = aws_lb_target_group.ecs_default_tcp[load_balancer.value.port].arn
container_name = var.name
container_port = load_balancer.value.port
}
}

dynamic "placement_constraints" {
Expand Down Expand Up @@ -45,6 +48,6 @@ resource "aws_ecs_service" "default" {
}

lifecycle {
ignore_changes = [load_balancer, task_definition, desired_count]
ignore_changes = [load_balancer, task_definition, desired_count, capacity_provider_strategy]
}
}
68 changes: 47 additions & 21 deletions ecs-task-definition.tf
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
locals {
port_mappings = [
for port in var.ports : {
hostPort = port.port
protocol = port.protocol
containerPort = port.port
}
]
}

resource "aws_ecs_task_definition" "default" {

family = "${var.cluster_name}-${var.name}"

execution_role_arn = var.task_role_arn
Expand All @@ -10,32 +21,47 @@ resource "aws_ecs_task_definition" "default" {
cpu = var.launch_type == "FARGATE" ? var.cpu : null
memory = var.launch_type == "FARGATE" ? var.memory : null

container_definitions = <<EOT
[
{
"name": "${var.name}",
"image": "${var.image}",
"cpu": ${var.cpu},
"memory": ${var.memory},
"essential": true,
"portMappings": [
{
"containerPort": ${var.container_port}
container_definitions = jsonencode([
{
name = var.name
image = var.image
cpu = var.cpu
memory = var.memory
essential = true
portMappings = local.port_mappings
logConfiguration = {
logDriver = "awslogs"
options = {
awslogs-group = aws_cloudwatch_log_group.default.name
awslogs-region = data.aws_region.current.name
awslogs-stream-prefix = "app"
}
}
],
"log_configuration": {
"log_driver": "awslogs",
"options": {
"awslogs-group": "${aws_cloudwatch_log_group.default.arn}",
"awslogs-region": "${data.aws_region.current.name}",
"awslogs-stream-prefix": "app"
mountPoints = length(var.efs_mapping) == 0 ? null : [{
sourceVolume = "efs-${keys(var.efs_mapping)[0]}"
containerPath = values(var.efs_mapping)[0]
}]
ulimits = var.ulimits
}
])

dynamic "volume" {
for_each = var.efs_mapping

content {
name = "efs-${volume.key}"

efs_volume_configuration {
file_system_id = volume.key
transit_encryption = "ENABLED"
authorization_config {
access_point_id = aws_efs_access_point.default[volume.key].id
}
}
}
}
]
EOT

lifecycle {
ignore_changes = [container_definitions]
}
}
}
12 changes: 12 additions & 0 deletions efs-access-point.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
resource "aws_efs_access_point" "default" {
for_each = var.efs_mapping
file_system_id = each.key
root_directory {
creation_info {
owner_gid = 0
owner_uid = 0
permissions = 755
}
path = "/${var.name}"
}
}
28 changes: 28 additions & 0 deletions iam-codedeploy.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
resource "aws_iam_role" "codedeploy_service" {

count = var.create_iam_codedeployrole == true ? 1 : 0

name = "codedeploy-service-${var.cluster_name}-${var.name}-${data.aws_region.current.name}"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "codedeploy.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}

resource "aws_iam_role_policy_attachment" "codedeploy_service" {
count = var.create_iam_codedeployrole == true ? 1 : 0
role = aws_iam_role.codedeploy_service[count.index].name
policy_arn = "arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS"
}
14 changes: 8 additions & 6 deletions nlb-target-group.tf
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
resource "aws_lb_listener" "ecs_tcp" {
for_each = {for port in var.ports : port.port => port}
load_balancer_arn = var.nlb ? try(aws_lb.default[0].arn, "") : var.nlb_arn
port = var.port
protocol = "TCP"
port = each.value.port
protocol = each.value.protocol

default_action {
type = "forward"
target_group_arn = aws_lb_target_group.ecs_default_tcp.arn
target_group_arn = aws_lb_target_group.ecs_default_tcp[each.value.port].arn
}
}

Expand All @@ -16,9 +17,10 @@ resource "random_string" "tg_nlb_prefix" {
}

resource "aws_lb_target_group" "ecs_default_tcp" {
name = format("%s-%s-tcp", substr("${var.cluster_name}-${var.name}", 0, 23), random_string.tg_nlb_prefix.result)
port = var.port
protocol = "TCP"
for_each = {for port in var.ports : port.port => port}
name = format("%s-%s-tcp", substr("${var.cluster_name}-${var.name}-${each.value.port}", 0, 23), random_string.tg_nlb_prefix.result)
port = each.value.port
protocol = each.value.protocol
vpc_id = var.vpc_id
target_type = var.launch_type == "FARGATE" ? "ip" : "instance"
}

0 comments on commit b7f4fa9

Please sign in to comment.