From 41bce9eafabe42cdd8690dd7161c078556365a50 Mon Sep 17 00:00:00 2001
From: oleksa <99fifo@gmail.com>
Date: Thu, 9 Jan 2025 15:34:10 +0200
Subject: [PATCH] Add multy TGs example
---
examples/multy-tg/README.md | 93 +++++++
examples/multy-tg/main.tf | 471 +++++++++++++++++++++++++++++++++
examples/multy-tg/outputs.tf | 132 +++++++++
examples/multy-tg/variables.tf | 0
examples/multy-tg/versions.tf | 10 +
5 files changed, 706 insertions(+)
create mode 100644 examples/multy-tg/README.md
create mode 100644 examples/multy-tg/main.tf
create mode 100644 examples/multy-tg/outputs.tf
create mode 100644 examples/multy-tg/variables.tf
create mode 100644 examples/multy-tg/versions.tf
diff --git a/examples/multy-tg/README.md b/examples/multy-tg/README.md
new file mode 100644
index 00000000..b33e14cc
--- /dev/null
+++ b/examples/multy-tg/README.md
@@ -0,0 +1,93 @@
+# ECS Cluster w/ Multy target groups on ALB
+
+Configuration in this directory creates:
+
+- ECS cluster using EC2 autoscaling groups
+- Autoscaling groups with IAM instance profile to be used by ECS cluster
+- Example ECS service that utilizes
+ - Mounts a host volume into the container definition
+ - Load balancer target group attachment
+ - Security group for access to the example service
+
+## Usage
+
+To run this example you need to execute:
+
+```bash
+$ terraform init
+$ terraform plan
+$ terraform apply
+```
+
+Note that this example may create resources which will incur monetary charges on your AWS bill. Run `terraform destroy` when you no longer need these resources.
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.0 |
+| [aws](#requirement\_aws) | >= 4.66.1 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | >= 4.66.1 |
+
+## Modules
+
+| Name | Source | Version |
+|------|--------|---------|
+| [alb](#module\_alb) | terraform-aws-modules/alb/aws | ~> 9.0 |
+| [autoscaling](#module\_autoscaling) | terraform-aws-modules/autoscaling/aws | ~> 6.5 |
+| [autoscaling\_sg](#module\_autoscaling\_sg) | terraform-aws-modules/security-group/aws | ~> 5.0 |
+| [ecs\_cluster](#module\_ecs\_cluster) | ../../modules/cluster | n/a |
+| [ecs\_service](#module\_ecs\_service) | ../../modules/service | n/a |
+| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 |
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source |
+| [aws_ssm_parameter.ecs_optimized_ami](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
+
+## Inputs
+
+No inputs.
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [cluster\_arn](#output\_cluster\_arn) | ARN that identifies the cluster |
+| [cluster\_autoscaling\_capacity\_providers](#output\_cluster\_autoscaling\_capacity\_providers) | Map of capacity providers created and their attributes |
+| [cluster\_capacity\_providers](#output\_cluster\_capacity\_providers) | Map of cluster capacity providers attributes |
+| [cluster\_id](#output\_cluster\_id) | ID that identifies the cluster |
+| [cluster\_name](#output\_cluster\_name) | Name that identifies the cluster |
+| [service\_autoscaling\_policies](#output\_service\_autoscaling\_policies) | Map of autoscaling policies and their attributes |
+| [service\_autoscaling\_scheduled\_actions](#output\_service\_autoscaling\_scheduled\_actions) | Map of autoscaling scheduled actions and their attributes |
+| [service\_container\_definitions](#output\_service\_container\_definitions) | Container definitions |
+| [service\_iam\_role\_arn](#output\_service\_iam\_role\_arn) | Service IAM role ARN |
+| [service\_iam\_role\_name](#output\_service\_iam\_role\_name) | Service IAM role name |
+| [service\_iam\_role\_unique\_id](#output\_service\_iam\_role\_unique\_id) | Stable and unique string identifying the service IAM role |
+| [service\_id](#output\_service\_id) | ARN that identifies the service |
+| [service\_name](#output\_service\_name) | Name of the service |
+| [service\_task\_definition\_arn](#output\_service\_task\_definition\_arn) | Full ARN of the Task Definition (including both `family` and `revision`) |
+| [service\_task\_definition\_revision](#output\_service\_task\_definition\_revision) | Revision of the task in a particular family |
+| [service\_task\_exec\_iam\_role\_arn](#output\_service\_task\_exec\_iam\_role\_arn) | Task execution IAM role ARN |
+| [service\_task\_exec\_iam\_role\_name](#output\_service\_task\_exec\_iam\_role\_name) | Task execution IAM role name |
+| [service\_task\_exec\_iam\_role\_unique\_id](#output\_service\_task\_exec\_iam\_role\_unique\_id) | Stable and unique string identifying the task execution IAM role |
+| [service\_task\_set\_arn](#output\_service\_task\_set\_arn) | The Amazon Resource Name (ARN) that identifies the task set |
+| [service\_task\_set\_id](#output\_service\_task\_set\_id) | The ID of the task set |
+| [service\_task\_set\_stability\_status](#output\_service\_task\_set\_stability\_status) | The stability status. This indicates whether the task set has reached a steady state |
+| [service\_task\_set\_status](#output\_service\_task\_set\_status) | The status of the task set |
+| [service\_tasks\_iam\_role\_arn](#output\_service\_tasks\_iam\_role\_arn) | Tasks IAM role ARN |
+| [service\_tasks\_iam\_role\_name](#output\_service\_tasks\_iam\_role\_name) | Tasks IAM role name |
+| [service\_tasks\_iam\_role\_unique\_id](#output\_service\_tasks\_iam\_role\_unique\_id) | Stable and unique string identifying the tasks IAM role |
+
+
+## License
+
+Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-ecs/blob/master/LICENSE).
diff --git a/examples/multy-tg/main.tf b/examples/multy-tg/main.tf
new file mode 100644
index 00000000..5a52ca8f
--- /dev/null
+++ b/examples/multy-tg/main.tf
@@ -0,0 +1,471 @@
+provider "aws" {
+ region = local.region
+}
+
+data "aws_availability_zones" "available" {}
+
+locals {
+ region = "eu-west-1"
+ name = "ex-${basename(path.cwd)}"
+
+ vpc_cidr = "10.0.0.0/16"
+ azs = slice(data.aws_availability_zones.available.names, 0, 3)
+
+ container_port = 80
+
+ tags = {
+ Name = local.name
+ Example = local.name
+ Repository = "https://github.com/terraform-aws-modules/terraform-aws-ecs"
+ }
+}
+
+################################################################################
+# Cluster
+################################################################################
+
+module "ecs_cluster" {
+ source = "../../modules/cluster"
+
+ cluster_name = local.name
+
+ # Capacity provider - autoscaling groups
+ default_capacity_provider_use_fargate = false
+ autoscaling_capacity_providers = {
+ # On-demand instances
+ ex_1 = {
+ auto_scaling_group_arn = module.autoscaling["ex_1"].autoscaling_group_arn
+ managed_termination_protection = "ENABLED"
+
+ managed_scaling = {
+ maximum_scaling_step_size = 5
+ minimum_scaling_step_size = 1
+ status = "ENABLED"
+ target_capacity = 60
+ }
+
+ default_capacity_provider_strategy = {
+ weight = 60
+ base = 20
+ }
+ }
+ }
+
+ tags = local.tags
+}
+
+################################################################################
+# Service
+################################################################################
+
+module "ecs_service" {
+ source = "../../modules/service"
+
+ # Service
+ name = "NGINX"
+ cluster_arn = module.ecs_cluster.arn
+
+ # Task Definition
+ requires_compatibilities = ["EC2"]
+ cpu = 256
+ memory = 256
+ capacity_provider_strategy = {
+ # On-demand instances
+ ex_1 = {
+ capacity_provider = module.ecs_cluster.autoscaling_capacity_providers["ex_1"].name
+ weight = 1
+ base = 1
+ }
+ }
+
+ # Container definition(s)
+ container_definitions = {
+ ("nginx") = {
+ image = "nginx:latest"
+ port_mappings = [
+ {
+ name = "nginx"
+ containerPort = 80
+ protocol = "tcp"
+ }
+ ]
+ entrypoint = [
+ "/bin/bash",
+ "-c"
+ ]
+ command = [
+ "mkdir -p /usr/share/nginx/html/nginx ; echo '
Current Time Current Time
' > /usr/share/nginx/html/nginx/index.html && exec nginx -g 'daemon off;'"
+ ]
+
+ # Example image used requires access to write to root filesystem
+ readonly_root_filesystem = false
+
+ enable_cloudwatch_logging = true
+ create_cloudwatch_log_group = true
+ cloudwatch_log_group_name = "/aws/ecs/nginx/nginx"
+ cloudwatch_log_group_retention_in_days = 7
+
+ log_configuration = {
+ logDriver = "awslogs"
+ }
+ }
+ }
+
+ load_balancer = {
+ service = {
+ target_group_arn = module.alb.target_groups["nginx"].arn
+ container_name = "nginx"
+ container_port = 80
+ }
+ }
+
+ subnet_ids = module.vpc.public_subnets
+ security_group_rules = {
+ alb_http_ingress = {
+ type = "ingress"
+ from_port = 80
+ to_port = 80
+ protocol = "tcp"
+ description = "Service port"
+ source_security_group_id = module.alb.security_group_id
+ }
+ }
+
+ tags = {
+ Name = "nginx"
+ Example = "nginx"
+ Repository = "https://github.com/terraform-aws-modules/terraform-aws-ecs"
+ }
+}
+
+################################################################################
+# Service
+################################################################################
+
+module "ecs_service_2" {
+ source = "../../modules/service"
+
+ # Service
+ name = local.name
+ cluster_arn = module.ecs_cluster.arn
+
+ # Task Definition
+ requires_compatibilities = ["EC2"]
+ cpu = 256
+ memory = 256
+ capacity_provider_strategy = {
+ # On-demand instances
+ ex_1 = {
+ capacity_provider = module.ecs_cluster.autoscaling_capacity_providers["ex_1"].name
+ weight = 1
+ base = 1
+ }
+ }
+
+ # Container definition(s)
+ container_definitions = {
+ ("docker-labs") = {
+ image = "docker/getting-started:latest"
+ port_mappings = [
+ {
+ name = "docker-labs"
+ containerPort = local.container_port
+ protocol = "tcp"
+ }
+ ]
+
+ # Example image used requires access to write to root filesystem
+ readonly_root_filesystem = false
+
+ enable_cloudwatch_logging = true
+ create_cloudwatch_log_group = true
+ cloudwatch_log_group_name = "/aws/ecs/${local.name}/docker-labs"
+ cloudwatch_log_group_retention_in_days = 7
+
+ log_configuration = {
+ logDriver = "awslogs"
+ }
+ }
+ }
+
+ load_balancer = {
+ service = {
+ target_group_arn = module.alb.target_groups["docker-labs"].arn
+ container_name = "docker-labs"
+ container_port = local.container_port
+ }
+ }
+
+ subnet_ids = module.vpc.private_subnets
+ security_group_rules = {
+ alb_http_ingress = {
+ type = "ingress"
+ from_port = local.container_port
+ to_port = local.container_port
+ protocol = "tcp"
+ description = "Service port"
+ source_security_group_id = module.alb.security_group_id
+ }
+ }
+
+ tags = local.tags
+}
+
+
+################################################################################
+# Supporting Resources
+################################################################################
+
+# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html#ecs-optimized-ami-linux
+data "aws_ssm_parameter" "ecs_optimized_ami" {
+ name = "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended"
+}
+
+module "alb" {
+ source = "terraform-aws-modules/alb/aws"
+ version = "~> 9.0"
+
+ name = local.name
+
+ load_balancer_type = "application"
+
+ vpc_id = module.vpc.vpc_id
+ subnets = module.vpc.public_subnets
+
+ # For example only
+ enable_deletion_protection = false
+
+ # Security Group
+ security_group_ingress_rules = {
+ all_http = {
+ from_port = 80
+ to_port = 80
+ ip_protocol = "tcp"
+ cidr_ipv4 = "0.0.0.0/0"
+ }
+ }
+ security_group_egress_rules = {
+ all = {
+ ip_protocol = "-1"
+ cidr_ipv4 = module.vpc.vpc_cidr_block
+ }
+ }
+
+ listeners = {
+ ex_http = {
+ port = 80
+ protocol = "HTTP"
+
+ forward = {
+ target_group_key = "docker-labs"
+ }
+
+ rules = {
+
+ docker-labs = {
+ actions = [{
+ type = "weighted-forward"
+ target_groups = [
+ {
+ target_group_key = "docker-labs"
+ }
+ ]
+ stickiness = {
+ enabled = true
+ duration = 3600
+ }
+ }]
+ conditions = [{
+ path_pattern = {
+ values = ["/tutorial/*"]
+ }
+ tags = {
+ Name = "dgs"
+ Example = "dgs"
+ }
+ }]
+ }
+
+ nginx = {
+ actions = [{
+ type = "weighted-forward"
+ target_groups = [
+ {
+ target_group_key = "nginx"
+ }
+ ]
+ stickiness = {
+ enabled = true
+ duration = 3600
+ }
+ }]
+ conditions = [{
+ path_pattern = {
+ values = ["/nginx/*"]
+ }
+ }]
+ tags = {
+ Name = "nginx"
+ Example = "nginx"
+ }
+ }
+
+ }
+ }
+ }
+
+ target_groups = {
+ docker-labs = {
+ backend_protocol = "HTTP"
+ backend_port = local.container_port
+ target_type = "ip"
+ deregistration_delay = 5
+ load_balancing_cross_zone_enabled = true
+
+ health_check = {
+ enabled = true
+ healthy_threshold = 5
+ interval = 30
+ matcher = "200"
+ path = "/"
+ port = "traffic-port"
+ protocol = "HTTP"
+ timeout = 5
+ unhealthy_threshold = 2
+ }
+
+ # Theres nothing to attach here in this definition. Instead,
+ # ECS will attach the IPs of the tasks to this target group
+ create_attachment = false
+ }
+ nginx = {
+ backend_protocol = "HTTP"
+ backend_port = local.container_port
+ target_type = "ip"
+ deregistration_delay = 5
+ load_balancing_cross_zone_enabled = true
+
+ health_check = {
+ enabled = true
+ healthy_threshold = 5
+ interval = 30
+ matcher = "200"
+ path = "/"
+ port = "traffic-port"
+ protocol = "HTTP"
+ timeout = 5
+ unhealthy_threshold = 2
+ }
+
+ # Theres nothing to attach here in this definition. Instead,
+ # ECS will attach the IPs of the tasks to this target group
+ create_attachment = false
+ }
+ }
+
+ tags = local.tags
+}
+
+module "autoscaling" {
+ source = "terraform-aws-modules/autoscaling/aws"
+ version = "~> 6.5"
+
+ for_each = {
+ # On-demand instances
+ ex_1 = {
+ instance_type = "t3.micro"
+ use_mixed_instances_policy = false
+ mixed_instances_policy = {}
+
+ user_data = <<-EOF
+ #!/bin/bash
+
+ # Write ECS configuration
+ cat <<'EOT' > /etc/ecs/ecs.config
+ ECS_CLUSTER=${local.name}
+ ECS_LOGLEVEL=debug
+ ECS_CONTAINER_INSTANCE_TAGS=${jsonencode(local.tags)}
+ ECS_ENABLE_TASK_IAM_ROLE=true
+ EOT
+ EOF
+
+
+ }
+ }
+
+ name = "${local.name}-${each.key}"
+
+ image_id = jsondecode(data.aws_ssm_parameter.ecs_optimized_ami.value)["image_id"]
+ instance_type = each.value.instance_type
+
+ security_groups = [module.autoscaling_sg.security_group_id]
+ user_data = base64encode(each.value.user_data)
+ ignore_desired_capacity_changes = true
+
+ create_iam_instance_profile = true
+ iam_role_name = local.name
+ iam_role_description = "ECS role for ${local.name}"
+ iam_role_policies = {
+ AmazonEC2ContainerServiceforEC2Role = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
+ AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
+ }
+
+ vpc_zone_identifier = module.vpc.public_subnets
+ health_check_type = "EC2"
+ min_size = 1
+ max_size = 2
+ desired_capacity = 1
+
+ # https://github.com/hashicorp/terraform-provider-aws/issues/12582
+ autoscaling_group_tags = {
+ AmazonECSManaged = true
+ }
+
+ # Required for managed_termination_protection = "ENABLED"
+ protect_from_scale_in = true
+
+ # Spot instances
+ use_mixed_instances_policy = each.value.use_mixed_instances_policy
+ mixed_instances_policy = each.value.mixed_instances_policy
+
+ tags = local.tags
+}
+
+module "autoscaling_sg" {
+ source = "terraform-aws-modules/security-group/aws"
+ version = "~> 5.0"
+
+ name = local.name
+ description = "Autoscaling group security group"
+ vpc_id = module.vpc.vpc_id
+
+ computed_ingress_with_source_security_group_id = [
+ {
+ rule = "http-80-tcp"
+ source_security_group_id = module.alb.security_group_id
+ }
+ ]
+ number_of_computed_ingress_with_source_security_group_id = 1
+
+ egress_rules = ["all-all"]
+
+ tags = local.tags
+}
+
+module "vpc" {
+ source = "terraform-aws-modules/vpc/aws"
+ version = "~> 5.0"
+
+ name = local.name
+ cidr = local.vpc_cidr
+
+ azs = local.azs
+ private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)]
+ public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)]
+
+ map_public_ip_on_launch = true
+
+ enable_nat_gateway = false
+
+ tags = local.tags
+}
diff --git a/examples/multy-tg/outputs.tf b/examples/multy-tg/outputs.tf
new file mode 100644
index 00000000..2f6f85a7
--- /dev/null
+++ b/examples/multy-tg/outputs.tf
@@ -0,0 +1,132 @@
+################################################################################
+# Cluster
+################################################################################
+
+output "cluster_arn" {
+ description = "ARN that identifies the cluster"
+ value = module.ecs_cluster.arn
+}
+
+output "cluster_id" {
+ description = "ID that identifies the cluster"
+ value = module.ecs_cluster.id
+}
+
+output "cluster_name" {
+ description = "Name that identifies the cluster"
+ value = module.ecs_cluster.name
+}
+
+output "cluster_capacity_providers" {
+ description = "Map of cluster capacity providers attributes"
+ value = module.ecs_cluster.cluster_capacity_providers
+}
+
+output "cluster_autoscaling_capacity_providers" {
+ description = "Map of capacity providers created and their attributes"
+ value = module.ecs_cluster.autoscaling_capacity_providers
+}
+
+################################################################################
+# Service
+################################################################################
+
+output "service_id" {
+ description = "ARN that identifies the service"
+ value = module.ecs_service.id
+}
+
+output "service_name" {
+ description = "Name of the service"
+ value = module.ecs_service.name
+}
+
+output "service_iam_role_name" {
+ description = "Service IAM role name"
+ value = module.ecs_service.iam_role_name
+}
+
+output "service_iam_role_arn" {
+ description = "Service IAM role ARN"
+ value = module.ecs_service.iam_role_arn
+}
+
+output "service_iam_role_unique_id" {
+ description = "Stable and unique string identifying the service IAM role"
+ value = module.ecs_service.iam_role_unique_id
+}
+
+output "service_container_definitions" {
+ description = "Container definitions"
+ value = module.ecs_service.container_definitions
+}
+
+output "service_task_definition_arn" {
+ description = "Full ARN of the Task Definition (including both `family` and `revision`)"
+ value = module.ecs_service.task_definition_arn
+}
+
+output "service_task_definition_revision" {
+ description = "Revision of the task in a particular family"
+ value = module.ecs_service.task_definition_revision
+}
+
+output "service_task_exec_iam_role_name" {
+ description = "Task execution IAM role name"
+ value = module.ecs_service.task_exec_iam_role_name
+}
+
+output "service_task_exec_iam_role_arn" {
+ description = "Task execution IAM role ARN"
+ value = module.ecs_service.task_exec_iam_role_arn
+}
+
+output "service_task_exec_iam_role_unique_id" {
+ description = "Stable and unique string identifying the task execution IAM role"
+ value = module.ecs_service.task_exec_iam_role_unique_id
+}
+
+output "service_tasks_iam_role_name" {
+ description = "Tasks IAM role name"
+ value = module.ecs_service.tasks_iam_role_name
+}
+
+output "service_tasks_iam_role_arn" {
+ description = "Tasks IAM role ARN"
+ value = module.ecs_service.tasks_iam_role_arn
+}
+
+output "service_tasks_iam_role_unique_id" {
+ description = "Stable and unique string identifying the tasks IAM role"
+ value = module.ecs_service.tasks_iam_role_unique_id
+}
+
+output "service_task_set_id" {
+ description = "The ID of the task set"
+ value = module.ecs_service.task_set_id
+}
+
+output "service_task_set_arn" {
+ description = "The Amazon Resource Name (ARN) that identifies the task set"
+ value = module.ecs_service.task_set_arn
+}
+
+output "service_task_set_stability_status" {
+ description = "The stability status. This indicates whether the task set has reached a steady state"
+ value = module.ecs_service.task_set_stability_status
+}
+
+output "service_task_set_status" {
+ description = "The status of the task set"
+ value = module.ecs_service.task_set_status
+}
+
+output "service_autoscaling_policies" {
+ description = "Map of autoscaling policies and their attributes"
+ value = module.ecs_service.autoscaling_policies
+}
+
+output "service_autoscaling_scheduled_actions" {
+ description = "Map of autoscaling scheduled actions and their attributes"
+ value = module.ecs_service.autoscaling_scheduled_actions
+}
diff --git a/examples/multy-tg/variables.tf b/examples/multy-tg/variables.tf
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/multy-tg/versions.tf b/examples/multy-tg/versions.tf
new file mode 100644
index 00000000..682191e7
--- /dev/null
+++ b/examples/multy-tg/versions.tf
@@ -0,0 +1,10 @@
+terraform {
+ required_version = ">= 1.0"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 4.66.1"
+ }
+ }
+}