Categorygithub.com/launchbynttdata/tf-aws-module_collection-ecs_app
module
0.0.0-20240912141313-c2a5556e23e7
Repository: https://github.com/launchbynttdata/tf-aws-module_collection-ecs_app.git
Documentation: pkg.go.dev

# README

tf-aws-module_collection-ecs_app

License License: CC BY-NC-ND 4.0

Overview

This module is a reference architecture terraform module that will provision a ECS service with all its dependencies in a provided ECS Cluster.

Note: Currently because of a bug with the provider, it is not possible to use terraform to perform redeploy on the ECS Service. The bug https://github.com/hashicorp/terraform-provider-aws/issues/28070 restricts users to enable the flag force_new_deployement on the terraform module. Hence, for now we can use this module to deploy the application to ECS Service for the first time but all the subsequent deployments needs to be performed some other way (using AWS CLI is one option).

Usage

A sample variable file example.tfvars is available in the root directory which can be used to test this module. User needs to follow the below steps to execute this module

  1. Update the example.tfvars to manually enter values for all fields marked within <> to make the variable file usable
  2. Create a file provider.tf with the below contents
     provider "aws" {
       profile = "<profile_name>"
       region  = "<region_name>"
     }
    
    If using SSO, make sure you are logged in aws sso login --profile <profile_name>
  3. Make sure terraform binary is installed on your local. Use command type terraform to find the installation location. If you are using asdf, you can run asfd install and it will install the correct terraform version for you. .tool-version contains all the dependencies.
  4. Run the terraform to provision infrastructure on AWS
    # Initialize
    terraform init
    # Plan
    terraform plan -var-file example.tfvars
    # Apply (this is create the actual infrastructure)
    terraform apply -var-file example.tfvars -auto-approve
    

Known Issues

  1. When force_new_deployment=true, we get this error: https://github.com/hashicorp/terraform-provider-aws/issues/28070
  2. Access Logs in ALB: https://github.com/hashicorp/terraform-provider-aws/issues/16674

Pre-Commit hooks

.pre-commit-config.yaml file defines certain pre-commit hooks that are relevant to terraform, golang and common linting tasks. There are no custom hooks added.

commitlint hook enforces commit message in certain format. The commit contains the following structural elements, to communicate intent to the consumers of your commit messages:

  • fix: a commit of the type fix patches a bug in your codebase (this correlates with PATCH in Semantic Versioning).
  • feat: a commit of the type feat introduces a new feature to the codebase (this correlates with MINOR in Semantic Versioning).
  • BREAKING CHANGE: a commit that has a footer BREAKING CHANGE:, or appends a ! after the type/scope, introduces a breaking API change (correlating with MAJOR in Semantic Versioning). A BREAKING CHANGE can be part of commits of any type. footers other than BREAKING CHANGE: may be provided and follow a convention similar to git trailer format.
  • build: a commit of the type build adds changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
  • chore: a commit of the type chore adds changes that don't modify src or test files
  • ci: a commit of the type ci adds changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
  • docs: a commit of the type docs adds documentation only changes
  • perf: a commit of the type perf adds code change that improves performance
  • refactor: a commit of the type refactor adds code change that neither fixes a bug nor adds a feature
  • revert: a commit of the type revert reverts a previous commit
  • style: a commit of the type style adds code changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
  • test: a commit of the type test adds missing tests or correcting existing tests

Base configuration used for this project is commitlint-config-conventional (based on the Angular convention)

If you are a developer using vscode, this plugin may be helpful.

detect-secrets-hook prevents new secrets from being introduced into the baseline. TODO: INSERT DOC LINK ABOUT HOOKS

In order for pre-commit hooks to work properly

  • You need to have the pre-commit package manager installed. Here are the installation instructions.
  • pre-commit would install all the hooks when commit message is added by default except for commitlint hook. commitlint hook would need to be installed manually using the command below
pre-commit install --hook-type commit-msg

To test the resource group module locally

  1. For development/enhancements to this module locally, you'll need to install all of its components. This is controlled by the configure target in the project's Makefile. Before you can run configure, familiarize yourself with the variables in the Makefile and ensure they're pointing to the right places.
make configure

This adds in several files and directories that are ignored by git. They expose many new Make targets.

  1. The first target you care about is env. This is the common interface for setting up environment variables. The values of the environment variables will be used to authenticate with cloud provider from local development workstation.

make configure command will bring down aws_env.sh file on local workstation. Developer would need to modify this file, replace the environment variable values with relevant values.

These environment variables are used by terratest integration suit.

Then run this make target to set the environment variables on developer workstation.

make env
  1. The first target you care about is check.

Pre-requisites Before running this target it is important to ensure that, developer has created files mentioned below on local workstation under root directory of git repository that contains code for primitives/segments. Note that these files are aws specific. If primitive/segment under development uses any other cloud provider than AWS, this section may not be relevant.

  • A file named provider.tf with contents below
provider "aws" {
  profile = "<profile_name>"
  region  = "<region_name>"
}
  • A file named terraform.tfvars which contains key value pairs of variables used.

Note that since these files are added in gitignore they would not be checked in into primitive/segment's git repo.

After creating these files, for running tests associated with the primitive/segment, run

make check

If make check target is successful, developer is good to commit the code to primitive/segment's git repo.

make check target

  • runs terraform commands to lint,validate and plan terraform code.
  • runs conftests. conftests make sure policy checks are successful.
  • runs terratest. This is integration test suit.
  • runs opa tests

Requirements

NameVersion
terraform~> 1.0
aws~> 5.0

Providers

NameVersion
aws5.66.0

Modules

NameSourceVersion
resource_namesterraform.registry.launch.nttdata.com/module_library/resource_name/launch~> 2.0
config_bucketterraform.registry.launch.nttdata.com/module_collection/s3_bucket/aws~> 1.0
s3_bucketterraform-aws-modules/s3-bucket/aws~> 3.14.1
s3_bucket_objectsterraform-aws-modules/s3-bucket/aws//modules/object~> 3.15.1
sg_ecs_serviceterraform-aws-modules/security-group/aws~> 4.17.1
sg_albterraform-aws-modules/security-group/aws~> 4.17.1
acmterraform-aws-modules/acm/aws~> 4.3.2
albterraform-aws-modules/alb/aws~> 8.7
ecrterraform.registry.launch.nttdata.com/module_collection/ecr/aws~> 1.0
container_definitionsgit::https://github.com/cloudposse/terraform-aws-ecs-container-definition.git0.58.2
service_discovery_serviceterraform.registry.launch.nttdata.com/module_primitive/service_discovery_service/aws~> 1.0
ecs_task_execution_policycloudposse/iam-policy/aws~> 0.4.0
ecs_task_policycloudposse/iam-policy/aws~> 0.4.0
ecs_alb_service_taskcloudposse/ecs-alb-service-task/aws~> 0.67.1
alb_dns_recordterraform.registry.launch.nttdata.com/module_primitive/dns_record/aws~> 1.0
additional_cnamesterraform.registry.launch.nttdata.com/module_primitive/dns_record/aws~> 1.0

Resources

NameType
aws_route53_zone.dns_zonedata source

Inputs

NameDescriptionTypeDefaultRequired
logical_product_family(Required) Name of the product family for which the resource is created.
Example: org_name, department_name.
string"launch"no
logical_product_service(Required) Name of the product service for which the resource is created.
For example, backend, frontend, middleware etc.
string"ecs"no
environmentEnvironment in which the resource should be provisioned like dev, qa, prod etc.string"dev"no
environment_numberThe environment count for the respective environment. Defaults to 000. Increments in value of 1string"000"no
resource_numberThe resource count for the respective resource. Defaults to 000. Increments in value of 1string"000"no
regionAWS Region in which the infra needs to be provisionedstring"us-east-2"no
resource_names_mapA map of key to resource_name that will be used by tf-launch-module_library-resource_name to generate resource names
map(object({
name = string
max_length = optional(number, 60)
}))
{
"acm": {
"max_length": 60,
"name": "acm"
},
"alb": {
"max_length": 31,
"name": "alb"
},
"alb_http_listener": {
"max_length": 60,
"name": "http"
},
"alb_https_listener": {
"max_length": 60,
"name": "https"
},
"alb_sg": {
"max_length": 60,
"name": "albsg"
},
"alb_tg": {
"max_length": 31,
"name": "albtg"
},
"ecr": {
"max_length": 60,
"name": "ecr"
},
"ecs_service": {
"max_length": 60,
"name": "svc"
},
"ecs_sg": {
"max_length": 60,
"name": "ecssg"
},
"ecs_task": {
"max_length": 60,
"name": "td"
},
"s3_config": {
"max_length": 63,
"name": "conf"
},
"s3_logs": {
"max_length": 63,
"name": "logs"
},
"task_exec_policy": {
"max_length": 60,
"name": "execplcy"
},
"task_exec_role": {
"max_length": 60,
"name": "execrole"
},
"task_policy": {
"max_length": 60,
"name": "taskplcy"
},
"task_role": {
"max_length": 60,
"name": "taskrole"
},
"vpc": {
"max_length": 60,
"name": "vpc"
}
}
no
vpc_idThe VPC ID of the VPC where infrastructure will be provisionedstringn/ayes
private_subnetsList of private subnets. ECS services provisioned in private subnets would need NAT gateway to access internet.
Internal ALBs must be provisioned in private subnets
list(string)n/ayes
public_subnetsList of public subnets. ECS services provisioned in public subnets can access internet directly. External ALBs must be
provisioned in public subnets
list(string)[]no
subnet_mappingA list of subnet mapping blocks describing subnets to attach to network load balancerlist(map(string))[]no
ecs_cluster_arnARN of the ECS clusterstringn/ayes
ecs_svc_security_groupSecurity group for the Virtual Gateway ECS application. By default, it allows traffic from ALB on the app_port

More details on how to set the below fields can be found at
https://github.com/terraform-aws-modules/terraform-aws-security-group/blob/master/rules.tf
object({
ingress_rules = optional(list(string))
ingress_cidr_blocks = optional(list(string))
ingress_with_cidr_blocks = optional(list(map(string)))
egress_rules = optional(list(string))
egress_cidr_blocks = optional(list(string))
egress_with_cidr_blocks = optional(list(map(string)))
ingress_with_sg = optional(list(map(string)))
egress_with_sg = optional(list(map(string)))
})
nullno
alb_sgSecurity Group for the ALB. The ingress rules specified determine what ports on ALB are open to clients
and the egress rules specify how ALB can talk to upstream servers (ECS in this case)

More details on how to fill the below fields can be found at
https://github.com/terraform-aws-modules/terraform-aws-security-group/blob/master/rules.tf
object({
description = optional(string)
ingress_rules = optional(list(string))
ingress_cidr_blocks = optional(list(string))
egress_rules = optional(list(string))
egress_cidr_blocks = optional(list(string))
ingress_with_cidr_blocks = optional(list(map(string)))
egress_with_cidr_blocks = optional(list(map(string)))
})
n/ayes
target_groupsList of target groups for the ALB
The health_check can accept the following keys
- enabled, interval, port, path, healthy_threshold, unhealthy_threshold, timeout, protocol, matcher
list(object({
# Need to use name_prefix instead of name as the lifecycle property create_before_destroy is set
name_prefix = optional(string, "albtg")
backend_protocol = optional(string, "HTTP")
backend_port = optional(number, 80)
target_type = optional(string, "ip")
health_check = optional(map(string), {})
}))
n/ayes
load_balancer_typeThe type of the load balancer. Default is 'application'. Valid values are 'application' and 'network'string"application"no
is_internalWhether this load balancer is internal or public facing. If is_internal=false, then var.public_subnets must be specifiedbooltrueno
http_tcp_listenersList of HTTP TCP listeners
list(object({
port = number
protocol = string
action_type = string
redirect = any
}))
[]no
https_listenersList of HTTPs listeners
list(object({
port = number
protocol = string
#certificate_arn = string
}))
[]no
listener_ssl_policy_defaultThe security policy if using HTTPS externally on the load balancer. See.string"ELBSecurityPolicy-TLS13-1-0-2021-06"no
redirect_to_httpsWhether all http traffic should be redirected to https. Valid only for ALB when https listeners are configuredboolfalseno
enable_http2Whether to enable HTTP/2.0 on the Application Load Balancer (not NLB). Default is falseboolfalseno
enable_cross_zone_load_balancingIndicates whether cross zone load balancing should be enabled in application load balancers.boolfalseno
subject_alternate_namesAdditional domain names to be added to the certificate created for ALB. Domain names must be FQDN.list(string)[]no
alb_logs_bucket_idS3 bucket ID for ALB logsstring""no
alb_logs_bucket_prefixS3 bucket prefix for ALB logsstringnullno
dns_zone_nameName of the Route53 DNS Zone where custom DNS records will be created. Required if use_https_listeners=true. var.private_zone
must also be specified if this variable is not empty.

By default, an A record will be created for the ALB with the name as generated by module.resource_names["alb"].standard
In case, additional cnames are required, they can be specified in the additional_cnames variable
string""no
private_zoneWhether the dns_zone_name provided above is a private or public hosted zone. Required if dns_zone_name is not empty.
private_zone=true means the hosted zone is private and false means it is public.
boolnullno
additional_cnamesAdditional CNAME records to be created in the DNS zone pointing to the ALB. Must be FQDN in form of .<dns_zone_name>list(string)[]no
print_container_jsonPrint the container JSON object as output. Useful for debuggingboolfalseno
app_environmentEnvironment variables to be injected into the application containersmap(string){}no
app_secretsSecrets to be injected into the application containers. Map of secret Manager ARNsmap(string){}no
app_imageImage to be used for the application container in the form of <image>:<tag>

When var.create_ecr_repo is true, this variable must be specified as
<ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/<var.ecr_repo_name>:<tag>.
stringn/ayes
containersSpecifications for containers to be launched in ECS for this task
list(object({
name = string
command = optional(list(string), [])
essential = optional(bool, false)
cpu = optional(number, 0)
memory = optional(number, null)
memory_reservation = optional(number, null)
readonly_root_filesystem = optional(bool, false)
environment = optional(map(string), null)
secrets = optional(map(string), null)
mount_points = optional(list(object({
containerPath = optional(string)
readOnly = optional(bool, false)
sourceVolume = optional(string)
})), [])
port_mappings = optional(list(object({
containerPort = number
hostPort = optional(number)
protocol = optional(string, "tcp")
})), [])
healthcheck = optional(object({
retries = number
command = list(string)
timeout = number
interval = number
startPeriod = number
}), null)
user = optional(string, null)
container_depends_on = optional(list(object({
containerName = string
condition = string
})), [])
log_configuration = optional(object({
logDriver = optional(string, "awslogs")
options = object({
awslogs-group = string
awslogs-region = string
awslogs-create-group = optional(string, "true")
awslogs-stream-prefix = string
})
}), null)
}))
[]no
otel_config_file_nameOpenTelemetry Configuration file namestring""no
bind_mount_volumesExtra bind mount volumes to be created for this tasklist(object({ name = string }))[]no
ecs_launch_typeThe launch type of the ECS service. Default is FARGATEstring"FARGATE"no
network_modeThe network_mode of the ECS service. Default is awsvpcstring"awsvpc"no
ignore_changes_task_definitionLifecycle ignore policy for task definition. If true, terraform won't detect changes when task_definition is changed outside of terraformbooltrueno
assign_public_ipIf true, public IP will be assigned to this service task, else private IPboolfalseno
ignore_changes_desired_countLifecycle ignore policy for desired_count. If true, terraform won't detect changes when desired_count is changed outside of terraformbooltrueno
task_cpuAmount of CPU to be allocated to the taskstring512no
task_memoryAmount of Memory to be allocated to the tasknumber1024no
health_check_grace_period_secondsSeconds to ignore failing load balancer health checks on newly instantiated tasks to prevent premature shutdown, up to 7200. Only valid for services configured to use load balancersnumber0no
deployment_minimum_healthy_percentThe lower limit (as a percentage of desired_count) of the number of tasks that must remain running and healthy in a service during a deploymentnumber100no
deployment_maximum_percentThe upper limit of the number of tasks (as a percentage of desired_count) that can be running in a service during a deploymentnumber200no
desired_countThe number of instances of the task definition to place and keep runningnumber1no
deployment_controller_typeType of deployment controller. Valid values are CODE_DEPLOY and ECSstring"ECS"no
wait_for_steady_stateIf true, it will wait for the service to reach a steady state (like aws ecs wait services-stable) before continuingboolfalseno
redeploy_on_applyRedeploys the service everytime a terraform apply is executed. force_new_deployment should also be true for this flag to workboolfalseno
force_new_deploymentEnable to force a new task deployment of the service when terraform apply is executed.boolfalseno
enable_service_discoveryIf true, the service discovery is enabled for this ECS Serviceboolfalseno
service_discovery_container_nameThe container name used for service discovery. Should match the name in var.containers. Mandatory in case of multiple containersstring""no
cloud_map_namespace_idCloud Map Namespace IDstring""no
service_discovery_service_nameName of the Service Discovery Servicestring""no
ecs_exec_role_custom_policy_jsonCustom policy to attach to ecs task execution role. Document must be valid json.string""no
ecs_role_custom_policy_jsonCustom policy to attach to ecs task role. Document must be valid json.string""no
create_config_bucketWhether to create a config s3 bucket to store configurationsboolfalseno
config_objectsA map of objects to be created in config_bucket, where key is the object key in s3 bucket and value is the path of the filemap(string){}no
kms_s3_key_arnARN of the AWS S3 key used for the config S3 bucket encryptionstring""no
runtime_platformZero or one runtime platform configurations that containers in your task may use.
Map of strings with optional keys operating_system_family and cpu_architecture.
See runtime_platform docs https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition#runtime_platform
list(map(string))[]no
create_ecr_repoWhether to create an ECR repository for the application?
When create_ecr_repo=true, an ecr will be created as <ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/<var.ecr_repo_name>
The very first apply is expected to fail, as the ECR repo will be empty. One needs to push an image to this repo and
then trigger the apply again.
boolfalseno
ecr_repo_nameName of the ECR repository.stringnullno
force_deleteWhether to delete the repository even if it contains imagesboolfalseno
image_tag_mutabilityThe tag mutability setting for the repository. Must be one of: MUTABLE or IMMUTABLEstring"MUTABLE"no
principals_full_accessPrincipal ARNs to provide with full access to the ECRlist(string)[]no
principals_pull_though_accessPrincipal ARNs to provide with pull though access to the ECRlist(string)[]no
principals_push_accessPrincipal ARNs to provide with push access to the ECRlist(string)[]no
scan_images_on_pushIndicates whether images are scanned after being pushed to the repository (true) or not (false)booltrueno
tagsA map of custom tags to be associated with the provisioned infrastructures.map(string){}no

Outputs

NameDescription
container_jsonContainer json for the ECS Task Definition
alb_dnsDNS of the Application Load Balancer
alb_arnARN of the Application Load Balancer
alb_dns_recordsCustom DNS records for the ALB
alb_additional_dns_namesAdditional DNS records for the ALB
alb_target_group_arnsARNs of the ALB Target Groups
alb_target_group_namesNames of the ALB Target Groups
s3_logs_arnARN of S3 bucket for logs
s3_logs_idID of S3 bucket for logs
service_discovery_service_arnARN of Service Discovery Service
service_discovery_service_idID of Service Discovery Service
config_bucket_nameID of the config S3 bucket
ecs_service_arnECS Service ARN
ecs_service_nameECS Service name
ecs_task_definition_arnECS task definition ARN
ecr_repository_urlECR Repository URL. Only valid when var.create_ecr_repo=true

# Packages

No description provided by the author