diff --git a/terraform/ec2-examples/core-infra/README.md b/terraform/ec2-examples/core-infra-ipv6/README.md similarity index 97% rename from terraform/ec2-examples/core-infra/README.md rename to terraform/ec2-examples/core-infra-ipv6/README.md index 0093a75f..eb9a36e6 100644 --- a/terraform/ec2-examples/core-infra/README.md +++ b/terraform/ec2-examples/core-infra-ipv6/README.md @@ -1,7 +1,7 @@ # Core Infrastructure This folder contains the Terraform code to deploy the core infratructure for an ECS EC2 based workload. The AWS resources created by the script are: * Networking - * VPC + * VPC in dual-stack mode: your resources can communicate over IPv4, or IPv6, or both. IPv4 and IPv6 communication are independent of each other. * 3 public subnets, 1 per AZ. If a region has less than 3 AZs it will create same number of public subnets as AZs. * 3 private subnets, 1 per AZ. If a region has less than 3 AZs it will create same number of private subnets as AZs. * 1 NAT Gateway diff --git a/terraform/ec2-examples/core-infra/main.tf b/terraform/ec2-examples/core-infra-ipv6/main.tf similarity index 86% rename from terraform/ec2-examples/core-infra/main.tf rename to terraform/ec2-examples/core-infra-ipv6/main.tf index 2774d2ae..522fbb0e 100644 --- a/terraform/ec2-examples/core-infra/main.tf +++ b/terraform/ec2-examples/core-infra-ipv6/main.tf @@ -77,7 +77,7 @@ resource "aws_service_discovery_private_dns_namespace" "this" { } ################################################################################ -# Supporting Resources +# Supporting Resources - VPC with ipv6-dualstack ################################################################################ module "vpc" { @@ -88,12 +88,19 @@ module "vpc" { 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)] + private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k)] + public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 4)] enable_nat_gateway = true single_nat_gateway = true + enable_ipv6 = true + public_subnet_assign_ipv6_address_on_creation = true + private_subnet_assign_ipv6_address_on_creation = true + + public_subnet_ipv6_prefixes = [0, 1, 2] + private_subnet_ipv6_prefixes = [3, 4, 5] + # Manage so we can name manage_default_network_acl = true default_network_acl_tags = { Name = "${local.name}-default" } @@ -107,7 +114,7 @@ module "vpc" { # 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" + name = "/aws/service/ecs/optimized-ami/amazon-linux-2023/recommended" } module "autoscaling" { @@ -132,9 +139,9 @@ module "autoscaling" { vpc_zone_identifier = module.vpc.private_subnets health_check_type = "EC2" - min_size = 3 - max_size = 5 - desired_capacity = 3 + min_size = 1 + max_size = 1 + desired_capacity = 1 # https://github.com/hashicorp/terraform-provider-aws/issues/12582 autoscaling_group_tags = { @@ -155,8 +162,9 @@ module "autoscaling_sg" { description = "Autoscaling group security group" vpc_id = module.vpc.vpc_id - ingress_cidr_blocks = [module.vpc.vpc_cidr_block] - ingress_rules = ["http-80-tcp"] + ingress_cidr_blocks = [module.vpc.vpc_cidr_block] + ingress_ipv6_cidr_blocks = [module.vpc.vpc_ipv6_cidr_block] + ingress_rules = ["http-80-tcp"] egress_rules = ["all-all"] diff --git a/terraform/ec2-examples/core-infra/outputs.tf b/terraform/ec2-examples/core-infra-ipv6/outputs.tf similarity index 100% rename from terraform/ec2-examples/core-infra/outputs.tf rename to terraform/ec2-examples/core-infra-ipv6/outputs.tf diff --git a/terraform/ec2-examples/core-infra/versions.tf b/terraform/ec2-examples/core-infra-ipv6/versions.tf similarity index 100% rename from terraform/ec2-examples/core-infra/versions.tf rename to terraform/ec2-examples/core-infra-ipv6/versions.tf diff --git a/terraform/ec2-examples/lb-service/README.md b/terraform/ec2-examples/lb-service-ipv6/README.md similarity index 86% rename from terraform/ec2-examples/lb-service/README.md rename to terraform/ec2-examples/lb-service-ipv6/README.md index c5cff412..17fd6182 100644 --- a/terraform/ec2-examples/lb-service/README.md +++ b/terraform/ec2-examples/lb-service-ipv6/README.md @@ -1,6 +1,11 @@ # ECS load-balanced service -This solution blueprint creates a web-facing load balanced ECS service. There are two steps to deploying this service: +This solution blueprint creates a web-facing load balanced ECS service. +The Load Balancer is dualstack mode: Clients can connect to the load balancer using both IPv4 addresses (for example, 192.0.2.1) and IPv6 addresses (for example, 2001:db8:85a3::8a2e:0370:7334). +The ECS Task is registered in two different Target groups(ipv4 and ipv6). +Due current limitations, ECS Tasks are not register in ipv6 target type. + +There are two steps to deploying this service: * Deploy the [core-infra](../core-infra/README.md). Note if you have already deployed the `core-infra` then you can reuse it. * Deploy this blueprint using the below commands diff --git a/terraform/ec2-examples/lb-service/main.tf b/terraform/ec2-examples/lb-service-ipv6/main.tf similarity index 66% rename from terraform/ec2-examples/lb-service/main.tf rename to terraform/ec2-examples/lb-service-ipv6/main.tf index 03694fc0..8bf1d865 100644 --- a/terraform/ec2-examples/lb-service/main.tf +++ b/terraform/ec2-examples/lb-service-ipv6/main.tf @@ -3,7 +3,7 @@ provider "aws" { } locals { - name = "ecsdemo-frontend" + name = "ecsdemo-frontend-ipv6" region = "us-west-2" container_port = 3000 # Container port is specific to this app example @@ -32,7 +32,7 @@ module "ecs_service" { requires_compatibilities = ["EC2"] capacity_provider_strategy = { default = { - capacity_provider = "core-infra" # needs to match name of capacity provider + capacity_provider = "core-infra-ipv6" # needs to match name of capacity provider weight = 1 base = 1 } @@ -81,15 +81,25 @@ module "ecs_service" { description = "Service port" source_security_group_id = module.alb.security_group_id } - egress_all = { + egress_all_ipv4 = { type = "egress" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } + egress_all_ipv6 = { + type = "egress" + from_port = 0 + to_port = 0 + protocol = "-1" + ipv6_cidr_blocks = ["::/0"] + } } + + + tags = local.tags } @@ -121,23 +131,38 @@ module "alb" { # For example only enable_deletion_protection = false - vpc_id = data.aws_vpc.vpc.id - subnets = data.aws_subnets.public.ids + vpc_id = data.aws_vpc.vpc.id + subnets = data.aws_subnets.public.ids + ip_address_type = "dualstack" security_group_ingress_rules = { - all_http = { + all_http_ipv4 = { from_port = 80 to_port = 80 ip_protocol = "tcp" description = "HTTP web traffic" cidr_ipv4 = "0.0.0.0/0" } + all_http_ipv6 = { + from_port = 8080 + to_port = 8080 + ip_protocol = "tcp" + description = "HTTP web traffic" + cidr_ipv6 = "::/0" + } } - security_group_egress_rules = { for subnet in data.aws_subnet.private_cidr : - (subnet.availability_zone) => { + security_group_egress_rules = merge( + { for subnet in data.aws_subnet.private_cidr : "${subnet.availability_zone}" => { ip_protocol = "-1" cidr_ipv4 = subnet.cidr_block + } + }, + { for subnet in data.aws_subnet.private_cidr : "${subnet.availability_zone}-ipv6" => { + ip_protocol = "-1" + cidr_ipv6 = subnet.ipv6_cidr_block + } } - } + ) + listeners = { http = { @@ -148,6 +173,14 @@ module "alb" { target_group_key = "ecs-task" } } + http2 = { + port = "8080" + protocol = "HTTP" + + forward = { + target_group_key = "ecs-task-ipv6" + } + } } target_groups = { @@ -172,6 +205,35 @@ module "alb" { # ECS will attach the IPs of the tasks to this target group create_attachment = false } + + #this is just to show that due current limitations, ECS Targets are not registered on the ipv6 target group. + #This is documented on https://docs.aws.amazon.com/AmazonECS/latest/developerguide/alb.html#alb-considerations + #Consider the following when using Application Load Balancers with Amazon ECS: + #Target group must have the IP address type set to IPv4. + + ecs-task-ipv6 = { + backend_protocol = "HTTP" + backend_port = local.container_port + target_type = "ip" + ip_address_type = "ipv6" + + health_check = { + enabled = true + healthy_threshold = 5 + interval = 30 + matcher = "200-299" + path = "/" + port = "traffic-port" + protocol = "HTTP" + timeout = 5 + unhealthy_threshold = 2 + } + + # There's 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 @@ -184,21 +246,21 @@ module "alb" { data "aws_vpc" "vpc" { filter { name = "tag:Name" - values = ["core-infra"] + values = ["core-infra-ipv6"] } } data "aws_subnets" "public" { filter { name = "tag:Name" - values = ["core-infra-public-*"] + values = ["core-infra-ipv6-public-*"] } } data "aws_subnets" "private" { filter { name = "tag:Name" - values = ["core-infra-private-*"] + values = ["core-infra-ipv6-private-*"] } } @@ -208,7 +270,7 @@ data "aws_subnet" "private_cidr" { } data "aws_ecs_cluster" "core_infra" { - cluster_name = "core-infra" + cluster_name = "core-infra-ipv6" } data "aws_service_discovery_dns_namespace" "this" { diff --git a/terraform/ec2-examples/lb-service/outputs.tf b/terraform/ec2-examples/lb-service-ipv6/outputs.tf similarity index 100% rename from terraform/ec2-examples/lb-service/outputs.tf rename to terraform/ec2-examples/lb-service-ipv6/outputs.tf diff --git a/terraform/ec2-examples/lb-service/versions.tf b/terraform/ec2-examples/lb-service-ipv6/versions.tf similarity index 100% rename from terraform/ec2-examples/lb-service/versions.tf rename to terraform/ec2-examples/lb-service-ipv6/versions.tf