# README
tf-aws-module_collection-ecs_appmesh_ingress
Overview
This terraform module creates an ingress route into the ECS Cluster using App Mesh through an Application Load Balancer and a Virtual Gateway
.
This module provisions an Application Load Balancer (ALB) with a HTTPs listener, listening on port 443
. All incoming traffic to this App Mesh are routed through this load balancer. The ALB routes traffic to the Virtual Gateway through the envoy proxy
running as an ECS Service.
Once the traffic reaches the Virtual Gateway listener, there can be multiple gateway routes
configured at the Virtual gateway to route the traffic to respective backends (ECS Service) through the configured Virtual Services that uses CloudMap service discovery to discover the ECS task instances.
This module just configures a single gateway route /health
for the ALB to perform health checks on. It is a single HTTP server configured to return 200 OK
.
The following AWS resources are provisioned in this module
- Application Load Balancer
- Listeners
- Target Group
- Security Group
- ACM Certificate for Load Balancer
- Public DNS record for Load Balancer. Optionally, creates a vanity URL (CName record)
- Virtual Gateway
- ECS Service
- Task Definition
- Security Group
- IAM roles for Task and Task Execution
- Health Check ECS application
- ECS Service
- Task Definition
- Virtual Service
- Virtual Node
- Service Discovery Service
- Private Certificate for Service Discovery Endpoint
- Virtual Gateway route for the above Health Check service
Below are some important considerations for the App Mesh Ingress configuration on ECS cluster
- All ingress traffic must pass through the Virtual Gateway
- In order for the ALB health checks to not fail for a deployed Virtual Gateway in ECS (without any actual apps deployed), it must have a backend configured for health-checks. In this module, we deploy a simple HTTP server for health checks of Virtual gateway.
- The backend services that do not need route from outside the Mesh may not have a route configured in Virtual Gateway.
- These services can be accessed by other services inside the Mesh using the Service Discovery endpoints (configured using CloudMap)
- The security group of these services must allow ingress traffic on the
application port
for these services
- All the backend services that need a route from outside the Mesh must have a
gateway route
configured in the Virtual gateway- Virtual gateway supports 2 kinds of routing
- Path based routing
- Matching Hostname header based routing
- Virtual gateway supports 2 kinds of routing
- ALB is configured with HTTPs listener. In order to provision certificates for TLS, a custom domain name must be configured for the ALB. TLS certificates cannot be provisioned for the default AWS URL.
- We use an
internal
ALB in this project - An HTTP listener is also configured to redirect all traffic at port
80
to443
. - An
A record
is created in thepublic
hosted zone in the AWS account - A certificate is provisioned for the
A record
as the first domain name in the ACM certificate- Additional SANs can be added for other CNAME records pointing to the ALB. This can be useful for
hostname
header based routing.
- Additional SANs can be added for other CNAME records pointing to the ALB. This can be useful for
- We use an
- ALB Target Group must be configured for
HTTPS
at port443
. The health check port should ideally be443
but can be any other port- The health check port must match the
application port
of theenvoy proxy
container in the task definition for Virtual Gateway ECS Service. The Virtual Gatewaylistener port
and thehealth check port
should also be the same. - The health check path can be anything, defaulting to
/
. If anything else is selected like/health
, the correspondinggateway route
must be configured to have the samematch_path_prefix
- The health check port must match the
- ALB Security group should allow ingress traffic on port
443
and optionally80
(will be auto redirected by HTTP listener) - Virtual Gateway Security Group configured on the ECS Service allows ingress traffic only from the
ALB Security Group
- Optionally, it can allow ingress traffic on
9901
port to check stats on the App Mesh.
- Optionally, it can allow ingress traffic on
Usage
Several sample variable files tfvars are available in the root directory which can be used to test this module. User needs to follow the below steps to execute this module
- Update the
tfvars
file to manually enter values for all fields marked within<>
to make the variable file usable - Create a file
provider.tf
with the below contents
If usingprovider "aws" { profile = "<profile_name>" region = "<region_name>" }
SSO
, make sure you are logged inaws sso login --profile <profile_name>
- Make sure terraform binary is installed on your local. Use command
type terraform
to find the installation location. If you are usingasdf
, you can runasfd install
and it will install the correct terraform version for you..tool-version
contains all the dependencies. - 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 and Guidelines
- The ALB is currently set to only work with TLS listener in this module. Unable to make it work for both HTTP and TLS listener in if/else. The parent module is not supporting
- If
tls_enforce=true
in this module, then ensure that any applications deployed to this cluster that uses this ingress must also havetls_enforce=true
, else those modules will fail to talk to the ingress - Sometimes the
terraform destroy
is unable to destroy all resources and fails with error shown below. But, this is mitigated by runningterraform destroy
again.
│ Error: deleting App Mesh Virtual Router (a0602f30-46ea-49ce-b545-a9255087d0dd): operation error App Mesh: DeleteVirtualRouter, https response error StatusCode: 409, RequestID: 0f756a18-5adc-43a4-9622-7590885de530, ResourceInUseException: VirtualRouter with name xxxx cannot be deleted because it is a VirtualService provider
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 forcommitlint
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
- 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'sMakefile
. Before you can runconfigure
, familiarize yourself with the variables in theMakefile
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.
- 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
- 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 pair 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
tolint
,validate
andplan
terraform code. - runs
conftests
.conftests
make surepolicy
checks are successful. - runs
terratest
. This is integration test suit. - runs
opa
tests
Know Issues
Currently, the encrypt at transit
is not supported in terraform. There is an open issue for this logged with Hashicorp - https://github.com/hashicorp/terraform-provider-aws/pull/26987
Requirements
Name | Version |
---|---|
terraform | ~> 1.0 |
aws | ~> 5.0 |
Providers
Name | Version |
---|---|
aws | 5.67.0 |
Modules
Name | Source | Version |
---|---|---|
resource_names | terraform.registry.launch.nttdata.com/module_library/resource_name/launch | ~> 2.0 |
sg_alb | terraform-aws-modules/security-group/aws | ~> 4.17.1 |
alb_logs_s3 | terraform-aws-modules/s3-bucket/aws | ~> 3.8.2 |
alb | terraform-aws-modules/alb/aws | ~> 8.7 |
alb_dns_records | terraform.registry.launch.nttdata.com/module_primitive/dns_record/aws | ~> 1.0 |
acm | terraform-aws-modules/acm/aws | ~> 4.3.2 |
sds | terraform.registry.launch.nttdata.com/module_primitive/service_discovery_service/aws | ~> 1.0 |
private_certs | terraform.registry.launch.nttdata.com/module_primitive/acm_private_cert/aws | ~> 1.0 |
virtual_gateway | terraform.registry.launch.nttdata.com/module_primitive/virtual_gateway/aws | ~> 1.0 |
ecs_task_execution_policy | cloudposse/iam-policy/aws | ~> 2.0.1 |
ecs_task_policy | cloudposse/iam-policy/aws | ~> 2.0.1 |
virtual_gateway_container_definition | git::https://github.com/cloudposse/terraform-aws-ecs-container-definition.git | tags/0.61.1 |
sg_ecs_service_vgw | terraform-aws-modules/security-group/aws | ~> 4.17.1 |
virtual_gateway_ecs_service | cloudposse/ecs-alb-service-task/aws | ~> 0.67.1 |
ecs_app_heart_beat | terraform.registry.launch.nttdata.com/module_collection/ecs_appmesh_app/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 | "backend" | no |
class_env | (Required) Environment where resource is going to be deployed. For example. dev, qa, uat | string | "dev" | no |
instance_env | Number that represents the instance of the environment. | number | 0 | no |
instance_resource | Number that represents the instance of the resource. | number | 0 | 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( | { | no |
vpc_id | (Required) The VPC ID of the VPC where infrastructure will be provisioned | string | n/a | yes |
private_subnets | (Required) 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. Required if load_balancer_type=network More details on the various attributes of subnet_mapping can be found at https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb#subnet_mapping When subnet_mapping is specified the private/public subnets variable is ignored. | list(map(string)) | [] | no |
vgw_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({ | null | no |
ecs_cluster_arn | (Required) ARN of the ECS Cluster where the ingress service will be created | string | n/a | yes |
app_mesh_id | (Required) ID of the App Mesh where virtual Gateway is to be created. ID and Name are the same for App Mesh | string | n/a | yes |
namespace_name | (Required) Name of the Cloud Map namespace to be used for Service Discovery | string | n/a | yes |
namespace_id | (Required) ID of the Cloud Map namespace to be used for Service Discovery | string | n/a | yes |
load_balancer_type | The type of the load balancer. Default is 'application'. Can be either application or network. | string | "application" | no |
is_internal | Whether this load balancer is internal or public facing. If is_internal=false, then var.public_subnets or subnet_mapping must be specified | bool | true | no |
alb_sg | (Required) Security Group for the ALB. https://github.com/terraform-aws-modules/terraform-aws-security-group/blob/master/rules.tf | object({ | n/a | yes |
target_groups | (Required) List of target groups for the ALB"target_type can be ip, instancehealth_check must be set for backend_protocol=HTTPS.Valid health_check attributes are healthy_threshold, unhealthy_threshold, path, port, protocol - protocol must be HTTP, HTTPS etc. | list(object({ | n/a | yes |
subject_alternate_names | Additional domain names to be added to the certificate created for ALB. Domain names must be FQDN. | list(string) | [] | 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 | n/a | yes |
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. | string | "" | 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 |
idle_timeout | The time in seconds that the connection is allowed to be idle. | number | 60 | 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 |
use_https_listeners | Whether to enable HTTPs in the ALB | bool | true | no |
listener_ssl_policy_default | The security policy if using HTTPS externally on the load balancer. See. | string | "ELBSecurityPolicy-TLS13-1-2-2021-06" | no |
private_ca_arn | ARN of the Private CA. This is used to sign private certificates used in App Mesh. Required when tls_enforce=true | string | "" | no |
tls_enforce | Whether to enforce TLS in App Mesh Virtual Gateway and services | bool | true | no |
vgw_logs_text_format | The text format. | string | null | no |
vgw_tls_mode | The mode for the listener’s Transport Layer Security (TLS) configuration. Must be one of DISABLED, PERMISSIVE, STRICT. | string | "DISABLED" | no |
vgw_tls_ports | If you specify a listener port other than 443, you must specify this field. | list(number) | [] | no |
vgw_health_check_path | The destination path for the health check request. | string | "/" | no |
vgw_health_check_protocol | The protocol for the health check request. Must be one of [http http2 grpc]. | string | "http" | no |
vgw_listener_port | The port mapping information for the listener. | number | n/a | yes |
vgw_listener_protocol | The protocol for the port mapping. Must be one of [http http2 grpc]. | string | "http" | no |
ecs_exec_role_managed_policy_arns | A Map (ARNs) of AWS managed policies to be attached to the ECS Task Exec role. | map(string) | {} | no |
ecs_role_managed_policy_arns | A Map (ARNs) of AWS managed policies to be attached to the ECS Task role. | map(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 |
envoy_proxy_image | Optional docker image of the envoy proxy in the format <docker_image>:<tag> Default is 840364872350.dkr.ecr.us-east-2.amazonaws.com/aws-appmesh-envoy:v1.29.6.0-prod | string | "" | 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 | number | 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 |
app_task_cpu | Amount of CPU to be allocated to the health check app task | number | 512 | no |
app_task_memory | Amount of Memory to be allocated to the health check app task | number | 1024 | no |
app_desired_count | The number of instances of the health check task definition to place and keep running | number | 1 | no |
app_image_tag | Docker image for the heartBeat application, in the format <docker_image><docker_tag> | string | n/a | yes |
app_port | The port at which the health check application is running | number | n/a | yes |
app_environment | Environment variables to be injected into the heart beat container. Sometimes helpful to set the non standard port | map(string) | {} | no |
app_security_group | Security group for the health check ECS application. Need to open ports if one wants to access the heart-beat application manually. | object({ | null | no |
match_path_prefix | Virtual gateway route path match. Must be same as the ALB health check path | string | "/" | no |
tags | A map of custom tags to be associated with the resources | map(string) | {} | no |
Outputs
Name | Description |
---|---|
resource_names | A map of resource_name_types to generated resource names used in this module |
app_sg_id | The ID of the VPC Endpoint Security Group |
alb_dns | AWS provided DNS record of the ALB |
alb_arn | The ID and ARN of the application load balancer created by the module |
alb_id | The ID and ARN of the application load balancer created by the module |
alb_sg_id | ID of the ALB Security Group |
alb_https_listener_arns | ARNs of the HTTPS Listeners attached to the ALB |
alb_http_listener_arns | ARNs of the HTTP Listeners attached to the ALB |
dns_zone_id | Zone ID of the hosted zone for ALB records provided as input. |
dns_zone_name | Name of the Route53 DNS Zone where custom DNS records will be created. Required if use_https_listeners=true |
alb_dns_records | Custom DNS record for the ALB |
alb_additional_dns_names | Additional DNS records for the ALB |
private_ca_arn | ARN of the Private CA. This is used to sign private certificates used in App Mesh. Required when TLS is enabled in App Mesh |
alb_cert_arn | ARN of the certificate provisioned for ALB by ACM |
virtual_gateway_cert_arn | ARN of the certificate provisioned for the virtual gateway when var.tls_enforce is true |
virtual_gateway_arn | ARN of the Virtual Gateway |
virtual_gateway_task_definition_name | Revision of the Virtual Gateway ECS app task definition. |
virtual_gateway_task_definition_version | Revision of the Virtual Gateway ECS app task definition. |
virtual_gateway_name | Name of the Virtual Gateway |
tls_enforce | Whether TLS is enforced on the Virtual Gateway. If true, all the Virtual Nodes should also enable TLS |
namespace_id | ID of the Cloud Map namespace to be used for Service Discovery |
namespace_name | Name of the Cloud Map namespace to be used for Service Discovery |
heartbeat_app_task_definition_name | Task Definition Version of the HeartBeat application |
heartbeat_app_task_definition_version | Task Definition Version of the HeartBeat application |