tf-aws-module_collection-ecs_app

module
v0.0.0-...-c2a5556 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 12, 2024 License: Apache-2.0

README

tf-aws-module_collection-ecs_app

License

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

Name Version
terraform ~> 1.0
aws ~> 5.0

Providers

Name Version
aws 5.66.0

Modules

Name Source Version
resource_names terraform.registry.launch.nttdata.com/module_library/resource_name/launch ~> 2.0
config_bucket terraform.registry.launch.nttdata.com/module_collection/s3_bucket/aws ~> 1.0
s3_bucket terraform-aws-modules/s3-bucket/aws ~> 3.14.1
s3_bucket_objects terraform-aws-modules/s3-bucket/aws//modules/object ~> 3.15.1
sg_ecs_service terraform-aws-modules/security-group/aws ~> 4.17.1
sg_alb terraform-aws-modules/security-group/aws ~> 4.17.1
acm terraform-aws-modules/acm/aws ~> 4.3.2
alb terraform-aws-modules/alb/aws ~> 8.7
ecr terraform.registry.launch.nttdata.com/module_collection/ecr/aws ~> 1.0
container_definitions git::https://github.com/cloudposse/terraform-aws-ecs-container-definition.git 0.58.2
service_discovery_service terraform.registry.launch.nttdata.com/module_primitive/service_discovery_service/aws ~> 1.0
ecs_task_execution_policy cloudposse/iam-policy/aws ~> 0.4.0
ecs_task_policy cloudposse/iam-policy/aws ~> 0.4.0
ecs_alb_service_task cloudposse/ecs-alb-service-task/aws ~> 0.67.1
alb_dns_record terraform.registry.launch.nttdata.com/module_primitive/dns_record/aws ~> 1.0
additional_cnames terraform.registry.launch.nttdata.com/module_primitive/dns_record/aws ~> 1.0

Resources

Name Type
aws_route53_zone.dns_zone data source

Inputs

Name Description Type Default Required
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
environment Environment in which the resource should be provisioned like dev, qa, prod etc. string "dev" no
environment_number The environment count for the respective environment. Defaults to 000. Increments in value of 1 string "000" no
resource_number The resource count for the respective resource. Defaults to 000. Increments in value of 1 string "000" no
region AWS Region in which the infra needs to be provisioned string "us-east-2" no
resource_names_map A 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_id The VPC ID of the VPC where infrastructure will be provisioned string n/a yes
private_subnets List 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/a yes
public_subnets List 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_mapping A list of subnet mapping blocks describing subnets to attach to network load balancer list(map(string)) [] no
ecs_cluster_arn ARN of the ECS cluster string n/a yes
ecs_svc_security_group Security 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)))
})
null no
alb_sg Security 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/a yes
target_groups List 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/a yes
load_balancer_type The type of the load balancer. Default is 'application'. Valid values are 'application' and 'network' string "application" no
is_internal Whether this load balancer is internal or public facing. If is_internal=false, then var.public_subnets must be specified bool true no
http_tcp_listeners List of HTTP TCP listeners
list(object({
port = number
protocol = string
action_type = string
redirect = any
}))
[] no
https_listeners List of HTTPs listeners
list(object({
port = number
protocol = string
#certificate_arn = string
}))
[] no
listener_ssl_policy_default The security policy if using HTTPS externally on the load balancer. See. string "ELBSecurityPolicy-TLS13-1-0-2021-06" no
redirect_to_https Whether all http traffic should be redirected to https. Valid only for ALB when https listeners are configured bool false no
enable_http2 Whether to enable HTTP/2.0 on the Application Load Balancer (not NLB). Default is false bool false no
enable_cross_zone_load_balancing Indicates whether cross zone load balancing should be enabled in application load balancers. bool false no
subject_alternate_names Additional domain names to be added to the certificate created for ALB. Domain names must be FQDN. list(string) [] no
alb_logs_bucket_id S3 bucket ID for ALB logs string "" no
alb_logs_bucket_prefix S3 bucket prefix for ALB logs string null no
dns_zone_name Name 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_zone Whether 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.
bool null no
additional_cnames Additional 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_json Print the container JSON object as output. Useful for debugging bool false no
app_environment Environment variables to be injected into the application containers map(string) {} no
app_secrets Secrets to be injected into the application containers. Map of secret Manager ARNs map(string) {} no
app_image Image 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>.
string n/a yes
containers Specifications 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_name OpenTelemetry Configuration file name string "" no
bind_mount_volumes Extra bind mount volumes to be created for this task list(object({ name = string })) [] no
ecs_launch_type The launch type of the ECS service. Default is FARGATE string "FARGATE" no
network_mode The network_mode of the ECS service. Default is awsvpc string "awsvpc" no
ignore_changes_task_definition Lifecycle ignore policy for task definition. If true, terraform won't detect changes when task_definition is changed outside of terraform bool true no
assign_public_ip If true, public IP will be assigned to this service task, else private IP bool false no
ignore_changes_desired_count Lifecycle ignore policy for desired_count. If true, terraform won't detect changes when desired_count is changed outside of terraform bool true no
task_cpu Amount of CPU to be allocated to the task string 512 no
task_memory Amount of Memory to be allocated to the task number 1024 no
health_check_grace_period_seconds Seconds 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 balancers number 0 no
deployment_minimum_healthy_percent The lower limit (as a percentage of desired_count) of the number of tasks that must remain running and healthy in a service during a deployment number 100 no
deployment_maximum_percent The upper limit of the number of tasks (as a percentage of desired_count) that can be running in a service during a deployment number 200 no
desired_count The number of instances of the task definition to place and keep running number 1 no
deployment_controller_type Type of deployment controller. Valid values are CODE_DEPLOY and ECS string "ECS" no
wait_for_steady_state If true, it will wait for the service to reach a steady state (like aws ecs wait services-stable) before continuing bool false no
redeploy_on_apply Redeploys the service everytime a terraform apply is executed. force_new_deployment should also be true for this flag to work bool false no
force_new_deployment Enable to force a new task deployment of the service when terraform apply is executed. bool false no
enable_service_discovery If true, the service discovery is enabled for this ECS Service bool false no
service_discovery_container_name The container name used for service discovery. Should match the name in var.containers. Mandatory in case of multiple containers string "" no
cloud_map_namespace_id Cloud Map Namespace ID string "" no
service_discovery_service_name Name of the Service Discovery Service string "" no
ecs_exec_role_custom_policy_json Custom policy to attach to ecs task execution role. Document must be valid json. string "" no
ecs_role_custom_policy_json Custom policy to attach to ecs task role. Document must be valid json. string "" no
create_config_bucket Whether to create a config s3 bucket to store configurations bool false no
config_objects A 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 file map(string) {} no
kms_s3_key_arn ARN of the AWS S3 key used for the config S3 bucket encryption string "" no
runtime_platform Zero 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_repo Whether 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.
bool false no
ecr_repo_name Name of the ECR repository. string null no
force_delete Whether to delete the repository even if it contains images bool false no
image_tag_mutability The tag mutability setting for the repository. Must be one of: MUTABLE or IMMUTABLE string "MUTABLE" no
principals_full_access Principal ARNs to provide with full access to the ECR list(string) [] no
principals_pull_though_access Principal ARNs to provide with pull though access to the ECR list(string) [] no
principals_push_access Principal ARNs to provide with push access to the ECR list(string) [] no
scan_images_on_push Indicates whether images are scanned after being pushed to the repository (true) or not (false) bool true no
tags A map of custom tags to be associated with the provisioned infrastructures. map(string) {} no

Outputs

Name Description
container_json Container json for the ECS Task Definition
alb_dns DNS of the Application Load Balancer
alb_arn ARN of the Application Load Balancer
alb_dns_records Custom DNS records for the ALB
alb_additional_dns_names Additional DNS records for the ALB
alb_target_group_arns ARNs of the ALB Target Groups
alb_target_group_names Names of the ALB Target Groups
s3_logs_arn ARN of S3 bucket for logs
s3_logs_id ID of S3 bucket for logs
service_discovery_service_arn ARN of Service Discovery Service
service_discovery_service_id ID of Service Discovery Service
config_bucket_name ID of the config S3 bucket
ecs_service_arn ECS Service ARN
ecs_service_name ECS Service name
ecs_task_definition_arn ECS task definition ARN
ecr_repository_url ECR Repository URL. Only valid when var.create_ecr_repo=true

Directories

Path Synopsis
tests

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL