Skip to content

Terraform registry repository for ECS service module

License

Notifications You must be signed in to change notification settings

boldlink/terraform-aws-ecs-service

Repository files navigation

License Latest Release Build Status Build Status Build Status Build Status Build Status Build Status Build Status Build Status

Description

This Terraform module creates an ECS service using FARGATE compatibilities.

Why choose this module

  • The module follows aws security best practices and uses checkov to ensure compliance.
  • Contains elaborate examples that you can use to setup your ecs-service within a very short time.
  • Deploy related multiple resources for your application at once

Examples available here

Troubleshooting ALB Target Group Health Check Failure

If you encounter any health check failures while using this module, please consider the following steps to troubleshoot the issue:

  1. Verify Docker Container Port Mapping: Ensure that the port mapping between the Docker container and the host port is accurately configured. Incorrect port mapping can lead to health check failures.

  2. Adjust ALB Health Check Interval: Some applications require a longer time to become fully operational. Make sure the health check interval of your Application Load Balancer (ALB) is appropriately set to allow enough time for the application to start and respond to health checks.

  3. Validate Target Group Health Check Endpoint: Verify that the endpoint used for the target group health checks is correct and returns a 200 status code. This ensures that the health checks are performed against the intended endpoint.

  4. Verify ALB Security Group Inbound Traffic: When using an Application Load Balancer, ensure that the port configured in your application is allowed in the ALB security group's inbound traffic rules. By default, the service security group in this module only permits traffic from the ALB security group for the specified ports. Adjust the security group rules accordingly to allow traffic from the desired ports.

For more detailed information on troubleshooting ALB health check failures, you can refer to this Stack Overflow post. It provides additional insights and solutions to common issues related to ALB health checks.

AWS ECS Service Deployment Control

This module supports AWS ECS Service Deployment Control where it provides the capability to trigger a new deployment for the ECS service, even without changes in the Terraform codebase. This feature ensures that the service is updated with the latest changes, even when using the same image tag instead of semantic versioning.

Use Case Scenarios:

Force Deployment: When deploying an ECS service using the same image tag, triggering a new deployment forces the service to update with the latest changes. This is particularly useful when you want to ensure that the service is always running the latest version of the containerized application, regardless of the image tag. It eliminates the need for manually updating the tag or using semantic versioning for each deployment.

  • To take advantage of this feature, ensure the var.force_new_deployment variable is set to true in your Terraform configuration. For this example, this feature is enabled. Disable it by ensuring the var.force_new_deployment to false.

Usage

NOTE: These examples use the latest version of this module

data "aws_partition" "current" {}

data "aws_ecs_cluster" "ecs" {
  cluster_name = local.supporting_resources_name
}

data "aws_kms_alias" "supporting_kms" {
  name = "alias/${local.supporting_resources_name}"
}

data "aws_vpc" "supporting" {
  filter {
    name   = "tag:Name"
    values = [local.supporting_resources_name]
  }
}

data "aws_subnets" "private" {
  filter {
    name   = "tag:Name"
    values = ["${local.supporting_resources_name}*.pri.*"]
  }
}

data "aws_subnet" "private" {
  for_each = toset(data.aws_subnets.private.ids)
  id       = each.value
}

data "aws_iam_policy_document" "ecs_assume_role_policy" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["ecs-tasks.amazonaws.com"]
    }
  }
}
locals {
  private_subnet_id = [
    for i in data.aws_subnet.private : i.id
  ]
  name                      = "minimum-example"
  cluster                   = data.aws_ecs_cluster.ecs.arn
  supporting_resources_name = "terraform-aws-ecs-service"
  vpc_id                    = data.aws_vpc.supporting.id
  private_subnets           = local.private_subnet_id
  partition                 = data.aws_partition.current.partition
  default_container_definitions = jsonencode(
    [
      {
        name      = local.name
        image     = "boldlink/flaskapp:latest"
        cpu       = 10
        memory    = 512
        essential = true
        portMappings = [
          {
            containerPort = 5000
            hostPort      = 5000
          }
        ]
      }
    ]
  )  
  task_execution_role_policy_doc = jsonencode(
    {
      Version = "2012-10-17",
      Statement = [{
        Effect = "Allow",
        Action = [
          "logs:CreateLogStream",
          "logs:PutLogEvents",
        ]
        Resource = ["arn:${local.partition}:logs:::log-group:${local.name}"]
        },
        {
          Effect = "Allow"
          Action = [
            "ecr:GetAuthorizationToken",
            "ecr:BatchCheckLayerAvailability",
            "ecr:GetDownloadUrlForLayer",
            "ecr:BatchGetImage",
            "logs:CreateLogStream",
            "logs:PutLogEvents"
          ]

          Resource = ["*"]
        }
    ] }
  )
}
module "ecs_service" {
  source                     = "../../"
  name                       = var.name
  family                     = "${var.name}-task-definition"
  network_mode               = var.network_mode
  cluster                    = local.cluster
  vpc_id                     = local.vpc_id
  task_role_policy           = data.aws_iam_policy_document.ecs_assume_role_policy.json
  task_execution_role        = data.aws_iam_policy_document.ecs_assume_role_policy.json
  task_execution_role_policy = local.task_execution_role_policy_doc
  container_definitions      = local.default_container_definitions
  kms_key_id                 = data.aws_kms_alias.supporting_kms.target_key_arn
  tags                       = local.tags
  lb_ingress_rules           = var.lb_ingress_rules

  network_configuration = {
    subnets = local.private_subnets
  }
}

Documentation

AWS ECS Service

Terraform ECS Documentation

Requirements

Name Version
terraform >= 0.14.11
aws >= 5.0.0
tls >= 3.0.0

Providers

Name Version
aws 5.51.1
tls 4.0.5

Modules

No modules.

Resources

Name Type
aws_acm_certificate.main resource
aws_appautoscaling_policy.stepscaling resource
aws_appautoscaling_policy.targetscaling resource
aws_appautoscaling_scheduled_action.this resource
aws_appautoscaling_target.this resource
aws_cloudwatch_log_group.main resource
aws_ecs_service.service resource
aws_ecs_task_definition.this resource
aws_iam_role.task_execution_role resource
aws_iam_role.task_role resource
aws_iam_role_policy.task_execution_role_policy resource
aws_iam_role_policy.task_role_policy resource
aws_kms_key.cloudwatch_log_group resource
aws_lb.main resource
aws_lb_listener.http_redirect resource
aws_lb_listener.https resource
aws_lb_listener.nlb resource
aws_lb_target_group.main_alb resource
aws_lb_target_group.main_nlb resource
aws_security_group.lb resource
aws_security_group.service resource
aws_security_group_rule.lb_egress resource
aws_security_group_rule.lb_ingress resource
aws_security_group_rule.service_egress resource
aws_security_group_rule.service_ingress resource
aws_security_group_rule.service_ingress_sg resource
aws_security_group_rule.service_with_lb_ingress resource
aws_wafregional_web_acl_association.main resource
aws_wafv2_web_acl_association.main resource
tls_private_key.default resource
tls_self_signed_cert.default resource
aws_caller_identity.current data source
aws_partition.current data source
aws_region.current data source

Inputs

Name Description Type Default Required
access_logs (Optional) Define an Access Logs block, requires a bucket name and an optional prefix. The S3 bucket must already exist. The default value is false.

access_logs = {
bucket = string
enabled = bollean
prefix = string
}
map(string) {} no
acm_certificate_arn ARN of ACM generated/third party certificate string null no
alb_subnets Subnet IDs for the application load balancer. list(string) [] no
associate_with_waf Whether to associate created ALB with AWS WAFv2 ACL bool false no
associate_with_wafregional Whether to associate created ALB with WAF Regional Web ACL bool false no
autoscale_role_arn (Optional) The ARN of the IAM role that allows Application AutoScaling to modify your scalable target on your behalf. string null no
cluster Amazon Resource Name (ARN) of cluster which the service runs on string null no
container_definitions Container definitions provided as valid JSON document. Default uses golang:alpine running a simple hello world. string null no
cpu Number of cpu units used by the task. If the requires_compatibilities is FARGATE this field is required. number 256 no
create_load_balancer Whether to create a load balancer for ecs. bool false no
create_task_definition Whether to create the task definition or not bool true no
default_type Type for default action string "forward" no
deployment_controller_type (Optional) Type of deployment controller string "ECS" no
desired_count The number of instances of a task definition number 1 no
drop_invalid_header_fields Indicates whether HTTP headers with header fields that are not valid are removed by the load balancer (true) or routed to targets (false). The default is false. Elastic Load Balancing requires that message header names contain only alphanumeric characters and hyphens. Only valid for Load Balancers of type application. bool false no
enable_autoscaling Whether to enable autoscaling or not for ecs bool false no
enable_deletion_protection If true, deletion of the load balancer will be disabled via the AWS API. This will prevent Terraform from deleting the load balancer. Defaults to false. bool false no
enable_execute_command value to enable execute command at the ecs service, default = false bool false no
enable_key_rotation Choose whether to enable key rotation bool true no
family (Required) A unique name for your task definition. string null no
force_new_deployment Enable to force a new task deployment of the service. This can be used to update tasks to use a newer Docker image with same image/tag combination (e.g., myimage:latest), roll Fargate tasks onto a newer platform version, or immediately deploy ordered_placement_strategy and placement_constraints updates. bool false no
healthy_threshold (Optional) Number of consecutive health checks successes required before considering an unhealthy target healthy. Defaults to 3. number 3 no
idle_timeout (Optional) The time in seconds that the connection is allowed to be idle. Only valid for Load Balancers of type application. Default: 60 number 60 no
internal (Optional) If true, the LB will be internal. bool false no
interval (Optional) Approximate amount of time, in seconds, between health checks of an individual target. The range is 5-300. For lambda target groups, it needs to be greater than the timeout of the underlying lambda. Defaults to 30. number 30 no
key_deletion_window_in_days The number of days before the key is deleted number 7 no
kms_key_id The KMS ARN for cloudwatch log group string null no
launch_type Launch type on which to run your service. The valid values are EC2, FARGATE, and EXTERNAL. string "FARGATE" no
lb_ingress_rules Ingress rules to add to the load balancer security group. The rules defined here will be used by service security group

lb_ingress_rules = [{
from_port = number
to_port = number
protocol = string
description = string
cidr_blocks = list(string)
}]
list(any) [] no
listener_port (Required) The port to listen on for the load balancer number 80 no
listener_protocol (Required) The protocol to listen on. Valid values are HTTP, HTTPS, TCP, or SSL string "HTTP" no
load_balancer (Optional) Configuration block for load balancers, accepts the following arguments:

load_balancer = [{
container_name = string # Name of the container to associate with the load balancer
container_port = number # Port number the container listens on
target_group_arn = string # ARN of the target group to associate with the load balancer when using an external Alb/NLB
}]
any [] no
load_balancer_type (Optional) The type of load balancer to create. Possible values are application, gateway, or network. The default value is application. string "application" no
matcher (May be required) Response codes to use when checking for a healthy responses from a target. You can specify multiple values (for example, 200,202 for HTTP(s)) string null no
max_capacity (Required) The max capacity of the scalable target. number 2 no
memory Amount (in MiB) of memory used by the task. If the requires_compatibilities is FARGATE this field is required. number 1024 no
min_capacity (Required) The min capacity of the scalable target. number 1 no
name The service name. string n/a yes
network_configuration (Optional) Network configuration for the service. This parameter is required for task definitions that use the awsvpc network mode to receive their own Elastic Network Interface, and it is not supported for other network modes. any {} no
network_mode Docker networking mode to use for the containers in the task. Valid values are none, bridge, awsvpc, and host. string "none" no
path (May be required) Destination for the health check request. Required for HTTP/HTTPS ALB and HTTP NLB. Only applies to HTTP/HTTPS. string "/" no
propagate_tags (Optional) Whether to propagate the tags from the task definition or the service to the tasks. The valid values are SERVICE and TASK_DEFINITION string "TASK_DEFINITION" no
requires_compatibilities Set of launch types required by the task. The valid values are EC2 and FARGATE. list(string) [] no
retention_in_days 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, 3653, and 0. If you select 0, the events in the log group are always retained and never expire. number 3653 no
scalable_dimension (Required) The scalable dimension of the scalable target. string "" no
scheduled_actions Scheduled actions to apply to the ecs scalable target.
list(object({
name = string
schedule = string
min_capacity = number
max_capacity = number
timezone = string
}))
[] no
self_signed_cert_common_name Distinguished name string "devboldlink.wpengine.com" no
self_signed_cert_organization The organization owning this self signed certificate string "Boldlink-SIG" no
service_ingress_rules Ingress rules to add to the service security group.

service_ingress_rules = [{
from_port = number
to_port = number
protocol = string
description = string
cidr_blocks = list(string)
}]
list(any) [] no
service_ingress_rules_sg Ingress rules to add to the service security group.

service_ingress_rules = [{
from_port = number
to_port = number
protocol = string
description = string
source_security_group_id = string
}]
list(any) [] no
service_namespace (Required) The AWS service namespace of the scalable target. string "ecs" no
ssl_policy (Optional) Name of the SSL Policy for the listener. Required if protocol is HTTPS or TLS string "ELBSecurityPolicy-TLS-1-2-2017-01" no
step_scaling_policies Scaling policies to apply to the scalable target. Supported policy types are StepScaling and TargetTrackingScaling.

list(object({
name = string
policy_type = string
step_scaling_policy_configuration = object({
adjustment_type = string
cooldown = number
metric_aggregation_type = string
step_adjustments = list(object({
metric_interval_lower_bound = number
metric_interval_upper_bound = number
scaling_adjustment = number
}))
})
}))
any [] no
tags Key Value tags to apply to the resources map(string) {} no
target_scaling_policies Scaling policies to apply to the scalable target. Supported policy types are StepScaling and SepScaling.

list(object({
name = string
policy_type = string
target_tracking_scaling_policy_configuration = object({
target_value = number
scale_in_cooldown = number
scale_out_cooldown = number
predefined_metric_specification = object({
predefined_metric_type = string
})
})
}))
any [] no
target_type Type of target that you must specify when registering targets with this target group. See doc for supported values. The default is instance. string "ip" no
task_assume_role_policy The assume role policy for the task role string "" no
task_execution_assume_role_policy The assume role policy for the task execution role string null no
task_execution_role_policy Specify the IAM policy for task definition task execution string "" no
task_role_policy Specify the IAM policy for task role string "" no
tasks_maximum_percent Upper limit on the number of running tasks. number 200 no
tasks_minimum_healthy_percent Lower limit on the number of running tasks. number 100 no
tg_port Port on which targets receive traffic, unless overridden when registering a specific target. Required when target_type is instance or ip. Does not apply when target_type is lambda. number 80 no
tg_protocol Protocol to use for routing traffic to the targets. Should be one of GENEVE, HTTP, HTTPS, TCP, TCP_UDP, TLS, or UDP. Required when target_type is instance or ip. Does not apply when target_type is lambda. string "HTTP" no
triggers Map of arbitrary keys and values that, when changed, will trigger an in-place update (redeployment). Useful with plaintimestamp()

triggers = {
redeployment = plantimestamp()
}
map(string) {} no
volume_name Name of the volume. This name is referenced in the sourceVolume parameter of container definition in the mountPoints section. string "service-storage" no
vpc_id VPC ID to be used by ECS. string null no
wafregional_acl_id The ID of WAF Regional Web ACL to associate load balancer with string null no
web_acl_arn The ARN of WAF web acl to associate load balancer with string null no

Outputs

Name Description
cloudwatch_log_group_arn ARN of cloudwatch log group
iam_role_arn_role The ARN of IAM task role
iam_role_create_date_role Creation date of IAM task role
iam_role_id_role ID of IAM task role
iam_role_name_role Name of IAM task role
iam_role_unique_id_role Unique ID of IAM task role
lb_arn The ARN of the load balancer (matches id)
lb_dns_name DNS name of load balancer
lb_dns_zone_id DNS zone id of load balancer
lb_sg_arn ID of the load balancer security group.
lb_sg_id ARN of the load balancer security group.
service_sg_arn ID of the service security group.
service_sg_id ARN of the service security group.
task_definition_arn Full ARN of the Task Definition (including both family and revision)
task_definition_arn_without_revision ARN of the Task Definition with the trailing revision removed. This may be useful for situations where the latest task definition is always desired. If a revision isn't specified, the latest ACTIVE revision is used.
task_definition_revision Revision of the task in a particular family.

Third party software

This repository uses third party software:

  • pre-commit - Used to help ensure code and documentation consistency
    • Install with brew install pre-commit
    • Manually use with pre-commit run
  • terraform 0.14.11 For backwards compatibility we are using version 0.14.11 for testing making this the min version tested and without issues with terraform-docs.
  • terraform-docs - Used to generate the Inputs and Outputs sections
    • Install with brew install terraform-docs
    • Manually use via pre-commit
  • tflint - Used to lint the Terraform code
    • Install with brew install tflint
    • Manually use via pre-commit

Supporting resources:

The example stacks are used by BOLDLink developers to validate the modules by building an actual stack on AWS.

Some of the modules have dependencies on other modules (ex. Ec2 instance depends on the VPC module) so we create them first and use data sources on the examples to use the stacks.

Any supporting resources will be available on the tests/supportingResources and the lifecycle is managed by the Makefile targets.

Resources on the tests/supportingResources folder are not intended for demo or actual implementation purposes, and can be used for reference.

Makefile

The makefile contained in this repo is optimized for linux paths and the main purpose is to execute testing for now.

  • Create all tests stacks including any supporting resources:
make tests
  • Clean all tests except existing supporting resources:
make clean
  • Clean supporting resources - this is done separately so you can test your module build/modify/destroy independently.
make cleansupporting
  • !!!DANGER!!! Clean the state files from examples and test/supportingResources - use with CAUTION!!!
make cleanstatefiles

BOLDLink-SIG 2024

About

Terraform registry repository for ECS service module

Resources

License

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •