From c25b505f01a51cd255abc2859a7550e4200e9b7f Mon Sep 17 00:00:00 2001 From: Magdalena Herrera Soto Date: Thu, 2 Mar 2023 18:23:26 +0100 Subject: [PATCH 1/6] IPv6 with Amazon generated adressess and IPv6 native for private subnets --- data.tf | 17 ++++- main.tf | 74 +++++++++++++++++---- modules/calculate_subnets/main.tf | 5 +- modules/calculate_subnets/outputs.tf | 5 ++ modules/calculate_subnets_ipv6/main.tf | 25 +++++++ modules/calculate_subnets_ipv6/outputs.tf | 4 ++ modules/calculate_subnets_ipv6/providers.tf | 13 ++++ modules/calculate_subnets_ipv6/variables.tf | 14 ++++ variables.tf | 39 ++++++++++- 9 files changed, 178 insertions(+), 18 deletions(-) create mode 100644 modules/calculate_subnets_ipv6/main.tf create mode 100644 modules/calculate_subnets_ipv6/outputs.tf create mode 100644 modules/calculate_subnets_ipv6/providers.tf create mode 100644 modules/calculate_subnets_ipv6/variables.tf diff --git a/data.tf b/data.tf index e1121ee..82970aa 100644 --- a/data.tf +++ b/data.tf @@ -2,7 +2,11 @@ locals { azs = slice(data.aws_availability_zones.current.names, 0, var.az_count) # references to module.calculate_subnets output - calculated_subnets = module.calculate_subnets.subnets_by_type + calculated_subnets = module.calculate_subnets.subnets_by_type + subnets_with_ipv6_native = module.calculate_subnets.subnets_with_ipv6_native + + # references to module.caluclate_subnets_ipv6 + calculated_subnets_ipv6 = module.calculate_subnets_ipv6.subnets_ipv6 ################################################################## # Subnet names @@ -79,6 +83,17 @@ locals { cidr_block = var.cidr_block == null ? aws_vpc.main[0].cidr_block : var.cidr_block create_flow_logs = (var.vpc_flow_logs == null || var.vpc_flow_logs.log_destination_type == "none") ? false : true + + # IPv6 ############################################################ + # Ipv6 cidr block (To change when multiple Ipv6 CIDR blocks) + vpc_ipv6_cidr_block = var.vpc_ipv6_cidr_block == null ? aws_vpc.main[0].ipv6_cidr_block : var.vpc_ipv6_cidr_block + + # Egress Only Internet Gateway for IPv6 + # list of private subnet keys with connect_to_public_eigw = true + private_subnets_egress_routed = [for type in local.private_subnet_names : type if try(var.subnets[type].connect_to_eigw == true, false)] + # private subnets with cidrs per az if connect_to_public_eoigw = true ... "privatetwo/us-east-1a" + private_subnet_names_egress_routed = [for subnet in local.private_per_az : subnet if contains(local.private_subnets_egress_routed, split("/", subnet)[0])] + public_with_eigw = can(var.subnets["public"].connect_to_eigw) ? true : false } data "aws_availability_zones" "current" {} diff --git a/main.tf b/main.tf index 1dc6812..eddf09d 100644 --- a/main.tf +++ b/main.tf @@ -7,16 +7,29 @@ module "calculate_subnets" { subnets = var.subnets } +module "calculate_subnets_ipv6" { + source = "./modules/calculate_subnets_ipv6" + + cidr_ipv6 = local.vpc_ipv6_cidr_block + azs = local.azs + + subnets = var.subnets +} + # VPC RESOURCE (and secondary CIDR blocks - if configured) resource "aws_vpc" "main" { count = local.create_vpc ? 1 : 0 - cidr_block = var.cidr_block - enable_dns_hostnames = var.vpc_enable_dns_hostnames - enable_dns_support = var.vpc_enable_dns_support - instance_tenancy = var.vpc_instance_tenancy - ipv4_ipam_pool_id = var.vpc_ipv4_ipam_pool_id - ipv4_netmask_length = var.vpc_ipv4_netmask_length + cidr_block = var.cidr_block + enable_dns_hostnames = var.vpc_enable_dns_hostnames + enable_dns_support = var.vpc_enable_dns_support + instance_tenancy = var.vpc_instance_tenancy + ipv4_ipam_pool_id = var.vpc_ipv4_ipam_pool_id + ipv4_netmask_length = var.vpc_ipv4_netmask_length + assign_generated_ipv6_cidr_block = var.vpc_assign_generated_ipv6_cidr_block + ipv6_cidr_block = var.vpc_ipv6_cidr_block + ipv6_ipam_pool_id = var.vpc_ipv6_ipam_pool_id + ipv6_netmask_length = var.vpc_ipv6_netmask_length tags = merge( { "Name" = var.name }, @@ -39,9 +52,11 @@ resource "aws_vpc_ipv4_cidr_block_association" "secondary" { resource "aws_subnet" "public" { for_each = contains(local.subnet_keys, "public") ? toset(local.azs) : toset([]) - availability_zone = each.key - vpc_id = local.vpc.id - cidr_block = local.calculated_subnets["public"][each.key] + availability_zone = each.key + vpc_id = local.vpc.id + cidr_block = local.calculated_subnets["public"][each.key] + assign_ipv6_address_on_creation = can(local.calculated_subnets_ipv6["public"][each.key]) + ipv6_cidr_block = can(local.calculated_subnets_ipv6["public"][each.key]) ? local.calculated_subnets_ipv6["public"][each.key] : null tags = merge( { Name = "${local.subnet_names["public"]}-${each.key}" }, @@ -154,10 +169,14 @@ resource "aws_route" "public_to_cwan" { resource "aws_subnet" "private" { for_each = toset(try(local.private_per_az, [])) - availability_zone = split("/", each.key)[1] - vpc_id = local.vpc.id - cidr_block = local.calculated_subnets[split("/", each.key)[0]][split("/", each.key)[1]] - map_public_ip_on_launch = false + availability_zone = split("/", each.key)[1] + vpc_id = local.vpc.id + cidr_block = can(local.calculated_subnets[split("/", each.key)[0]][split("/", each.key)[1]]) ? local.calculated_subnets[split("/", each.key)[0]][split("/", each.key)[1]] : null + map_public_ip_on_launch = false + assign_ipv6_address_on_creation = can(local.calculated_subnets_ipv6[split("/", each.key)[0]][split("/", each.key)[1]]) + ipv6_cidr_block = can(local.calculated_subnets_ipv6[split("/", each.key)[0]][split("/", each.key)[1]]) ? local.calculated_subnets_ipv6[split("/", each.key)[0]][split("/", each.key)[1]] : null + ipv6_native = contains(local.subnets_with_ipv6_native, split("/", each.key)[0]) ? true : false + enable_resource_name_dns_aaaa_record_on_launch = contains(local.subnets_with_ipv6_native, split("/", each.key)[0]) ? true : false tags = merge( { Name = "${local.subnet_names[split("/", each.key)[0]]}-${split("/", each.key)[1]}" }, @@ -390,3 +409,32 @@ module "flow_logs" { tags = module.tags.tags_aws } + +# IPv6 Routing +resource "aws_egress_only_internet_gateway" "eigw" { + count = var.vpc_egress_only_internet_gateway ? 1 : 0 + vpc_id = local.vpc.id + + tags = merge( + { "Name" = var.name }, + module.tags.tags_aws + ) +} + +resource "aws_route" "private_to_egress_only" { + for_each = toset(try(local.private_subnet_names_egress_routed, [])) + + route_table_id = aws_route_table.private[each.key].id + destination_ipv6_cidr_block = "0::/0" + # try to get nat for AZ, else use singular nat + egress_only_gateway_id = aws_egress_only_internet_gateway.eigw[0].id +} + +resource "aws_route" "public_to_egress_only" { + for_each = local.public_with_eigw ? toset(local.azs) : toset([]) + + route_table_id = aws_route_table.public[each.key].id + destination_ipv6_cidr_block = "0::/0" + # try to get nat for AZ, else use singular nat + egress_only_gateway_id = aws_egress_only_internet_gateway.eigw[0].id +} \ No newline at end of file diff --git a/modules/calculate_subnets/main.tf b/modules/calculate_subnets/main.tf index 87c7d57..f8119ea 100644 --- a/modules/calculate_subnets/main.tf +++ b/modules/calculate_subnets/main.tf @@ -3,8 +3,11 @@ locals { type_grouped_named_subnets_to_build = { for name, subnet_definition in var.subnets : name => [for _, az in var.azs : "${name}/${az}"] } # which network groups require calculating subnet types_to_calculate = [for type, subnet_definition in var.subnets : type if can(subnet_definition.netmask)] + types_ipv6_native = [for type, subnet_definition in var.subnets : type if can(subnet_definition.ipv6_native)] # network groups that are set explicitly - types_with_explicit = setsubtract(keys(var.subnets), local.types_to_calculate) + types_with_explicit_and_ipv6 = setsubtract(keys(var.subnets), local.types_to_calculate) + types_with_explicit = setsubtract(local.types_with_explicit_and_ipv6, local.types_ipv6_native) + # network object to pass to calculating module calculated_subnet_objects = flatten([for _, type in local.types_to_calculate : [for _, v in local.type_grouped_named_subnets_to_build[type] : { diff --git a/modules/calculate_subnets/outputs.tf b/modules/calculate_subnets/outputs.tf index aec4332..09397eb 100644 --- a/modules/calculate_subnets/outputs.tf +++ b/modules/calculate_subnets/outputs.tf @@ -2,3 +2,8 @@ output "subnets_by_type" { description = "Outputs subnets prefixes by type (private, public). Derived from split(var.separator, )." value = merge(try(local.explict_cidrs_grouped, {}), try(module.subnet_calculator[0].grouped_by_separator, {})) } + +output "subnets_with_ipv6_native" { + description = "Outputs types of subnets that are ipv6_native." + value = local.types_ipv6_native +} \ No newline at end of file diff --git a/modules/calculate_subnets_ipv6/main.tf b/modules/calculate_subnets_ipv6/main.tf new file mode 100644 index 0000000..b6ef7b9 --- /dev/null +++ b/modules/calculate_subnets_ipv6/main.tf @@ -0,0 +1,25 @@ +locals { + # group subnets by type and create names for each type + type_grouped_named_subnets_to_build = { for name, subnet_definition in var.subnets : name => [for _, az in var.azs : "${name}/${az}"] } + # which network groups require calculating subnet + subnets_to_calculate = [for type, subnet_definition in var.subnets : type if can(subnet_definition.assign_ipv6_address_on_creation)] + # network groups that are set explicitly + types_with_no_ipv6 = setsubtract(keys(var.subnets), local.subnets_to_calculate) + + # network object to pass to calculating module + calculated_subnet_objects = flatten([for _, type in local.subnets_to_calculate : [for _, v in local.type_grouped_named_subnets_to_build[type] : { + "name" = v + "netmask" = 64 + } + ]]) +} + +module "subnet_calculator" { + count = local.subnets_to_calculate == [] ? 0 : 1 + + source = "drewmullen/subnets/cidr" + version = "1.0.2" + + base_cidr_block = var.cidr_ipv6 + networks = local.calculated_subnet_objects +} diff --git a/modules/calculate_subnets_ipv6/outputs.tf b/modules/calculate_subnets_ipv6/outputs.tf new file mode 100644 index 0000000..47174a3 --- /dev/null +++ b/modules/calculate_subnets_ipv6/outputs.tf @@ -0,0 +1,4 @@ +output "subnets_ipv6" { + description = "Outputs subnets prefixes by type (private, public). Derived from split(var.separator, )." + value = try(module.subnet_calculator[0].grouped_by_separator, {}) +} \ No newline at end of file diff --git a/modules/calculate_subnets_ipv6/providers.tf b/modules/calculate_subnets_ipv6/providers.tf new file mode 100644 index 0000000..db5ea45 --- /dev/null +++ b/modules/calculate_subnets_ipv6/providers.tf @@ -0,0 +1,13 @@ +terraform { + required_version = ">= 1.3.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.72.0" + } + awscc = { + source = "hashicorp/awscc" + version = ">= 0.15.0" + } + } +} diff --git a/modules/calculate_subnets_ipv6/variables.tf b/modules/calculate_subnets_ipv6/variables.tf new file mode 100644 index 0000000..9712922 --- /dev/null +++ b/modules/calculate_subnets_ipv6/variables.tf @@ -0,0 +1,14 @@ +variable "subnets" { + description = "Definition of subnets to be built. If `netmask` is passed will calculate CIDR. Else `cidrs` list is ziped to var.azs and merged into final output to be built into aws_subnet(s)." + type = any + # validation happening on root module +} +variable "azs" { + description = "List of AZs to build. AZ is appened to each IP address prefix name." + type = list(string) +} + +variable "cidr_ipv6" { + description = "CIDR value to use as base for calculating IP address prefixes." + type = string +} \ No newline at end of file diff --git a/variables.tf b/variables.tf index c8110f4..9f428c4 100644 --- a/variables.tf +++ b/variables.tf @@ -147,7 +147,9 @@ EOF "netmask", "name_prefix", "nat_gateway_configuration", - "tags" + "tags", + "assign_ipv6_address_on_creation", + "connect_to_eigw" ])) == 0 } @@ -184,8 +186,8 @@ EOF } validation { - error_message = "Each subnet type must contain only 1 key: `cidrs` or `netmask`." - condition = alltrue([for subnet_type, v in var.subnets : length(setintersection(keys(v), ["cidrs", "netmask"])) == 1]) + error_message = "Each subnet type must contain only 1 key: `cidrs` or `netmask` or `ipv_native`." + condition = alltrue([for subnet_type, v in var.subnets : length(setintersection(keys(v), ["cidrs", "netmask", "ipv6_native"])) == 1]) } validation { @@ -286,3 +288,34 @@ EOF type = any default = {} } + +# Variables used for IPv6 +variable "vpc_ipv6_cidr_block" { + description = "CIDR range to assign to VPC if creating VPC or to associte as a secondary CIDR. Overridden by var.vpc_id output from data.aws_vpc." + default = null + type = string +} + +variable "vpc_assign_generated_ipv6_cidr_block" { + description = "Whether the vpc has ipv6 generated cider block" + type = bool + default = false +} + +variable "vpc_ipv6_ipam_pool_id" { + description = "Set to use IPAM to get CIDR IPV6 block." + type = string + default = null +} + +variable "vpc_ipv6_netmask_length" { + description = "Set to use IPAM to get CIDR block using a specified netmask. Must be set with var.vpc_ipv6_ipam_pool_id." + type = string + default = null +} + +variable "vpc_egress_only_internet_gateway" { + description = "Set to use the egress only gateway for all traffic Ipv6 going to the Internet." + type = bool + default = false +} From 42d7b4f7720741ffc50243ec485e643727fc1f4d Mon Sep 17 00:00:00 2001 From: Pablo Sanchez Carmona Date: Fri, 7 Apr 2023 22:54:32 +0200 Subject: [PATCH 2/6] ipv6 support + update examples --- examples/cloud_wan/.header.md | 2 +- examples/cloud_wan/README.md | 7 ++++--- examples/cloud_wan/main.tf | 35 ++++++++++++++++++++++++++++------- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/examples/cloud_wan/.header.md b/examples/cloud_wan/.header.md index 043fad8..f5d7408 100644 --- a/examples/cloud_wan/.header.md +++ b/examples/cloud_wan/.header.md @@ -7,4 +7,4 @@ This example shows how you can use this module with `core_network` subnets, and * The VPC module creates the following (in two AWS Regions): * Two sets of subnets (workloads and core_network) * Cloud WAN's VPC attachment - with attachment acceptance for the VPC to associate to the *prod* segment. - * Routing to Core Network (0.0.0.0/0) in workload subnets. \ No newline at end of file + * Routing to Core Network (0.0.0.0/0 & ::/0) in workload subnets. \ No newline at end of file diff --git a/examples/cloud_wan/README.md b/examples/cloud_wan/README.md index 8f1edde..9848bff 100644 --- a/examples/cloud_wan/README.md +++ b/examples/cloud_wan/README.md @@ -8,7 +8,7 @@ This example shows how you can use this module with `core_network` subnets, and * The VPC module creates the following (in two AWS Regions): * Two sets of subnets (workloads and core\_network) * Cloud WAN's VPC attachment - with attachment acceptance for the VPC to associate to the *prod* segment. - * Routing to Core Network (0.0.0.0/0) in workload subnets. + * Routing to Core Network (0.0.0.0/0 & ::/0) in workload subnets. ## Requirements @@ -28,14 +28,15 @@ This example shows how you can use this module with `core_network` subnets, and | Name | Source | Version | |------|--------|---------| -| [ireland\_vpc](#module\_ireland\_vpc) | aws-ia/vpc/aws | >= 4.0.0 | -| [nvirginia\_vpc](#module\_nvirginia\_vpc) | aws-ia/vpc/aws | >= 4.0.0 | +| [ireland\_vpc](#module\_ireland\_vpc) | aws-ia/vpc/aws | >= 4.2.0 | +| [nvirginia\_vpc](#module\_nvirginia\_vpc) | aws-ia/vpc/aws | >= 4.2.0 | ## Resources | Name | Type | |------|------| | [aws_networkmanager_core_network.core_network](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkmanager_core_network) | resource | +| [aws_networkmanager_core_network_policy_attachment.core_network_policy_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkmanager_core_network_policy_attachment) | resource | | [aws_networkmanager_global_network.global_network](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkmanager_global_network) | resource | | [aws_networkmanager_core_network_policy_document.policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/networkmanager_core_network_policy_document) | data source | diff --git a/examples/cloud_wan/main.tf b/examples/cloud_wan/main.tf index bb89aa5..5a436a0 100644 --- a/examples/cloud_wan/main.tf +++ b/examples/cloud_wan/main.tf @@ -2,11 +2,12 @@ # VPC module (North Virginia) module "nvirginia_vpc" { source = "aws-ia/vpc/aws" - version = ">= 4.0.0" + version = ">= 4.2.0" providers = { aws = aws.awsnvirginia } name = "nvirginia-vpc" cidr_block = "10.0.0.0/24" + vpc_assign_generated_ipv6_cidr_block = true az_count = 2 core_network = { @@ -16,12 +17,18 @@ module "nvirginia_vpc" { core_network_routes = { workload = "0.0.0.0/0" } + core_network_ipv6_routes = { + workload = "::/0" + } subnets = { - workload = { netmask = 28 } + workload = { + netmask = 28 + assign_ipv6_cidr = true + } core_network = { netmask = 28 - ipv6_support = false + assign_ipv6_cidr = true appliance_mode_support = true require_acceptance = true accept_attachment = true @@ -36,11 +43,12 @@ module "nvirginia_vpc" { # VPC module (Ireland) module "ireland_vpc" { source = "aws-ia/vpc/aws" - version = ">= 4.0.0" + version = ">= 4.2.0" providers = { aws = aws.awsireland } name = "ireland-vpc" cidr_block = "10.0.1.0/24" + vpc_assign_generated_ipv6_cidr_block = true az_count = 2 core_network = { @@ -50,12 +58,18 @@ module "ireland_vpc" { core_network_routes = { workload = "0.0.0.0/0" } + core_network_ipv6_routes = { + workload = "::/0" + } subnets = { - workload = { netmask = 28 } + workload = { + netmask = 28 + assign_ipv6_cidr = true + } core_network = { netmask = 28 - ipv6_support = false + assign_ipv6_cidr = true require_acceptance = false tags = { @@ -78,9 +92,16 @@ resource "aws_networkmanager_core_network" "core_network" { description = "Core Network - VPC module" global_network_id = aws_networkmanager_global_network.global_network.id - policy_document = jsonencode(jsondecode(data.aws_networkmanager_core_network_policy_document.policy.json)) tags = { Name = "Core Network - VPC module" } } + +# Core Network policy attachment +resource "aws_networkmanager_core_network_policy_attachment" "core_network_policy_attachment" { + provider = aws.awsnvirginia + + core_network_id = aws_networkmanager_core_network.core_network.id + policy_document = data.aws_networkmanager_core_network_policy_document.policy.json +} From 3fa402625ad8cebd9a2ab089deb6e966d425c354 Mon Sep 17 00:00:00 2001 From: Pablo Sanchez Carmona Date: Fri, 7 Apr 2023 22:55:04 +0200 Subject: [PATCH 3/6] ipv6 support + update examples --- data.tf | 33 ++- examples/cloud_wan/main.tf | 14 +- examples/ipv6/.header.md | 11 + examples/ipv6/.terraform-docs.yaml | 21 ++ examples/ipv6/README.md | 44 ++++ examples/ipv6/main.tf | 35 ++++ examples/ipv6/outputs.tf | 0 examples/ipv6/providers.tf | 16 ++ examples/ipv6/variables.tf | 7 + examples/transit_gateway/.header.md | 7 +- examples/transit_gateway/README.md | 24 ++- examples/transit_gateway/main.tf | 51 +++-- examples/transit_gateway/outputs.tf | 2 +- .../transit_gateway}/providers.tf | 5 + examples/transit_gateway/variables.tf | 17 ++ main.tf | 193 ++++++++++++------ modules/calculate_subnets_ipv6/main.tf | 11 +- modules/calculate_subnets_ipv6/outputs.tf | 2 +- test/examples_ipv6_test.go | 17 ++ test/examples_transit_gateway_test.go | 1 - .../hcl_fixtures/transit_gateway_base/main.tf | 19 -- .../transit_gateway_base/outputs.tf | 9 - .../transit_gateway_base/variables.tf | 8 - variables.tf | 114 +++++++---- 24 files changed, 474 insertions(+), 187 deletions(-) create mode 100644 examples/ipv6/.header.md create mode 100644 examples/ipv6/.terraform-docs.yaml create mode 100644 examples/ipv6/README.md create mode 100644 examples/ipv6/main.tf create mode 100644 examples/ipv6/outputs.tf create mode 100644 examples/ipv6/providers.tf create mode 100644 examples/ipv6/variables.tf rename {test/hcl_fixtures/transit_gateway_base => examples/transit_gateway}/providers.tf (69%) create mode 100644 test/examples_ipv6_test.go delete mode 100644 test/hcl_fixtures/transit_gateway_base/main.tf delete mode 100644 test/hcl_fixtures/transit_gateway_base/outputs.tf delete mode 100644 test/hcl_fixtures/transit_gateway_base/variables.tf diff --git a/data.tf b/data.tf index 82970aa..8266403 100644 --- a/data.tf +++ b/data.tf @@ -5,7 +5,7 @@ locals { calculated_subnets = module.calculate_subnets.subnets_by_type subnets_with_ipv6_native = module.calculate_subnets.subnets_with_ipv6_native - # references to module.caluclate_subnets_ipv6 + # references to module.calculate_subnets_ipv6 calculated_subnets_ipv6 = module.calculate_subnets_ipv6.subnets_ipv6 ################################################################## @@ -22,7 +22,6 @@ locals { # Notes: # - subnets map contains arbitrary amount of subnet "keys" which are both defined as the subnets type and default name (unless name_prefix is provided). # - resource name labels for subnet use the key as private subnet keys are constructed - singleton_subnet_types = ["public", "transit_gateway", "core_network"] private_subnet_names = setsubtract(local.subnet_keys, local.singleton_subnet_types) @@ -33,17 +32,27 @@ locals { # private subnets with cidrs per az if connect_to_public_natgw = true ... "privatetwo/us-east-1a" private_subnet_names_nat_routed = [for subnet in local.private_per_az : subnet if contains(local.private_subnets_nat_routed, split("/", subnet)[0])] - # removed to support transit_gateway_routes + # support variables for transit_gateway_routes subnets_tgw_routed = keys(var.transit_gateway_routes) private_subnet_key_names_tgw_routed = [for subnet in local.private_per_az : subnet if contains(local.subnets_tgw_routed, split("/", subnet)[0])] + # support variables for transit_gateway_ipv6_routes + ipv6_subnets_tgw_routed = keys(var.transit_gateway_ipv6_routes) + ipv6_private_subnet_key_names_tgw_routed = [for subnet in local.private_per_az : subnet if contains(local.ipv6_subnets_tgw_routed, split("/", subnet)[0])] + # support variables for core_network_routes subnets_cwan_routed = keys(var.core_network_routes) private_subnet_key_names_cwan_routes = [for subnet in local.private_per_az : subnet if contains(local.subnets_cwan_routed, split("/", subnet)[0])] - require_acceptance = try(var.subnets.core_network.require_acceptance, false) # value to default - accept_attachment = try(var.subnets.core_network.accept_attachment, true) # value to default - create_acceptance = (local.require_acceptance == true && local.accept_attachment == true) - create_cwan_routes = (local.require_acceptance == false) || local.create_acceptance + + # support variables for core_network_ipv6_routes + ipv6_subnets_cwan_routed = keys(var.core_network_routes) + ipv6_private_subnet_keys_names_cwan_routes = [for subnet in local.private_per_az : subnet if contains(local.ipv6_subnets_cwan_routed, split("/", subnet)[0])] + + # support variables for core_network subnets + require_acceptance = try(var.subnets.core_network.require_acceptance, false) # value to default + accept_attachment = try(var.subnets.core_network.accept_attachment, true) # value to default + create_acceptance = (local.require_acceptance == true && local.accept_attachment == true) + create_cwan_routes = (local.require_acceptance == false) || local.create_acceptance ################################################################## # NAT configurations options, maps user string input to HCL usable values. selected based on nat_gateway_configuration @@ -87,13 +96,19 @@ locals { # IPv6 ############################################################ # Ipv6 cidr block (To change when multiple Ipv6 CIDR blocks) vpc_ipv6_cidr_block = var.vpc_ipv6_cidr_block == null ? aws_vpc.main[0].ipv6_cidr_block : var.vpc_ipv6_cidr_block + # Checking if public subnets are dual-stack or IPv6-only + public_ipv6only = can(var.subnets.public.ipv6_native) + public_dualstack = !local.public_ipv6only && (can(var.subnets.public.assign_ipv6_cidr) || can(var.subnets.public.ipv6_cidrs)) + # Checking if transit_gateway subnets are dual-stack + tgw_dualstack = (can(var.subnets.transit_gateway.assign_ipv6_cidr) || can(var.subnets.transit_gateway.ipv6_cidrs)) + # Checking if core_network subnets are dual-stack + cwan_dualstack = (can(var.subnets.core_network.assign_ipv6_cidr) || can(var.subnets.core_network.ipv6_cidrs)) # Egress Only Internet Gateway for IPv6 # list of private subnet keys with connect_to_public_eigw = true private_subnets_egress_routed = [for type in local.private_subnet_names : type if try(var.subnets[type].connect_to_eigw == true, false)] - # private subnets with cidrs per az if connect_to_public_eoigw = true ... "privatetwo/us-east-1a" + # private subnets with cidrs per az if connect_to_public_eigw = true ... "privatetwo/us-east-1a" private_subnet_names_egress_routed = [for subnet in local.private_per_az : subnet if contains(local.private_subnets_egress_routed, split("/", subnet)[0])] - public_with_eigw = can(var.subnets["public"].connect_to_eigw) ? true : false } data "aws_availability_zones" "current" {} diff --git a/examples/cloud_wan/main.tf b/examples/cloud_wan/main.tf index 5a436a0..4061d2e 100644 --- a/examples/cloud_wan/main.tf +++ b/examples/cloud_wan/main.tf @@ -5,10 +5,10 @@ module "nvirginia_vpc" { version = ">= 4.2.0" providers = { aws = aws.awsnvirginia } - name = "nvirginia-vpc" - cidr_block = "10.0.0.0/24" + name = "nvirginia-vpc" + cidr_block = "10.0.0.0/24" vpc_assign_generated_ipv6_cidr_block = true - az_count = 2 + az_count = 2 core_network = { id = aws_networkmanager_core_network.core_network.id @@ -46,10 +46,10 @@ module "ireland_vpc" { version = ">= 4.2.0" providers = { aws = aws.awsireland } - name = "ireland-vpc" - cidr_block = "10.0.1.0/24" + name = "ireland-vpc" + cidr_block = "10.0.1.0/24" vpc_assign_generated_ipv6_cidr_block = true - az_count = 2 + az_count = 2 core_network = { id = aws_networkmanager_core_network.core_network.id @@ -103,5 +103,5 @@ resource "aws_networkmanager_core_network_policy_attachment" "core_network_polic provider = aws.awsnvirginia core_network_id = aws_networkmanager_core_network.core_network.id - policy_document = data.aws_networkmanager_core_network_policy_document.policy.json + policy_document = data.aws_networkmanager_core_network_policy_document.policy.json } diff --git a/examples/ipv6/.header.md b/examples/ipv6/.header.md new file mode 100644 index 0000000..dda3210 --- /dev/null +++ b/examples/ipv6/.header.md @@ -0,0 +1,11 @@ +# Creating Dual-Stack and IPv6-only subnets + +This example shows how you can create dual-stack and IPv6-only subnets in your Amazon VPC. This example creates the following: + +* VPC with IPv4 CIDR block and Amazon-generated IPv6 CIDR block. +* Internet gateway and Egress-only Internet gateway. +* 4 subnet types: + * Public subnet (dual-stack) with NAT gateways in all the Availability Zones used. + * IPv4-only private subnet, with default routes (0.0.0.0/0) via the NAT gateways. + * Dual-stack private subnet, with IPv4 default route (0.0.0.0/0) via the NAT gateways and IPv6 default route (::/0) via the Egress-only Internet gateway. + * IPv6-only private subnet, with IPv6 default route (::/0) via the Egress-only Internet gateway. \ No newline at end of file diff --git a/examples/ipv6/.terraform-docs.yaml b/examples/ipv6/.terraform-docs.yaml new file mode 100644 index 0000000..6dc99de --- /dev/null +++ b/examples/ipv6/.terraform-docs.yaml @@ -0,0 +1,21 @@ +formatter: markdown +header-from: .header.md +settings: + anchor: true + color: true + default: true + escape: true + html: true + indent: 2 + required: true + sensitive: true + type: true + lockfile: false + +sort: + enabled: true + by: required + +output: + file: README.md + mode: replace diff --git a/examples/ipv6/README.md b/examples/ipv6/README.md new file mode 100644 index 0000000..5d6f0fe --- /dev/null +++ b/examples/ipv6/README.md @@ -0,0 +1,44 @@ + +# Creating Dual-Stack and IPv6-only subnets + +This example shows how you can create dual-stack and IPv6-only subnets in your Amazon VPC. This example creates the following: + +* VPC with IPv4 CIDR block and Amazon-generated IPv6 CIDR block. +* Internet gateway and Egress-only Internet gateway. +* 4 subnet types: + * Public subnet (dual-stack) with NAT gateways in all the Availability Zones used. + * IPv4-only private subnet, with default routes (0.0.0.0/0) via the NAT gateways. + * Dual-stack private subnet, with IPv4 default route (0.0.0.0/0) via the NAT gateways and IPv6 default route (::/0) via the Egress-only Internet gateway. + * IPv6-only private subnet, with IPv6 default route (::/0) via the Egress-only Internet gateway. + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3.0 | +| [aws](#requirement\_aws) | >= 4.27.0 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [vpc](#module\_vpc) | aws-ia/vpc/aws | >= 4.2.0 | + +## Resources + +No resources. + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [aws\_region](#input\_aws\_region) | AWS Regions to create in Cloud WAN's core network. | `string` | `"eu-west-2"` | no | + +## Outputs + +No outputs. + \ No newline at end of file diff --git a/examples/ipv6/main.tf b/examples/ipv6/main.tf new file mode 100644 index 0000000..5a7af43 --- /dev/null +++ b/examples/ipv6/main.tf @@ -0,0 +1,35 @@ + +# VPC module +module "vpc" { + source = "aws-ia/vpc/aws" + version = ">= 4.2.0" + + name = "vpc-ipv6-generated" + cidr_block = "10.0.0.0/16" + vpc_assign_generated_ipv6_cidr_block = true + vpc_egress_only_internet_gateway = true + az_count = 2 + + subnets = { + public = { + netmask = 24 + nat_gateway_configuration = "all_azs" + assign_ipv6_cidr = true + } + ipv4 = { + netmask = 24 + connect_to_public_natgw = true + } + dualstack = { + netmask = 24 + connect_to_public_natgw = true + assign_ipv6_cidr = true + connect_to_eigw = true + } + ipv6 = { + ipv6_native = true + assign_ipv6_cidr = true + connect_to_eigw = true + } + } +} \ No newline at end of file diff --git a/examples/ipv6/outputs.tf b/examples/ipv6/outputs.tf new file mode 100644 index 0000000..e69de29 diff --git a/examples/ipv6/providers.tf b/examples/ipv6/providers.tf new file mode 100644 index 0000000..7974f62 --- /dev/null +++ b/examples/ipv6/providers.tf @@ -0,0 +1,16 @@ + +terraform { + required_version = ">= 1.3.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.27.0" + } + } +} + +# Provider definition +provider "aws" { + region = var.aws_region +} + diff --git a/examples/ipv6/variables.tf b/examples/ipv6/variables.tf new file mode 100644 index 0000000..0f9e5e5 --- /dev/null +++ b/examples/ipv6/variables.tf @@ -0,0 +1,7 @@ + +variable "aws_region" { + description = "AWS Regions to create in Cloud WAN's core network." + type = string + + default = "eu-west-2" +} \ No newline at end of file diff --git a/examples/transit_gateway/.header.md b/examples/transit_gateway/.header.md index 14a9c0b..683a23d 100644 --- a/examples/transit_gateway/.header.md +++ b/examples/transit_gateway/.header.md @@ -3,7 +3,10 @@ This example shows how you can use this module with `transit_gateway` subnets, and AWS Transit Gateway VPC attachment. This examples creates the following: * AWS Transit Gateway. +* IPv4 managed prefix list with two entries. * The VPC module creates the following: - * Four sets of subnets (*public*, *private_with_egress*, *truly_private*, and *transit_gateway*) + * Four sets of subnets (*public*, *private_with_egress*, *private_dualstack*, and *transit_gateway*) * Transit Gateway VPC attachment. - * Routing to Transit Gateway attachment from *public* and *private_with_egress* subnets. \ No newline at end of file + * Routing to Transit Gateway attachment: + * IPv4 routes from *public*, *private_with_egress*, and *private_dualstack*. + * IPv6 routes from *private_dualstack*. \ No newline at end of file diff --git a/examples/transit_gateway/README.md b/examples/transit_gateway/README.md index dcb706a..df4f526 100644 --- a/examples/transit_gateway/README.md +++ b/examples/transit_gateway/README.md @@ -4,37 +4,47 @@ This example shows how you can use this module with `transit_gateway` subnets, and AWS Transit Gateway VPC attachment. This examples creates the following: * AWS Transit Gateway. +* IPv4 managed prefix list with two entries. * The VPC module creates the following: - * Four sets of subnets (*public*, *private\_with\_egress*, *truly\_private*, and *transit\_gateway*) + * Four sets of subnets (*public*, *private\_with\_egress*, *private\_dualstack*, and *transit\_gateway*) * Transit Gateway VPC attachment. - * Routing to Transit Gateway attachment from *public* and *private\_with\_egress* subnets. + * Routing to Transit Gateway attachment: + * IPv4 routes from *public*, *private\_with\_egress*, and *private\_dualstack*. + * IPv6 routes from *private\_dualstack*. ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3.0 | +| [aws](#requirement\_aws) | >= 3.72.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | n/a | +| [aws](#provider\_aws) | >= 3.72.0 | ## Modules | Name | Source | Version | |------|--------|---------| -| [tgw\_base\_for\_example\_only](#module\_tgw\_base\_for\_example\_only) | ../../test/hcl_fixtures/transit_gateway_base | n/a | -| [vpc](#module\_vpc) | aws-ia/vpc/aws | >= 4.0.0 | +| [vpc](#module\_vpc) | aws-ia/vpc/aws | >= 4.2.0 | ## Resources | Name | Type | |------|------| +| [aws_ec2_managed_prefix_list.example](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_managed_prefix_list) | resource | +| [aws_ec2_transit_gateway.example](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway) | resource | | [aws_availability_zones.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | ## Inputs -No inputs. +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [aws\_region](#input\_aws\_region) | AWS Regions to create in Cloud WAN's core network. | `string` | `"eu-west-2"` | no | +| [prefixes](#input\_prefixes) | (optional) describe your variable | `map(string)` |
{
"internal": "192.168.0.0/16",
"primary": "10.0.0.0/8"
}
| no | ## Outputs diff --git a/examples/transit_gateway/main.tf b/examples/transit_gateway/main.tf index e2fed68..9084ec2 100644 --- a/examples/transit_gateway/main.tf +++ b/examples/transit_gateway/main.tf @@ -1,17 +1,41 @@ data "aws_availability_zones" "current" {} +resource "aws_ec2_transit_gateway" "example" { + description = "example" +} + +resource "aws_ec2_managed_prefix_list" "example" { + name = "All VPC CIDR-s" + address_family = "IPv4" + max_entries = 5 + + dynamic "entry" { + for_each = var.prefixes + + content { + cidr = entry.value + description = entry.key + } + } +} + module "vpc" { source = "aws-ia/vpc/aws" - version = ">= 4.0.0" + version = ">= 4.2.0" - name = "tgw" - cidr_block = "10.0.0.0/16" - az_count = 2 - transit_gateway_id = module.tgw_base_for_example_only.tgw_id + name = "tgw" + cidr_block = "10.0.0.0/16" + vpc_assign_generated_ipv6_cidr_block = true + az_count = 2 + transit_gateway_id = aws_ec2_transit_gateway.example.id transit_gateway_routes = { public = "10.0.0.0/8" private_with_egress = "192.168.0.0/16" + private_dualstack = aws_ec2_managed_prefix_list.example.id + } + transit_gateway_ipv6_routes = { + private_dualstack = "::/0" } subnets = { @@ -19,18 +43,17 @@ module "vpc" { netmask = 24 nat_gateway_configuration = "single_az" } - private_with_egress = { netmask = 24 connect_to_public_natgw = true } - - truly_private = { - netmask = 24 + private_dualstack = { + netmask = 24 + assign_ipv6_cidr = true } - transit_gateway = { netmask = 28 + assign_ipv6_cidr = true connect_to_public_natgw = true transit_gateway_default_route_table_association = true transit_gateway_default_route_table_propagation = true @@ -43,11 +66,3 @@ module "vpc" { } } } - -##################################### -# Example of a tgw deployment -##################################### - -module "tgw_base_for_example_only" { - source = "../../test/hcl_fixtures/transit_gateway_base" -} diff --git a/examples/transit_gateway/outputs.tf b/examples/transit_gateway/outputs.tf index d158bf4..27ee1bd 100644 --- a/examples/transit_gateway/outputs.tf +++ b/examples/transit_gateway/outputs.tf @@ -7,5 +7,5 @@ output "tgw_subnets_tags_length" { output "private_subnets_tags_length" { description = "Count of private subnet tags for a single az." - value = try(length(module.vpc.private_subnet_attributes_by_az["truly_private/${data.aws_availability_zones.current.names[0]}"].tags), null) + value = try(length(module.vpc.private_subnet_attributes_by_az["private_dualstack/${data.aws_availability_zones.current.names[0]}"].tags), null) } diff --git a/test/hcl_fixtures/transit_gateway_base/providers.tf b/examples/transit_gateway/providers.tf similarity index 69% rename from test/hcl_fixtures/transit_gateway_base/providers.tf rename to examples/transit_gateway/providers.tf index da0117c..fdd5859 100644 --- a/test/hcl_fixtures/transit_gateway_base/providers.tf +++ b/examples/transit_gateway/providers.tf @@ -8,3 +8,8 @@ terraform { } } +# Provider definition +provider "aws" { + region = var.aws_region +} + diff --git a/examples/transit_gateway/variables.tf b/examples/transit_gateway/variables.tf index e69de29..d19feea 100644 --- a/examples/transit_gateway/variables.tf +++ b/examples/transit_gateway/variables.tf @@ -0,0 +1,17 @@ + +variable "aws_region" { + description = "AWS Regions to create in Cloud WAN's core network." + type = string + + default = "eu-west-2" +} + +variable "prefixes" { + type = map(string) + description = "(optional) describe your variable" + + default = { + primary = "10.0.0.0/8", + internal = "192.168.0.0/16" + } +} \ No newline at end of file diff --git a/main.tf b/main.tf index eddf09d..2685ad0 100644 --- a/main.tf +++ b/main.tf @@ -1,3 +1,6 @@ + +# ---------- SUBNET CALCULATOR (IPv4 AND IPv6) ---------- + module "calculate_subnets" { source = "./modules/calculate_subnets" @@ -16,7 +19,7 @@ module "calculate_subnets_ipv6" { subnets = var.subnets } -# VPC RESOURCE (and secondary CIDR blocks - if configured) +# ---------- VPC RESOURCE ---------- resource "aws_vpc" "main" { count = local.create_vpc ? 1 : 0 @@ -37,7 +40,7 @@ resource "aws_vpc" "main" { ) } -# Secondary CIDR blocks - if configured +# ---------- SECONDARY IPv4 CIDR BLOCK (if configured) ---------- resource "aws_vpc_ipv4_cidr_block_association" "secondary" { count = (var.vpc_secondary_cidr && !local.create_vpc) ? 1 : 0 @@ -46,17 +49,19 @@ resource "aws_vpc_ipv4_cidr_block_association" "secondary" { ipv4_ipam_pool_id = var.vpc_ipv4_ipam_pool_id } -# PUBLIC SUBNET CONFIGURATION - +# ---------- PUBLIC SUBNET CONFIGURATION ---------- # Public Subnets resource "aws_subnet" "public" { for_each = contains(local.subnet_keys, "public") ? toset(local.azs) : toset([]) - availability_zone = each.key - vpc_id = local.vpc.id - cidr_block = local.calculated_subnets["public"][each.key] - assign_ipv6_address_on_creation = can(local.calculated_subnets_ipv6["public"][each.key]) - ipv6_cidr_block = can(local.calculated_subnets_ipv6["public"][each.key]) ? local.calculated_subnets_ipv6["public"][each.key] : null + availability_zone = each.key + vpc_id = local.vpc.id + cidr_block = can(local.calculated_subnets["public"][each.key]) ? local.calculated_subnets["public"][each.key] : null + ipv6_cidr_block = can(local.calculated_subnets_ipv6["public"][each.key]) ? local.calculated_subnets_ipv6["public"][each.key] : null + ipv6_native = contains(local.subnets_with_ipv6_native, "public") ? true : false + map_public_ip_on_launch = local.public_ipv6only ? null : true + assign_ipv6_address_on_creation = local.public_ipv6only || local.public_dualstack ? true : null + enable_resource_name_dns_aaaa_record_on_launch = local.public_ipv6only || local.public_dualstack ? true : false tags = merge( { Name = "${local.subnet_names["public"]}-${each.key}" }, @@ -127,16 +132,36 @@ resource "aws_internet_gateway" "main" { ) } -# Route: from public subnets to the Internet gateway +# Egress-only IGW (if indicated) +resource "aws_egress_only_internet_gateway" "eigw" { + count = var.vpc_egress_only_internet_gateway ? 1 : 0 + vpc_id = local.vpc.id + + tags = merge( + { "Name" = var.name }, + module.tags.tags_aws + ) +} + +# Route: 0.0.0.0/0 from public subnets to the Internet gateway resource "aws_route" "public_to_igw" { - for_each = contains(local.subnet_keys, "public") ? toset(local.azs) : toset([]) + for_each = contains(local.subnet_keys, "public") && !local.public_ipv6only ? toset(local.azs) : toset([]) route_table_id = aws_route_table.public[each.key].id destination_cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.main[0].id } -# Route: from public subnets to the Transit Gateway (if configured in var.transit_gateway_routes) +# Route: ::/0 from public subnets to the Internet gateway +resource "aws_route" "public_ipv6_to_igw" { + for_each = contains(local.subnet_keys, "public") && (local.public_ipv6only || local.public_dualstack) ? toset(local.azs) : toset([]) + + route_table_id = aws_route_table.public[each.key].id + destination_ipv6_cidr_block = "::/0" + gateway_id = aws_internet_gateway.main[0].id +} + +# Route: IPv4 routes from public subnets to the Transit Gateway (if configured in var.transit_gateway_routes) resource "aws_route" "public_to_tgw" { for_each = (contains(local.subnet_keys, "public") && contains(local.subnets_tgw_routed, "public")) ? toset(local.azs) : toset([]) @@ -145,9 +170,28 @@ resource "aws_route" "public_to_tgw" { transit_gateway_id = var.transit_gateway_id route_table_id = aws_route_table.public[each.key].id + + depends_on = [ + aws_ec2_transit_gateway_vpc_attachment.tgw + ] +} + +# Route: IPv6 routes from public subnets to the Transit Gateway (if configured in var.transit_gateway_ipv6_routes) +resource "aws_route" "ipv6_public_to_tgw" { + for_each = (contains(local.subnet_keys, "public") && contains(local.ipv6_subnets_tgw_routed, "public")) ? toset(local.azs) : toset([]) + + destination_ipv6_cidr_block = can(regex("^pl-", var.transit_gateway_ipv6_routes["public"])) ? null : var.transit_gateway_ipv6_routes["public"] + destination_prefix_list_id = can(regex("^pl-", var.transit_gateway_ipv6_routes["public"])) ? var.transit_gateway_ipv6_routes["public"] : null + + transit_gateway_id = var.transit_gateway_id + route_table_id = aws_route_table.public[each.key].id + + depends_on = [ + aws_ec2_transit_gateway_vpc_attachment.tgw + ] } -# Route: from public subnets to AWS Cloud WAN's core network (if configured in var.core_network_routes) +# Route: IPv4 routes from public subnets to AWS Cloud WAN's core network (if configured in var.core_network_routes) resource "aws_route" "public_to_cwan" { for_each = (contains(local.subnet_keys, "public") && contains(local.subnets_cwan_routed, "public") && local.create_cwan_routes) ? toset(local.azs) : toset([]) @@ -163,8 +207,23 @@ resource "aws_route" "public_to_cwan" { ] } -# PRIVATE SUBNETS CONFIGURATION +# Route: IPv6 routes from public subnets to AWS Cloud WAN's core network (if configured in var.core_network_routes) +resource "aws_route" "ipv6_public_to_cwan" { + for_each = (contains(local.subnet_keys, "public") && contains(local.ipv6_subnets_cwan_routed, "public") && local.create_cwan_routes) ? toset(local.azs) : toset([]) + + destination_ipv6_cidr_block = can(regex("^pl-", var.core_network_ipv6_routes["public"])) ? null : var.core_network_ipv6_routes["public"] + destination_prefix_list_id = can(regex("^pl-", var.core_network_ipv6_routes["public"])) ? var.core_network_ipv6_routes["public"] : null + + core_network_arn = var.core_network.arn + route_table_id = aws_route_table.public[each.key].id + + depends_on = [ + aws_networkmanager_vpc_attachment.cwan, + aws_networkmanager_attachment_accepter.cwan + ] +} +# ---------- PRIVATE SUBNETS CONFIGURATION ---------- # Private Subnets resource "aws_subnet" "private" { for_each = toset(try(local.private_per_az, [])) @@ -172,11 +231,11 @@ resource "aws_subnet" "private" { availability_zone = split("/", each.key)[1] vpc_id = local.vpc.id cidr_block = can(local.calculated_subnets[split("/", each.key)[0]][split("/", each.key)[1]]) ? local.calculated_subnets[split("/", each.key)[0]][split("/", each.key)[1]] : null - map_public_ip_on_launch = false - assign_ipv6_address_on_creation = can(local.calculated_subnets_ipv6[split("/", each.key)[0]][split("/", each.key)[1]]) ipv6_cidr_block = can(local.calculated_subnets_ipv6[split("/", each.key)[0]][split("/", each.key)[1]]) ? local.calculated_subnets_ipv6[split("/", each.key)[0]][split("/", each.key)[1]] : null ipv6_native = contains(local.subnets_with_ipv6_native, split("/", each.key)[0]) ? true : false - enable_resource_name_dns_aaaa_record_on_launch = contains(local.subnets_with_ipv6_native, split("/", each.key)[0]) ? true : false + map_public_ip_on_launch = contains(local.subnets_with_ipv6_native, split("/", each.key)[0]) ? null : false + assign_ipv6_address_on_creation = contains(local.subnets_with_ipv6_native, split("/", each.key)[0]) ? true : try(var.subnets[each.key].assign_ipv6_address_on_creation, false) + enable_resource_name_dns_aaaa_record_on_launch = contains(local.subnets_with_ipv6_native, split("/", each.key)[0]) ? true : try(var.subnets[each.key].enable_resource_name_dns_aaaa_record_on_launch, false) tags = merge( { Name = "${local.subnet_names[split("/", each.key)[0]]}-${split("/", each.key)[1]}" }, @@ -219,7 +278,16 @@ resource "aws_route" "private_to_nat" { nat_gateway_id = local.nat_per_az[split("/", each.key)[1]].id } -# Route: from private subnets to the Transit Gateway (if configured in var.transit_gateway_routes) +# Route: from the private subnet to the Egress-only IGW (if configured) +resource "aws_route" "private_to_egress_only" { + for_each = toset(try(local.private_subnet_names_egress_routed, [])) + + route_table_id = aws_route_table.private[each.key].id + destination_ipv6_cidr_block = "0::/0" + egress_only_gateway_id = aws_egress_only_internet_gateway.eigw[0].id +} + +# Route: IPv4 routes from private subnets to the Transit Gateway (if configured in var.transit_gateway_routes) resource "aws_route" "private_to_tgw" { for_each = toset(local.private_subnet_key_names_tgw_routed) @@ -228,9 +296,28 @@ resource "aws_route" "private_to_tgw" { route_table_id = aws_route_table.private[each.key].id transit_gateway_id = var.transit_gateway_id + + depends_on = [ + aws_ec2_transit_gateway_vpc_attachment.tgw + ] } -# Route: from private subnets to AWS Cloud WAN's core network (if configured in var.core_network_routes) +# Route: IPv6 routes from private subnets to the Transit Gateway (if configured in var.transit_gateway_ipv6_routes) +resource "aws_route" "ipv6_private_to_tgw" { + for_each = toset(local.ipv6_private_subnet_key_names_tgw_routed) + + destination_ipv6_cidr_block = can(regex("^pl-", var.transit_gateway_ipv6_routes[split("/", each.key)[0]])) ? null : var.transit_gateway_ipv6_routes[split("/", each.key)[0]] + destination_prefix_list_id = can(regex("^pl-", var.transit_gateway_ipv6_routes[split("/", each.key)[0]])) ? var.transit_gateway_ipv6_routes[split("/", each.key)[0]] : null + + route_table_id = aws_route_table.private[each.key].id + transit_gateway_id = var.transit_gateway_id + + depends_on = [ + aws_ec2_transit_gateway_vpc_attachment.tgw + ] +} + +# Route: IPv4 routes from private subnets to AWS Cloud WAN's core network (if configured in var.core_network_routes) resource "aws_route" "private_to_cwan" { for_each = { for k, v in toset(local.private_subnet_key_names_cwan_routes) : k => v @@ -249,8 +336,26 @@ resource "aws_route" "private_to_cwan" { ] } -# TRANSIT GATEWAY SUBNET CONFIGURATION +# Route: IPv6 routes from private subnets to AWS Cloud WAN's core network (if configured in var.core_network_routes) +resource "aws_route" "ipv6_private_to_cwan" { + for_each = { + for k, v in toset(local.ipv6_private_subnet_keys_names_cwan_routes) : k => v + if local.create_cwan_routes + } + + destination_ipv6_cidr_block = can(regex("^pl-", var.core_network_ipv6_routes[split("/", each.key)[0]])) ? null : var.core_network_ipv6_routes[split("/", each.key)[0]] + destination_prefix_list_id = can(regex("^pl-", var.core_network_ipv6_routes[split("/", each.key)[0]])) ? var.core_network_ipv6_routes[split("/", each.key)[0]] : null + + core_network_arn = var.core_network.arn + route_table_id = aws_route_table.private[each.key].id + depends_on = [ + aws_networkmanager_vpc_attachment.cwan, + aws_networkmanager_attachment_accepter.cwan + ] +} + +# ---------- TRANSIT GATEWAY SUBNET CONFIGURATION ---------- # Transit Gateway Subnets resource "aws_subnet" "tgw" { for_each = contains(local.subnet_keys, "transit_gateway") ? toset(local.azs) : toset([]) @@ -258,6 +363,7 @@ resource "aws_subnet" "tgw" { availability_zone = each.key vpc_id = local.vpc.id cidr_block = local.calculated_subnets["transit_gateway"][each.key] + ipv6_cidr_block = can(local.calculated_subnets_ipv6["transit_gateway"][each.key]) ? local.calculated_subnets_ipv6["transit_gateway"][each.key] : null tags = merge( { Name = "${local.subnet_names["transit_gateway"]}-${each.key}" }, @@ -291,7 +397,6 @@ resource "aws_route_table_association" "tgw" { resource "aws_route" "tgw_to_nat" { for_each = (try(var.subnets.transit_gateway.connect_to_public_natgw == true, false) && contains(local.subnet_keys, "public")) ? toset(local.azs) : toset([]) - route_table_id = aws_route_table.tgw[each.key].id destination_cidr_block = "0.0.0.0/0" # try to get nat for AZ, else use singular nat @@ -310,17 +415,10 @@ resource "aws_ec2_transit_gateway_vpc_attachment" "tgw" { transit_gateway_default_route_table_propagation = try(var.subnets.transit_gateway.transit_gateway_default_route_table_propagation, null) appliance_mode_support = try(var.subnets.transit_gateway.transit_gateway_appliance_mode_support, "disable") dns_support = try(var.subnets.transit_gateway.transit_gateway_dns_support, "enable") + ipv6_support = local.tgw_dualstack ? "enable" : "disable" } -resource "aws_ec2_transit_gateway_route_table_association" "tgw" { - count = can(var.subnets.transit_gateway.transit_gateway_route_table_id) ? 1 : 0 - - transit_gateway_attachment_id = aws_ec2_transit_gateway_vpc_attachment.tgw[0].id - transit_gateway_route_table_id = var.subnets.transit_gateway.transit_gateway_route_table_id -} - -# CORE NETWORK SUBNET CONFIGURATION - +# ---------- CORE NETWORK SUBNET CONFIGURATION ---------- # Core Network Subnets resource "aws_subnet" "cwan" { for_each = contains(local.subnet_keys, "core_network") ? toset(local.azs) : toset([]) @@ -328,6 +426,7 @@ resource "aws_subnet" "cwan" { availability_zone = each.key vpc_id = local.vpc.id cidr_block = local.calculated_subnets["core_network"][each.key] + ipv6_cidr_block = can(local.calculated_subnets_ipv6["core_network"][each.key]) ? local.calculated_subnets_ipv6["core_network"][each.key] : null tags = merge( { Name = "${local.subnet_names["core_network"]}-${each.key}" }, @@ -375,7 +474,7 @@ resource "aws_networkmanager_vpc_attachment" "cwan" { vpc_arn = local.vpc.arn options { - ipv6_support = try(var.subnets.core_nework.ipv6_support, false) + ipv6_support = local.cwan_dualstack ? true : false appliance_mode_support = try(var.subnets.core_network.appliance_mode_support, false) } @@ -394,9 +493,6 @@ resource "aws_networkmanager_attachment_accepter" "cwan" { attachment_type = "VPC" } -# Core Network VPC attachment acceptance (if required) -# TO ADD - # FLOW LOGS module "flow_logs" { count = local.create_flow_logs ? 1 : 0 @@ -408,33 +504,4 @@ module "flow_logs" { vpc_id = local.vpc.id tags = module.tags.tags_aws -} - -# IPv6 Routing -resource "aws_egress_only_internet_gateway" "eigw" { - count = var.vpc_egress_only_internet_gateway ? 1 : 0 - vpc_id = local.vpc.id - - tags = merge( - { "Name" = var.name }, - module.tags.tags_aws - ) -} - -resource "aws_route" "private_to_egress_only" { - for_each = toset(try(local.private_subnet_names_egress_routed, [])) - - route_table_id = aws_route_table.private[each.key].id - destination_ipv6_cidr_block = "0::/0" - # try to get nat for AZ, else use singular nat - egress_only_gateway_id = aws_egress_only_internet_gateway.eigw[0].id -} - -resource "aws_route" "public_to_egress_only" { - for_each = local.public_with_eigw ? toset(local.azs) : toset([]) - - route_table_id = aws_route_table.public[each.key].id - destination_ipv6_cidr_block = "0::/0" - # try to get nat for AZ, else use singular nat - egress_only_gateway_id = aws_egress_only_internet_gateway.eigw[0].id } \ No newline at end of file diff --git a/modules/calculate_subnets_ipv6/main.tf b/modules/calculate_subnets_ipv6/main.tf index b6ef7b9..9f94352 100644 --- a/modules/calculate_subnets_ipv6/main.tf +++ b/modules/calculate_subnets_ipv6/main.tf @@ -2,20 +2,23 @@ locals { # group subnets by type and create names for each type type_grouped_named_subnets_to_build = { for name, subnet_definition in var.subnets : name => [for _, az in var.azs : "${name}/${az}"] } # which network groups require calculating subnet - subnets_to_calculate = [for type, subnet_definition in var.subnets : type if can(subnet_definition.assign_ipv6_address_on_creation)] + types_to_calculate = [for type, subnet_definition in var.subnets : type if can(subnet_definition.assign_ipv6_cidr)] # network groups that are set explicitly - types_with_no_ipv6 = setsubtract(keys(var.subnets), local.subnets_to_calculate) + types_with_explicit = [for type, subnet_definition in var.subnets : type if can(subnet_definition.ipv6_cidrs)] # network object to pass to calculating module - calculated_subnet_objects = flatten([for _, type in local.subnets_to_calculate : [for _, v in local.type_grouped_named_subnets_to_build[type] : { + calculated_subnet_objects = flatten([for _, type in local.types_to_calculate : [for _, v in local.type_grouped_named_subnets_to_build[type] : { "name" = v "netmask" = 64 } ]]) + + # map of explicit cidrs to az + explict_cidrs_grouped = { for _, type in local.types_with_explicit : type => zipmap(var.azs, var.subnets[type].ipv6_cidrs[*]) } } module "subnet_calculator" { - count = local.subnets_to_calculate == [] ? 0 : 1 + count = local.types_to_calculate == [] ? 0 : 1 source = "drewmullen/subnets/cidr" version = "1.0.2" diff --git a/modules/calculate_subnets_ipv6/outputs.tf b/modules/calculate_subnets_ipv6/outputs.tf index 47174a3..a94a5e8 100644 --- a/modules/calculate_subnets_ipv6/outputs.tf +++ b/modules/calculate_subnets_ipv6/outputs.tf @@ -1,4 +1,4 @@ output "subnets_ipv6" { description = "Outputs subnets prefixes by type (private, public). Derived from split(var.separator, )." - value = try(module.subnet_calculator[0].grouped_by_separator, {}) + value = merge(try(local.explict_cidrs_grouped, {}), try(module.subnet_calculator[0].grouped_by_separator, {})) } \ No newline at end of file diff --git a/test/examples_ipv6_test.go b/test/examples_ipv6_test.go new file mode 100644 index 0000000..c315b39 --- /dev/null +++ b/test/examples_ipv6_test.go @@ -0,0 +1,17 @@ +package test + +import ( + "testing" + + "github.com/gruntwork-io/terratest/modules/terraform" +) + +func TestExamplesIPv6(t *testing.T) { + + terraformOptions := &terraform.Options{ + TerraformDir: "../examples/ipv6", + } + + defer terraform.Destroy(t, terraformOptions) + terraform.InitAndApply(t, terraformOptions) +} \ No newline at end of file diff --git a/test/examples_transit_gateway_test.go b/test/examples_transit_gateway_test.go index d412049..3202786 100644 --- a/test/examples_transit_gateway_test.go +++ b/test/examples_transit_gateway_test.go @@ -10,7 +10,6 @@ import ( func TestExamplesTransitGateway(t *testing.T) { terraformOptions := &terraform.Options{ TerraformDir: "../examples/transit_gateway", - Vars: map[string]interface{}{}, } defer terraform.Destroy(t, terraformOptions) diff --git a/test/hcl_fixtures/transit_gateway_base/main.tf b/test/hcl_fixtures/transit_gateway_base/main.tf deleted file mode 100644 index e8c4873..0000000 --- a/test/hcl_fixtures/transit_gateway_base/main.tf +++ /dev/null @@ -1,19 +0,0 @@ -resource "aws_ec2_transit_gateway" "example" { - description = "example" -} - -resource "aws_ec2_managed_prefix_list" "example" { - name = "All VPC CIDR-s" - address_family = "IPv4" - max_entries = 5 - - dynamic "entry" { - for_each = var.prefixes - - content { - cidr = entry.value - description = entry.key - } - } -} - diff --git a/test/hcl_fixtures/transit_gateway_base/outputs.tf b/test/hcl_fixtures/transit_gateway_base/outputs.tf deleted file mode 100644 index 8106de8..0000000 --- a/test/hcl_fixtures/transit_gateway_base/outputs.tf +++ /dev/null @@ -1,9 +0,0 @@ -output "tgw_id" { - description = "aws_ec2_transit_gateway ID." - value = aws_ec2_transit_gateway.example.id -} - -output "prefix_list_id" { - description = "aws_ec2_managed_prefix_list ID." - value = aws_ec2_managed_prefix_list.example.id -} diff --git a/test/hcl_fixtures/transit_gateway_base/variables.tf b/test/hcl_fixtures/transit_gateway_base/variables.tf deleted file mode 100644 index 8df5d40..0000000 --- a/test/hcl_fixtures/transit_gateway_base/variables.tf +++ /dev/null @@ -1,8 +0,0 @@ -variable "prefixes" { - type = map(string) - description = "(optional) describe your variable" - default = { - primary = "10.0.0.0/8", - internal = "192.168.0.0/16" - } -} diff --git a/variables.tf b/variables.tf index 9f428c4..ecc84af 100644 --- a/variables.tf +++ b/variables.tf @@ -72,6 +72,36 @@ variable "vpc_ipv4_netmask_length" { default = null } +variable "vpc_assign_generated_ipv6_cidr_block" { + description = "Whether the VPC has IPv6-generated CIDR block." + type = bool + default = null +} + +variable "vpc_ipv6_ipam_pool_id" { + description = "Set to use IPAM to get CIDR IPV6 block." + type = string + default = null +} + +variable "vpc_ipv6_cidr_block" { + description = "CIDR range to assign to VPC if creating VPC or to associte as a secondary CIDR. Overridden by var.vpc_id output from data.aws_vpc." + type = string + default = null +} + +variable "vpc_ipv6_netmask_length" { + description = "Set to use IPAM to get CIDR block using a specified netmask. Must be set with var.vpc_ipv6_ipam_pool_id." + type = string + default = null +} + +variable "vpc_egress_only_internet_gateway" { + description = "Set to use the egress only gateway for all traffic Ipv6 going to the Internet." + type = bool + default = false +} + variable "subnets" { description = <<-EOF Configuration of subnets to build in VPC. 1 Subnet per AZ is created. Subnet types are defined as maps with the available keys: "private", "public", "transit_gateway". Each Subnet type offers its own set of available arguments detailed below. @@ -79,6 +109,7 @@ variable "subnets" { **Attributes shared across subnet types:** - `cidrs` = (Optional|list(string)) **Cannot set if `netmask` is set.** List of CIDRs to set to subnets. Count of CIDRs defined must match quatity of azs in `az_count`. - `netmask` = (Optional|Int) Netmask of the `var.cidr_block` to calculate for each subnet. **Cannot set if `cidrs` is set.** + - `assign_ipv6_cidr` = (Optional|bool) - `name_prefix` = (Optional|String) A string prefix to use for the name of your subnet and associated resources. Subnet type key name is used if omitted (aka private, public, transit_gateway). Example `name_prefix = "private"` for `var.subnets.private` is redundant. - `tags` = (Optional|map(string)) Tags to set on the subnet and associated resources. @@ -92,7 +123,7 @@ variable "subnets" { **transit_gateway subnet type options:** - All shared keys above - - `connect_to_public_natgw` = (Optional|string) Determines if routes to NAT Gateways should be created. Specify the CIDR range or a prefix-list-id that you want routed to nat gateway. Usually `0.0.0.0/0`. Must also set `var.subnets.public.nat_gateway_configuration`. + - `connect_to_public_natgw` = (Optional|string) Determines if routes to NAT Gateways should be created. Specify the CIDR range or a prefix-list-id that you want routed to nat gateway. Usually `0.0.0.0/0`. Must also set `var.subnets.public.nat_gateway_configuration`. - `transit_gateway_default_route_table_association` = (Optional|bool) Boolean whether the VPC Attachment should be associated with the EC2 Transit Gateway association default route table. This cannot be configured or perform drift detection with Resource Access Manager shared EC2 Transit Gateways. - `transit_gateway_default_route_table_propagation` = (Optional|bool) Boolean whether the VPC Attachment should propagate routes with the EC2 Transit Gateway propagation default route table. This cannot be configured or perform drift detection with Resource Access Manager shared EC2 Transit Gateways. - `transit_gateway_appliance_mode_support` = (Optional|string) Whether Appliance Mode is enabled. If enabled, a traffic flow between a source and a destination uses the same Availability Zone for the VPC attachment for the lifetime of that flow. Valid values: `disable` (default) and `enable`. @@ -141,26 +172,29 @@ EOF # All var.subnets.public valid keys validation { - error_message = "Invalid key in public subnets. Valid options include: \"cidrs\", \"netmask\", \"name_prefix\", \"nat_gateway_configuration\", \"tags\"." + error_message = "Invalid key in public subnets. Valid options include: \"cidrs\", \"netmask\", \"name_prefix\", \"nat_gateway_configuration\", \"ipv6_native\", \"assign_ipv6_cidr\", \"ipv6_cidrs\", \"tags\"." condition = length(setsubtract(keys(try(var.subnets.public, {})), [ "cidrs", "netmask", "name_prefix", "nat_gateway_configuration", - "tags", - "assign_ipv6_address_on_creation", - "connect_to_eigw" + "ipv6_native", + "assign_ipv6_cidr", + "ipv6_cidrs", + "tags" ])) == 0 } # All var.subnets.transit_gateway valid keys validation { - error_message = "Invalid key in transit_gateway subnets. Valid options include: \"cidrs\", \"netmask\", \"name_prefix\", \"transit_gateway_default_route_table_association\", \"transit_gateway_default_route_table_propagation\", \"transit_gateway_appliance_mode_support\", \"transit_gateway_dns_support\", \"tags\"." + error_message = "Invalid key in transit_gateway subnets. Valid options include: \"cidrs\", \"netmask\", \"name_prefix\", \"connect_to_public_natgw\", \"assign_ipv6_cidr\", \"ipv6_cidrs\", \"transit_gateway_default_route_table_association\", \"transit_gateway_default_route_table_propagation\", \"transit_gateway_appliance_mode_support\", \"transit_gateway_dns_support\", \"tags\"." condition = length(setsubtract(keys(try(var.subnets.transit_gateway, {})), [ "cidrs", "netmask", "name_prefix", "connect_to_public_natgw", + "assign_ipv6_cidr", + "ipv6_cidrs", "transit_gateway_default_route_table_association", "transit_gateway_default_route_table_propagation", "transit_gateway_appliance_mode_support", @@ -171,13 +205,14 @@ EOF # All var.subnets.core_network valid keys validation { - error_message = "Invalid key in core_network subnets. Valid options include: \"cidrs\", \"netmask\", \"name_prefix\", \"ipv6_support\", \"appliance_mode_support\", \"require_acceptance\", \"accept_attachment\", \"tags\"." + error_message = "Invalid key in core_network subnets. Valid options include: \"cidrs\", \"netmask\", \"name_prefix\", \"connect_to_public_natgw\", \"assign_ipv6_cidr\", \"ipv6_cidrs\", \"appliance_mode_support\", \"require_acceptance\", \"accept_attachment\", \"tags\"." condition = length(setsubtract(keys(try(var.subnets.core_network, {})), [ "cidrs", "netmask", "name_prefix", "connect_to_public_natgw", - "ipv6_support", + "assign_ipv6_cidr", + "ipv6_cidrs", "appliance_mode_support", "require_acceptance", "accept_attachment", @@ -186,7 +221,7 @@ EOF } validation { - error_message = "Each subnet type must contain only 1 key: `cidrs` or `netmask` or `ipv_native`." + error_message = "Each subnet type must contain only 1 key: `cidrs` or `netmask` or `ipv6_native`." condition = alltrue([for subnet_type, v in var.subnets : length(setintersection(keys(v), ["cidrs", "netmask", "ipv6_native"])) == 1]) } @@ -259,6 +294,23 @@ EOF default = {} } +variable "transit_gateway_ipv6_routes" { + description = <<-EOF + Configuration of IPv6 route(s) to transit gateway. + For each `public` and/or `private` subnets named in the `subnets` variable, + Optionally create routes from the subnet to transit gateway. Specify the CIDR range or a prefix-list-id that you want routed to the transit gateway. + Example: + ``` + transit_gateway_routes = { + public = "::/0" + private = "pl-123" + } + ``` +EOF + type = any + default = {} +} + variable "core_network" { type = object({ id = string @@ -289,33 +341,19 @@ EOF default = {} } -# Variables used for IPv6 -variable "vpc_ipv6_cidr_block" { - description = "CIDR range to assign to VPC if creating VPC or to associte as a secondary CIDR. Overridden by var.vpc_id output from data.aws_vpc." - default = null - type = string -} - -variable "vpc_assign_generated_ipv6_cidr_block" { - description = "Whether the vpc has ipv6 generated cider block" - type = bool - default = false -} - -variable "vpc_ipv6_ipam_pool_id" { - description = "Set to use IPAM to get CIDR IPV6 block." - type = string - default = null -} - -variable "vpc_ipv6_netmask_length" { - description = "Set to use IPAM to get CIDR block using a specified netmask. Must be set with var.vpc_ipv6_ipam_pool_id." - type = string - default = null -} - -variable "vpc_egress_only_internet_gateway" { - description = "Set to use the egress only gateway for all traffic Ipv6 going to the Internet." - type = bool - default = false +variable "core_network_ipv6_routes" { + description = <<-EOF + Configuration of IPv6 route(s) to AWS Cloud WAN's core network. + For each `public` and/or `private` subnets named in the `subnets` variable, optionally create routes from the subnet to the core network. + You can specify either a CIDR range or a prefix-list-id that you want routed to the core network. + Example: + ``` + core_network_routes = { + public = "::/0" + private = "pl-123" + } + ``` +EOF + type = any + default = {} } From 8b9a4491f81a0b4b6b6771f83321376c98b9e1a1 Mon Sep 17 00:00:00 2001 From: Pablo Sanchez Carmona Date: Sat, 8 Apr 2023 10:55:46 +0200 Subject: [PATCH 4/6] README update and minor updates --- .header.md | 93 ++++++++++++++++++++++++++------------- README.md | 120 ++++++++++++++++++++++++++++++++++++--------------- main.tf | 9 ++-- outputs.tf | 5 +++ variables.tf | 54 ++++++++++++++--------- 5 files changed, 192 insertions(+), 89 deletions(-) diff --git a/.header.md b/.header.md index e9a566a..a1ea9de 100644 --- a/.header.md +++ b/.header.md @@ -1,35 +1,45 @@ # AWS VPC Module -This module can be used to deploy a pragmatic VPC with various subnets types in # AZs. Common deployment examples can be found in [examples/](https://github.com/aws-ia/terraform-aws-vpc/tree/main/examples). Subnet CIDRs can be explicitly set via list of string argument `cidrs` or set via a number `netmask` argument. +This module can be used to deploy a pragmatic VPC with various subnets types in # AZs. Common deployment examples can be found in [examples/](https://github.com/aws-ia/terraform-aws-vpc/tree/main/examples). __Note: For information regarding the 4.0 upgrade see our [upgrade guide](https://github.com/aws-ia/terraform-aws-vpc/blob/main/UPGRADE-GUIDE-4.0.md).__ ## Usage -The example below builds a VPC with public and private subnets in 3 AZs. Each subnet calulates a CIDR based on the `netmask` argument passed. The public subnets build nat gateways in each AZ but optionally can be switched to `single_az`. +The example below builds a dual-stack VPC with public and private subnets in 3 AZs. Each subnet calculates an IPv4 CIDR based on the `netmask` argument passed, and an IPv6 CIDR with a /64 prefix length. The public subnets build NAT gateways in each AZ but optionally can be switched to `single_az`. An Egress-only Internet gateway is created by using the variable `vpc_egress_only_internet_gateway`. ```hcl module "vpc" { source = "aws-ia/vpc/aws" - version = ">= 4.0.0" + version = ">= 4.2.0" - name = "multi-az-vpc" - cidr_block = "10.0.0.0/20" - az_count = 3 + name = "multi-az-vpc" + cidr_block = "10.0.0.0/16" + vpc_assign_generated_ipv6_cidr_block = true + vpc_egress_only_internet_gateway = true + az_count = 3 subnets = { + # Dual-stack subnet public = { name_prefix = "my_public" # omit to prefix with "public" netmask = 24 + assign_ipv6_cidr = true nat_gateway_configuration = "all_azs" # options: "single_az", "none" } - + # IPv4 only subnet private = { # omitting name_prefix defaults value to "private" # name_prefix = "private_with_egress" netmask = 24 connect_to_public_natgw = true } + # IPv6-only subnet + private_ipv6 = { + ipv6_native = true + assign_ipv6_cidr = true + connect_to_eigw = true + } } vpc_flow_logs = { @@ -43,13 +53,7 @@ module "vpc" { There are 3 reserved keys for subnet key names in var.subnets corresponding to types "public", "transit_gateway", and "core_network" [(an AWS Cloud WAN feature)](https://docs.aws.amazon.com/vpc/latest/cloudwan/cloudwan-networks-working-with.html). Other custom subnet key names are valid are and those subnets will be private subnets. -```terraform -transit_gateway_id = <> -transit_gateway_routes = { - public = "pl-123" - vpce = "10.0.0.0/8" -} - +```hcl subnets = { public = { name_prefix = "my-public" # omit to prefix with "public" @@ -71,10 +75,28 @@ subnets = { # name_prefix = "private" netmask = 24 } +``` + +```hcl +transit_gateway_id = <> +transit_gateway_routes = { + private = "0.0.0.0/0" + vpce = "pl-123" +} +transit_gateway_ipv6_routes = { + private = "::/0" +} + +subnets = { + private = { + netmask = 24 + assign_ipv6_cidr = true + } + vpce = { netmask = 24} transit_gateway = { netmask = 28 - connect_to_public_natgw = true + assign_ipv6_cidr = true transit_gateway_default_route_table_association = true transit_gateway_default_route_table_propagation = true transit_gateway_appliance_mode_support = "enable" @@ -86,7 +108,7 @@ subnets = { } ``` -```terraform +```hcl core_network = { id = <> arn = <> @@ -94,16 +116,20 @@ core_network = { core_network_routes = { workload = "pl-123" } +core_network_ipv6_routes = { + workload = "::/0" +} subnets = { workload = { - name_prefix = "workload-private" - netmask = 24 + name_prefix = "workload-private" + netmask = 24 + assign_ipv6_cidr = true } core_network = { netmask = 28 - ipv6_support = false + assign_ipv6_cidr = true appliance_mode_support = false require_acceptance = true accept_attachment = true @@ -116,7 +142,7 @@ subnets = { ## Updating a VPC with new or removed subnets -If using `netmask` to calculate subnets and you wish to either add or remove subnets (ex: adding / removing an AZ), you may have to change from using `netmask` for some subnets and set to explicit instead. Private subnets are always calculated before public. +If using `netmask` or `assign_ipv6_cidr` to calculate subnets and you wish to either add or remove subnets (ex: adding / removing an AZ), you may have to change from using `netmask` / `assign_ipv6_cidr` for some subnets and set to explicit instead. Private subnets are always calculated before public. When changing to explicit cidrs, subnets are always ordered by AZ. `0` -> a, `1` -> b, etc. @@ -124,32 +150,38 @@ Example: Changing from 2 azs to 3 Before: ```hcl -cidr_block = "10.0.0.0/16" -az_count = 2 +cidr_block = "10.0.0.0/16" +vpc_assign_generated_ipv6_cidr_block = true +az_count = 2 subnets = { public = { - netmask = 24 + netmask = 24 + assign_ipv6_cidr = true } private = { - netmask = 24 + netmask = 24 + assign_ipv6_cidr = true } } ``` After: ```hcl -cidr_block = "10.0.0.0/16" +cidr_block = "10.0.0.0/16" +vpc_assign_generated_ipv6_cidr_block = true az_count = 3 subnets = { public = { - cidrs = ["10.0.0.0/24", "10.0.1.0/24", "10.0.4.0/24"] + cidrs = ["10.0.0.0/24", "10.0.1.0/24", "10.0.4.0/24"] + ipv6_cidrs = ["2a05:d01c:bc3:b200::/64", "2a05:d01c:bc3:b201::/64", "2a05:d01c:bc3:b204::/64"] } private = { - cidrs = ["10.0.2.0/24", "10.0.3.0/24", "10.0.5.0/24"] + cidrs = ["10.0.2.0/24", "10.0.3.0/24", "10.0.5.0/24"] + ipv6_cidrs = ["2a05:d01c:bc3:b202::/64", "2a05:d01c:bc3:b203::/64", "2a05:d01c:bc3:b205::/64"] } } ``` @@ -166,7 +198,7 @@ Example Configuration: ```terraform module "vpc" { source = "aws-ia/vpc/aws" - version = ">= 4.0.0" + version = ">= 4.2.0" name = "multi-az-vpc" cidr_block = "10.0.0.0/20" @@ -228,6 +260,7 @@ This happens when the Core Network's VPC attachment requires acceptance, so it's subnets = { core_network = { netmask = 28 + assign_ipv6_cidr = true require_acceptance = true accept_attachment = true } @@ -240,6 +273,7 @@ subnets = { subnets = { core_network = { netmask = 28 + assign_ipv6_cidr = true require_acceptance = true accept_attachment = true } @@ -252,6 +286,7 @@ After you apply and the attachment is accepted (outside the module), change the subnets = { core_network = { netmask = 28 + assign_ipv6_cidr = true require_acceptance = false } } @@ -261,4 +296,4 @@ subnets = { # Contributing -Please see our [developer documentation](https://github.com/aws-ia/terraform-aws-vpc/blob/main/contributing.md) for guidance on contributing to this module +Please see our [developer documentation](https://github.com/aws-ia/terraform-aws-vpc/blob/main/contributing.md) for guidance on contributing to this module. diff --git a/README.md b/README.md index 7856122..1dd778b 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,46 @@ # AWS VPC Module -This module can be used to deploy a pragmatic VPC with various subnets types in # AZs. Common deployment examples can be found in [examples/](https://github.com/aws-ia/terraform-aws-vpc/tree/main/examples). Subnet CIDRs can be explicitly set via list of string argument `cidrs` or set via a number `netmask` argument. +This module can be used to deploy a pragmatic VPC with various subnets types in # AZs. Common deployment examples can be found in [examples/](https://github.com/aws-ia/terraform-aws-vpc/tree/main/examples). \_\_Note: For information regarding the 4.0 upgrade see our [upgrade guide](https://github.com/aws-ia/terraform-aws-vpc/blob/main/UPGRADE-GUIDE-4.0.md).\_\_ ## Usage -The example below builds a VPC with public and private subnets in 3 AZs. Each subnet calulates a CIDR based on the `netmask` argument passed. The public subnets build nat gateways in each AZ but optionally can be switched to `single_az`. +The example below builds a dual-stack VPC with public and private subnets in 3 AZs. Each subnet calculates an IPv4 CIDR based on the `netmask` argument passed, and an IPv6 CIDR with a /64 prefix length. The public subnets build NAT gateways in each AZ but optionally can be switched to `single_az`. An Egress-only Internet gateway is created by using the variable `vpc_egress_only_internet_gateway`. ```hcl module "vpc" { source = "aws-ia/vpc/aws" - version = ">= 4.0.0" + version = ">= 4.2.0" - name = "multi-az-vpc" - cidr_block = "10.0.0.0/20" - az_count = 3 + name = "multi-az-vpc" + cidr_block = "10.0.0.0/16" + vpc_assign_generated_ipv6_cidr_block = true + vpc_egress_only_internet_gateway = true + az_count = 3 subnets = { + # Dual-stack subnet public = { name_prefix = "my_public" # omit to prefix with "public" netmask = 24 + assign_ipv6_cidr = true nat_gateway_configuration = "all_azs" # options: "single_az", "none" } - + # IPv4 only subnet private = { # omitting name_prefix defaults value to "private" # name_prefix = "private_with_egress" netmask = 24 connect_to_public_natgw = true } + # IPv6-only subnet + private_ipv6 = { + ipv6_native = true + assign_ipv6_cidr = true + connect_to_eigw = true + } } vpc_flow_logs = { @@ -44,13 +54,7 @@ module "vpc" { There are 3 reserved keys for subnet key names in var.subnets corresponding to types "public", "transit\_gateway", and "core\_network" [(an AWS Cloud WAN feature)](https://docs.aws.amazon.com/vpc/latest/cloudwan/cloudwan-networks-working-with.html). Other custom subnet key names are valid are and those subnets will be private subnets. -```terraform -transit_gateway_id = <> -transit_gateway_routes = { - public = "pl-123" - vpce = "10.0.0.0/8" -} - +```hcl subnets = { public = { name_prefix = "my-public" # omit to prefix with "public" @@ -72,10 +76,28 @@ subnets = { # name_prefix = "private" netmask = 24 } +``` + +```hcl +transit_gateway_id = <> +transit_gateway_routes = { + private = "0.0.0.0/0" + vpce = "pl-123" +} +transit_gateway_ipv6_routes = { + private = "::/0" +} + +subnets = { + private = { + netmask = 24 + assign_ipv6_cidr = true + } + vpce = { netmask = 24} transit_gateway = { netmask = 28 - connect_to_public_natgw = true + assign_ipv6_cidr = true transit_gateway_default_route_table_association = true transit_gateway_default_route_table_propagation = true transit_gateway_appliance_mode_support = "enable" @@ -87,7 +109,7 @@ subnets = { } ``` -```terraform +```hcl core_network = { id = <> arn = <> @@ -95,16 +117,20 @@ core_network = { core_network_routes = { workload = "pl-123" } +core_network_ipv6_routes = { + workload = "::/0" +} subnets = { workload = { - name_prefix = "workload-private" - netmask = 24 + name_prefix = "workload-private" + netmask = 24 + assign_ipv6_cidr = true } core_network = { netmask = 28 - ipv6_support = false + assign_ipv6_cidr = true appliance_mode_support = false require_acceptance = true accept_attachment = true @@ -117,7 +143,7 @@ subnets = { ## Updating a VPC with new or removed subnets -If using `netmask` to calculate subnets and you wish to either add or remove subnets (ex: adding / removing an AZ), you may have to change from using `netmask` for some subnets and set to explicit instead. Private subnets are always calculated before public. +If using `netmask` or `assign_ipv6_cidr` to calculate subnets and you wish to either add or remove subnets (ex: adding / removing an AZ), you may have to change from using `netmask` / `assign_ipv6_cidr` for some subnets and set to explicit instead. Private subnets are always calculated before public. When changing to explicit cidrs, subnets are always ordered by AZ. `0` -> a, `1` -> b, etc. @@ -125,32 +151,38 @@ Example: Changing from 2 azs to 3 Before: ```hcl -cidr_block = "10.0.0.0/16" -az_count = 2 +cidr_block = "10.0.0.0/16" +vpc_assign_generated_ipv6_cidr_block = true +az_count = 2 subnets = { public = { - netmask = 24 + netmask = 24 + assign_ipv6_cidr = true } private = { - netmask = 24 + netmask = 24 + assign_ipv6_cidr = true } } ``` After: ```hcl -cidr_block = "10.0.0.0/16" +cidr_block = "10.0.0.0/16" +vpc_assign_generated_ipv6_cidr_block = true az_count = 3 subnets = { public = { - cidrs = ["10.0.0.0/24", "10.0.1.0/24", "10.0.4.0/24"] + cidrs = ["10.0.0.0/24", "10.0.1.0/24", "10.0.4.0/24"] + ipv6_cidrs = ["2a05:d01c:bc3:b200::/64", "2a05:d01c:bc3:b201::/64", "2a05:d01c:bc3:b204::/64"] } private = { - cidrs = ["10.0.2.0/24", "10.0.3.0/24", "10.0.5.0/24"] + cidrs = ["10.0.2.0/24", "10.0.3.0/24", "10.0.5.0/24"] + ipv6_cidrs = ["2a05:d01c:bc3:b202::/64", "2a05:d01c:bc3:b203::/64", "2a05:d01c:bc3:b205::/64"] } } ``` @@ -167,7 +199,7 @@ Example Configuration: ```terraform module "vpc" { source = "aws-ia/vpc/aws" - version = ">= 4.0.0" + version = ">= 4.2.0" name = "multi-az-vpc" cidr_block = "10.0.0.0/20" @@ -229,6 +261,7 @@ This happens when the Core Network's VPC attachment requires acceptance, so it's subnets = { core_network = { netmask = 28 + assign_ipv6_cidr = true require_acceptance = true accept_attachment = true } @@ -241,6 +274,7 @@ subnets = { subnets = { core_network = { netmask = 28 + assign_ipv6_cidr = true require_acceptance = true accept_attachment = true } @@ -253,6 +287,7 @@ After you apply and the attachment is accepted (outside the module), change the subnets = { core_network = { netmask = 28 + assign_ipv6_cidr = true require_acceptance = false } } @@ -262,7 +297,7 @@ subnets = { # Contributing -Please see our [developer documentation](https://github.com/aws-ia/terraform-aws-vpc/blob/main/contributing.md) for guidance on contributing to this module +Please see our [developer documentation](https://github.com/aws-ia/terraform-aws-vpc/blob/main/contributing.md) for guidance on contributing to this module. ## Requirements @@ -282,6 +317,7 @@ Please see our [developer documentation](https://github.com/aws-ia/terraform-aws | Name | Source | Version | |------|--------|---------| | [calculate\_subnets](#module\_calculate\_subnets) | ./modules/calculate_subnets | n/a | +| [calculate\_subnets\_ipv6](#module\_calculate\_subnets\_ipv6) | ./modules/calculate_subnets_ipv6 | n/a | | [flow\_logs](#module\_flow\_logs) | ./modules/flow_logs | n/a | | [subnet\_tags](#module\_subnet\_tags) | aws-ia/label/aws | 0.0.5 | | [tags](#module\_tags) | aws-ia/label/aws | 0.0.5 | @@ -290,17 +326,23 @@ Please see our [developer documentation](https://github.com/aws-ia/terraform-aws | Name | Type | |------|------| -| [aws_ec2_transit_gateway_route_table_association.tgw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_route_table_association) | resource | | [aws_ec2_transit_gateway_vpc_attachment.tgw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_vpc_attachment) | resource | +| [aws_egress_only_internet_gateway.eigw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/egress_only_internet_gateway) | resource | | [aws_eip.nat](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource | | [aws_internet_gateway.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/internet_gateway) | resource | | [aws_nat_gateway.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/nat_gateway) | resource | | [aws_networkmanager_attachment_accepter.cwan](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkmanager_attachment_accepter) | resource | | [aws_networkmanager_vpc_attachment.cwan](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkmanager_vpc_attachment) | resource | | [aws_route.cwan_to_nat](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | +| [aws_route.ipv6_private_to_cwan](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | +| [aws_route.ipv6_private_to_tgw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | +| [aws_route.ipv6_public_to_cwan](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | +| [aws_route.ipv6_public_to_tgw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | | [aws_route.private_to_cwan](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | +| [aws_route.private_to_egress_only](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | | [aws_route.private_to_nat](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | | [aws_route.private_to_tgw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | +| [aws_route.public_ipv6_to_igw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | | [aws_route.public_to_cwan](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | | [aws_route.public_to_igw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | | [aws_route.public_to_tgw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | @@ -328,22 +370,29 @@ Please see our [developer documentation](https://github.com/aws-ia/terraform-aws |------|-------------|------|---------|:--------:| | [az\_count](#input\_az\_count) | Searches region for # of AZs to use and takes a slice based on count. Assume slice is sorted a-z. | `number` | n/a | yes | | [name](#input\_name) | Name to give VPC. Note: does not effect subnet names, which get assigned name based on name\_prefix. | `string` | n/a | yes | -| [subnets](#input\_subnets) | Configuration of subnets to build in VPC. 1 Subnet per AZ is created. Subnet types are defined as maps with the available keys: "private", "public", "transit\_gateway". Each Subnet type offers its own set of available arguments detailed below.

**Attributes shared across subnet types:**
- `cidrs` = (Optional\|list(string)) **Cannot set if `netmask` is set.** List of CIDRs to set to subnets. Count of CIDRs defined must match quatity of azs in `az_count`.
- `netmask` = (Optional\|Int) Netmask of the `var.cidr_block` to calculate for each subnet. **Cannot set if `cidrs` is set.**
- `name_prefix` = (Optional\|String) A string prefix to use for the name of your subnet and associated resources. Subnet type key name is used if omitted (aka private, public, transit\_gateway). Example `name_prefix = "private"` for `var.subnets.private` is redundant.
- `tags` = (Optional\|map(string)) Tags to set on the subnet and associated resources.

**Any private subnet type options:**
- All shared keys above
- `connect_to_public_natgw` = (Optional\|string) Determines if routes to NAT Gateways should be created. Specify the CIDR range or a prefix-list-id that you want routed to nat gateway. Usually `0.0.0.0/0`. Must also set `var.subnets.public.nat_gateway_configuration`.

**public subnet type options:**
- All shared keys above
- `nat_gateway_configuration` = (Optional\|string) Determines if NAT Gateways should be created and in how many AZs. Valid values = `"none"`, `"single_az"`, `"all_azs"`. Default = "none". Must also set `var.subnets.private.connect_to_public_natgw = true`.

**transit\_gateway subnet type options:**
- All shared keys above
- `connect_to_public_natgw` = (Optional\|string) Determines if routes to NAT Gateways should be created. Specify the CIDR range or a prefix-list-id that you want routed to nat gateway. Usually `0.0.0.0/0`. Must also set `var.subnets.public.nat_gateway_configuration`.
- `transit_gateway_default_route_table_association` = (Optional\|bool) Boolean whether the VPC Attachment should be associated with the EC2 Transit Gateway association default route table. This cannot be configured or perform drift detection with Resource Access Manager shared EC2 Transit Gateways.
- `transit_gateway_default_route_table_propagation` = (Optional\|bool) Boolean whether the VPC Attachment should propagate routes with the EC2 Transit Gateway propagation default route table. This cannot be configured or perform drift detection with Resource Access Manager shared EC2 Transit Gateways.
- `transit_gateway_appliance_mode_support` = (Optional\|string) Whether Appliance Mode is enabled. If enabled, a traffic flow between a source and a destination uses the same Availability Zone for the VPC attachment for the lifetime of that flow. Valid values: `disable` (default) and `enable`.
- `transit_gateway_dns_support` = (Optional\|string) DNS Support is used if you need the VPC to resolve public IPv4 DNS host names to private IPv4 addresses when queried from instances in another VPC attached to the transit gateway. Valid values: `enable` (default) and `disable`.

**core\_network subnet type options:**
- All shared keys abovce
- `connect_to_public_natgw` = (Optional\|string) Determines if routes to NAT Gateways should be created. Specify the CIDR range or a prefix-list-id that you want routed to nat gateway. Usually `0.0.0.0/0`. Must also set `var.subnets.public.nat_gateway_configuration`.
- `ipv6_support` = (Optional\|bool) Boolean whether IPv6 is supported or not in the Cloud WAN's VPC attachment. Default to `false`.
- `appliance_mode_support` = (Optional\|bool) Indicates whether appliance mode is supported. If enabled, traffic flow between a source and destination use the same Availability Zone for the VPC attachment for the lifetime of that flow. Defaults to `false`.
- `require_acceptance` = (Optional\|bool) Boolean whether the core network VPC attachment to create requires acceptance or not. Defaults to `false`.
- `accept_attachment` = (Optional\|bool) Boolean whether the core network VPC attachment is accepted or not in the segment. Only valid if `require_acceptance` is set to `true`. Defaults to `true`.

Example:
subnets = {
public = {
netmask = 24
nat_gateway_configuration = "single_az"
}

private = {
netmask = 24
connect_to_public_natgw = true
}

transit_gateway = {
netmask = 24
connect_to_public_natgw = true
transit_gateway_default_route_table_association = true
transit_gateway_default_route_table_propagation = true
}

core_network = {
netmask = 24
connect_to_public_natgw = true
ipv6_support = true
appliance_mode_support = true
require_acceptance = true
accept_attachment = true
}
}
| `any` | n/a | yes | -| [cidr\_block](#input\_cidr\_block) | CIDR range to assign to VPC if creating VPC or to associte as a secondary CIDR. Overridden by var.vpc\_id output from data.aws\_vpc. | `string` | `null` | no | +| [subnets](#input\_subnets) | Configuration of subnets to build in VPC. 1 Subnet per AZ is created. Subnet types are defined as maps with the available keys: "private", "public", "transit\_gateway", "core\_network". Each Subnet type offers its own set of available arguments detailed below.

**Attributes shared across subnet types:**
- `cidrs` = (Optional\|list(string)) **Cannot set if `netmask` is set.** List of IPv4 CIDRs to set to subnets. Count of CIDRs defined must match quantity of azs in `az_count`.
- `netmask` = (Optional\|Int) **Cannot set if `cidrs` is set.** Netmask of the `var.cidr_block` to calculate for each subnet.
- `assign_ipv6_cidr` = (Optional\|bool) **Cannot set if `ipv6_cidrs` is set.** If true, it will calculate a /64 block from the IPv6 VPC CIDR to set in the subnets.
- `ipv6_cidrs` = (Optional\|list(string)) **Cannot set if `assign_ipv6_cidr` is set.** List of IPv6 CIDRs to set to subnets. The subnet size must use a /64 prefix length. Count of CIDRs defined must match quantity of azs in `az_count`.
- `name_prefix` = (Optional\|String) A string prefix to use for the name of your subnet and associated resources. Subnet type key name is used if omitted (aka private, public, transit\_gateway). Example `name_prefix = "private"` for `var.subnets.private` is redundant.
- `tags` = (Optional\|map(string)) Tags to set on the subnet and associated resources.

**Any private subnet type options:**
- All shared keys above
- `connect_to_public_natgw` = (Optional\|bool) Determines if routes to NAT Gateways should be created. Must also set `var.subnets.public.nat_gateway_configuration` in public subnets.
- `ipv6_native` = (Optional\|bool) Indicates whether to create an IPv6-ony subnet. Either `var.assign_ipv6_cidr` or `var.ipv6_cidrs` should be defined to allocate an IPv6 CIDR block.
- `connect_to_eigw` = (Optional\|bool) Determines if routes to the Egress-only Internet gateway should be created. Must also set `var.vpc_egress_only_internet_gateway`.

**public subnet type options:**
- All shared keys above
- `nat_gateway_configuration` = (Optional\|string) Determines if NAT Gateways should be created and in how many AZs. Valid values = `"none"`, `"single_az"`, `"all_azs"`. Default = "none". Must also set `var.subnets.private.connect_to_public_natgw = true`.
- `ipv6_native` = (Optional\|bool) Indicates whether to create an IPv6-ony subnet. Either `var.assign_ipv6_cidr` or `var.ipv6_cidrs` should be defined to allocate an IPv6 CIDR block.

**transit\_gateway subnet type options:**
- All shared keys above
- `connect_to_public_natgw` = (Optional\|string) Determines if routes to NAT Gateways should be created. Specify the CIDR range or a prefix-list-id that you want routed to nat gateway. Usually `0.0.0.0/0`. Must also set `var.subnets.public.nat_gateway_configuration`.
- `transit_gateway_default_route_table_association` = (Optional\|bool) Boolean whether the VPC Attachment should be associated with the EC2 Transit Gateway association default route table. This cannot be configured or perform drift detection with Resource Access Manager shared EC2 Transit Gateways.
- `transit_gateway_default_route_table_propagation` = (Optional\|bool) Boolean whether the VPC Attachment should propagate routes with the EC2 Transit Gateway propagation default route table. This cannot be configured or perform drift detection with Resource Access Manager shared EC2 Transit Gateways.
- `transit_gateway_appliance_mode_support` = (Optional\|string) Whether Appliance Mode is enabled. If enabled, a traffic flow between a source and a destination uses the same Availability Zone for the VPC attachment for the lifetime of that flow. Valid values: `disable` (default) and `enable`.
- `transit_gateway_dns_support` = (Optional\|string) DNS Support is used if you need the VPC to resolve public IPv4 DNS host names to private IPv4 addresses when queried from instances in another VPC attached to the transit gateway. Valid values: `enable` (default) and `disable`.

**core\_network subnet type options:**
- All shared keys abovce
- `connect_to_public_natgw` = (Optional\|string) Determines if routes to NAT Gateways should be created. Specify the CIDR range or a prefix-list-id that you want routed to nat gateway. Usually `0.0.0.0/0`. Must also set `var.subnets.public.nat_gateway_configuration`.
- `appliance_mode_support` = (Optional\|bool) Indicates whether appliance mode is supported. If enabled, traffic flow between a source and destination use the same Availability Zone for the VPC attachment for the lifetime of that flow. Defaults to `false`.
- `require_acceptance` = (Optional\|bool) Boolean whether the core network VPC attachment to create requires acceptance or not. Defaults to `false`.
- `accept_attachment` = (Optional\|bool) Boolean whether the core network VPC attachment is accepted or not in the segment. Only valid if `require_acceptance` is set to `true`. Defaults to `true`.

Example:
subnets = {
# Dual-stack subnet
public = {
netmask = 24
assign_ipv6_cidr = true
nat_gateway_configuration = "single_az"
}
# IPv4 only subnet
private = {
netmask = 24
connect_to_public_natgw = true
}
# IPv6 only subnet
ipv6 = {
ipv6_native = true
assign_ipv6_cidr = true
connect_to_eigw = true
}
# Transit gateway subnets (dual-stack)
transit_gateway = {
netmask = 24
assign_ipv6_cidr = true
connect_to_public_natgw = true
transit_gateway_default_route_table_association = true
transit_gateway_default_route_table_propagation = true
}
# Core Network subnets (dual-stack)
core_network = {
netmask = 24
assign_ipv6_cidr = true
connect_to_public_natgw = true
appliance_mode_support = true
require_acceptance = true
accept_attachment = true
}
}
| `any` | n/a | yes | +| [cidr\_block](#input\_cidr\_block) | IPv4 CIDR range to assign to VPC if creating VPC or to associte as a secondary IPv6 CIDR. Overridden by var.vpc\_id output from data.aws\_vpc. | `string` | `null` | no | | [core\_network](#input\_core\_network) | AWS Cloud WAN's core network information - to create a VPC attachment. Required when `cloud_wan` subnet is defined. Two attributes are required: the `id` and `arn` of the resource. |
object({
id = string
arn = string
})
|
{
"arn": null,
"id": null
}
| no | +| [core\_network\_ipv6\_routes](#input\_core\_network\_ipv6\_routes) | Configuration of IPv6 route(s) to AWS Cloud WAN's core network.
For each `public` and/or `private` subnets named in the `subnets` variable, optionally create routes from the subnet to the core network.
You can specify either a CIDR range or a prefix-list-id that you want routed to the core network.
Example:
core_network_routes = {
public = "::/0"
private = "pl-123"
}
| `any` | `{}` | no | | [core\_network\_routes](#input\_core\_network\_routes) | Configuration of route(s) to AWS Cloud WAN's core network.
For each `public` and/or `private` subnets named in the `subnets` variable, optionally create routes from the subnet to the core network.
You can specify either a CIDR range or a prefix-list-id that you want routed to the core network.
Example:
core_network_routes = {
public = "10.0.0.0/8"
private = "pl-123"
}
| `any` | `{}` | no | | [tags](#input\_tags) | Tags to apply to all resources. | `map(string)` | `{}` | no | | [transit\_gateway\_id](#input\_transit\_gateway\_id) | Transit gateway id to attach the VPC to. Required when `transit_gateway` subnet is defined. | `string` | `null` | no | +| [transit\_gateway\_ipv6\_routes](#input\_transit\_gateway\_ipv6\_routes) | Configuration of IPv6 route(s) to transit gateway.
For each `public` and/or `private` subnets named in the `subnets` variable,
Optionally create routes from the subnet to transit gateway. Specify the CIDR range or a prefix-list-id that you want routed to the transit gateway.
Example:
transit_gateway_routes = {
public = "::/0"
private = "pl-123"
}
| `any` | `{}` | no | | [transit\_gateway\_routes](#input\_transit\_gateway\_routes) | Configuration of route(s) to transit gateway.
For each `public` and/or `private` subnets named in the `subnets` variable,
Optionally create routes from the subnet to transit gateway. Specify the CIDR range or a prefix-list-id that you want routed to the transit gateway.
Example:
transit_gateway_routes = {
public = "10.0.0.0/8"
private = "pl-123"
}
| `any` | `{}` | no | +| [vpc\_assign\_generated\_ipv6\_cidr\_block](#input\_vpc\_assign\_generated\_ipv6\_cidr\_block) | Requests and Amazon-provided IPv6 CIDR block with a /56 prefix length. You cannot specify the range of IP addresses, or the size of the CIDR block. Conflics with `vpc_ipv6_ipam_pool_id`. | `bool` | `null` | no | +| [vpc\_egress\_only\_internet\_gateway](#input\_vpc\_egress\_only\_internet\_gateway) | Set to use the Egress-only Internet gateway for all IPv6 traffic going to the Internet. | `bool` | `false` | no | | [vpc\_enable\_dns\_hostnames](#input\_vpc\_enable\_dns\_hostnames) | Indicates whether the instances launched in the VPC get DNS hostnames. If enabled, instances in the VPC get DNS hostnames; otherwise, they do not. Disabled by default for nondefault VPCs. | `bool` | `true` | no | | [vpc\_enable\_dns\_support](#input\_vpc\_enable\_dns\_support) | Indicates whether the DNS resolution is supported for the VPC. If enabled, queries to the Amazon provided DNS server at the 169.254.169.253 IP address, or the reserved IP address at the base of the VPC network range "plus two" succeed. If disabled, the Amazon provided DNS service in the VPC that resolves public DNS hostnames to IP addresses is not enabled. Enabled by default. | `bool` | `true` | no | | [vpc\_flow\_logs](#input\_vpc\_flow\_logs) | Whether or not to create VPC flow logs and which type. Options: "cloudwatch", "s3", "none". By default creates flow logs to `cloudwatch`. Variable overrides null value types for some keys, defined in defaults.tf. |
object({
log_destination = optional(string)
iam_role_arn = optional(string)
kms_key_id = optional(string)

log_destination_type = string
retention_in_days = optional(number)
tags = optional(map(string))
traffic_type = optional(string, "ALL")
destination_options = optional(object({
file_format = optional(string, "plain-text")
hive_compatible_partitions = optional(bool, false)
per_hour_partition = optional(bool, false)
}))
})
|
{
"log_destination_type": "none"
}
| no | | [vpc\_id](#input\_vpc\_id) | VPC ID to use if not creating VPC. | `string` | `null` | no | | [vpc\_instance\_tenancy](#input\_vpc\_instance\_tenancy) | The allowed tenancy of instances launched into the VPC. | `string` | `"default"` | no | -| [vpc\_ipv4\_ipam\_pool\_id](#input\_vpc\_ipv4\_ipam\_pool\_id) | Set to use IPAM to get CIDR block. | `string` | `null` | no | -| [vpc\_ipv4\_netmask\_length](#input\_vpc\_ipv4\_netmask\_length) | Set to use IPAM to get CIDR block using a specified netmask. Must be set with var.vpc\_ipv4\_ipam\_pool\_id. | `string` | `null` | no | +| [vpc\_ipv4\_ipam\_pool\_id](#input\_vpc\_ipv4\_ipam\_pool\_id) | Set to use IPAM to get an IPv4 CIDR block. | `string` | `null` | no | +| [vpc\_ipv4\_netmask\_length](#input\_vpc\_ipv4\_netmask\_length) | Set to use IPAM to get an IPv4 CIDR block using a specified netmask. Must be set with var.vpc\_ipv4\_ipam\_pool\_id. | `string` | `null` | no | +| [vpc\_ipv6\_cidr\_block](#input\_vpc\_ipv6\_cidr\_block) | IPv6 CIDR range to assign to VPC if creating VPC or to associate as a secondary IPv6 CIDR. Can be set explicitly or derived from IPAM using `vpc_ipv6_ipam_pool_id`. Overridden by `var.vpc_id` output from `data.aws_vpc`. | `string` | `null` | no | +| [vpc\_ipv6\_ipam\_pool\_id](#input\_vpc\_ipv6\_ipam\_pool\_id) | Set to use IPAM to get an IPv6 CIDR block. | `string` | `null` | no | +| [vpc\_ipv6\_netmask\_length](#input\_vpc\_ipv6\_netmask\_length) | Set to use IPAM to get an IPv6 CIDR block using a specified netmask. Must be set with `var.vpc_ipv6_ipam_pool_id`. | `string` | `null` | no | | [vpc\_secondary\_cidr](#input\_vpc\_secondary\_cidr) | If `true` the module will create a `aws_vpc_ipv4_cidr_block_association` and subnets for that secondary cidr. If using IPAM for both primary and secondary CIDRs, you may only call this module serially (aka using `-target`, etc). | `bool` | `false` | no | -| [vpc\_secondary\_cidr\_natgw](#input\_vpc\_secondary\_cidr\_natgw) | If attaching a secondary CIDR instead of creating a VPC, you can map private/ tgw subnets to your public NAT GW with this argument. Simply pass the output `nat_gateway_attributes_by_az`, ex: `vpc_secondary_cidr_natgw = module.vpc.natgw_id_per_az`. If you did not build your primary with this module, you must construct a map { az : { id : nat-123asdb }} for each az. | `any` | `{}` | no | +| [vpc\_secondary\_cidr\_natgw](#input\_vpc\_secondary\_cidr\_natgw) | If attaching a secondary IPv4 CIDR instead of creating a VPC, you can map private/ tgw subnets to your public NAT GW with this argument. Simply pass the output `nat_gateway_attributes_by_az`, ex: `vpc_secondary_cidr_natgw = module.vpc.natgw_id_per_az`. If you did not build your primary with this module, you must construct a map { az : { id : nat-123asdb }} for each az. | `any` | `{}` | no | ## Outputs @@ -352,6 +401,7 @@ Please see our [developer documentation](https://github.com/aws-ia/terraform-aws | [azs](#output\_azs) | List of AZs where subnets are created. | | [core\_network\_attachment](#output\_core\_network\_attachment) | AWS Cloud WAN's core network attachment. Full output of aws\_networkmanager\_vpc\_attachment. | | [core\_network\_subnet\_attributes\_by\_az](#output\_core\_network\_subnet\_attributes\_by\_az) | Map of all core\_network subnets containing their attributes.

Example:
core_network_subnet_attributes = {
"us-east-1a" = {
"arn" = "arn:aws:ec2:us-east-1:<>:subnet/subnet-04a86315c4839b519"
"assign_ipv6_address_on_creation" = false
...

}
"us-east-1b" = {...)
}
| +| [egress\_only\_internet\_gateway](#output\_egress\_only\_internet\_gateway) | Egress-only Internet gateway attributes. Full output of aws\_egress\_only\_internet\_gateway. | | [nat\_gateway\_attributes\_by\_az](#output\_nat\_gateway\_attributes\_by\_az) | Map of nat gateway resource attributes by AZ.

Example:
nat_gateway_attributes_by_az = {
"us-east-1a" = {
"allocation_id" = "eipalloc-0e8b20303eea88b13"
"connectivity_type" = "public"
"id" = "nat-0fde39f9550f4abb5"
"network_interface_id" = "eni-0d422727088bf9a86"
"private_ip" = "10.0.3.40"
"public_ip" = <>
"subnet_id" = "subnet-0f11c92e439c8ab4a"
"tags" = tomap({
"Name" = "nat-my-public-us-east-1a"
})
"tags_all" = tomap({
"Name" = "nat-my-public-us-east-1a"
})
}
"us-east-1b" = { ... }
}
| | [natgw\_id\_per\_az](#output\_natgw\_id\_per\_az) | Map of nat gateway IDs for each resource. Will be duplicate ids if your var.subnets.public.nat\_gateway\_configuration = "single\_az".

Example:
natgw_id_per_az = {
"us-east-1a" = {
"id" = "nat-0fde39f9550f4abb5"
}
"us-east-1b" = {
"id" = "nat-0fde39f9550f4abb5"
}
}
| | [private\_subnet\_attributes\_by\_az](#output\_private\_subnet\_attributes\_by\_az) | Map of all private subnets containing their attributes.

Example:
private_subnet_attributes = {
"private/us-east-1a" = {
"arn" = "arn:aws:ec2:us-east-1:<>:subnet/subnet-04a86315c4839b519"
"assign_ipv6_address_on_creation" = false
...

}
"us-east-1b" = {...)
}
| diff --git a/main.tf b/main.tf index 2685ad0..e18105f 100644 --- a/main.tf +++ b/main.tf @@ -24,9 +24,6 @@ resource "aws_vpc" "main" { count = local.create_vpc ? 1 : 0 cidr_block = var.cidr_block - enable_dns_hostnames = var.vpc_enable_dns_hostnames - enable_dns_support = var.vpc_enable_dns_support - instance_tenancy = var.vpc_instance_tenancy ipv4_ipam_pool_id = var.vpc_ipv4_ipam_pool_id ipv4_netmask_length = var.vpc_ipv4_netmask_length assign_generated_ipv6_cidr_block = var.vpc_assign_generated_ipv6_cidr_block @@ -34,6 +31,10 @@ resource "aws_vpc" "main" { ipv6_ipam_pool_id = var.vpc_ipv6_ipam_pool_id ipv6_netmask_length = var.vpc_ipv6_netmask_length + enable_dns_hostnames = var.vpc_enable_dns_hostnames + enable_dns_support = var.vpc_enable_dns_support + instance_tenancy = var.vpc_instance_tenancy + tags = merge( { "Name" = var.name }, module.tags.tags_aws @@ -283,7 +284,7 @@ resource "aws_route" "private_to_egress_only" { for_each = toset(try(local.private_subnet_names_egress_routed, [])) route_table_id = aws_route_table.private[each.key].id - destination_ipv6_cidr_block = "0::/0" + destination_ipv6_cidr_block = "::/0" egress_only_gateway_id = aws_egress_only_internet_gateway.eigw[0].id } diff --git a/outputs.tf b/outputs.tf index f38e289..046a2a2 100644 --- a/outputs.tf +++ b/outputs.tf @@ -177,3 +177,8 @@ output "natgw_id_per_az" { ``` EOF } + +output "egress_only_internet_gateway" { + value = try(aws_egress_only_internet_gateway.eigw, null) + description = "Egress-only Internet gateway attributes. Full output of aws_egress_only_internet_gateway." +} diff --git a/variables.tf b/variables.tf index ecc84af..739a4df 100644 --- a/variables.tf +++ b/variables.tf @@ -4,7 +4,7 @@ variable "name" { } variable "cidr_block" { - description = "CIDR range to assign to VPC if creating VPC or to associte as a secondary CIDR. Overridden by var.vpc_id output from data.aws_vpc." + description = "IPv4 CIDR range to assign to VPC if creating VPC or to associte as a secondary IPv6 CIDR. Overridden by var.vpc_id output from data.aws_vpc." default = null type = string } @@ -34,7 +34,7 @@ variable "vpc_secondary_cidr" { variable "vpc_secondary_cidr_natgw" { type = any - description = "If attaching a secondary CIDR instead of creating a VPC, you can map private/ tgw subnets to your public NAT GW with this argument. Simply pass the output `nat_gateway_attributes_by_az`, ex: `vpc_secondary_cidr_natgw = module.vpc.natgw_id_per_az`. If you did not build your primary with this module, you must construct a map { az : { id : nat-123asdb }} for each az." + description = "If attaching a secondary IPv4 CIDR instead of creating a VPC, you can map private/ tgw subnets to your public NAT GW with this argument. Simply pass the output `nat_gateway_attributes_by_az`, ex: `vpc_secondary_cidr_natgw = module.vpc.natgw_id_per_az`. If you did not build your primary with this module, you must construct a map { az : { id : nat-123asdb }} for each az." default = {} } @@ -61,65 +61,69 @@ variable "vpc_instance_tenancy" { } variable "vpc_ipv4_ipam_pool_id" { - description = "Set to use IPAM to get CIDR block." + description = "Set to use IPAM to get an IPv4 CIDR block." type = string default = null } variable "vpc_ipv4_netmask_length" { - description = "Set to use IPAM to get CIDR block using a specified netmask. Must be set with var.vpc_ipv4_ipam_pool_id." + description = "Set to use IPAM to get an IPv4 CIDR block using a specified netmask. Must be set with var.vpc_ipv4_ipam_pool_id." type = string default = null } variable "vpc_assign_generated_ipv6_cidr_block" { - description = "Whether the VPC has IPv6-generated CIDR block." + description = "Requests and Amazon-provided IPv6 CIDR block with a /56 prefix length. You cannot specify the range of IP addresses, or the size of the CIDR block. Conflics with `vpc_ipv6_ipam_pool_id`." type = bool default = null } variable "vpc_ipv6_ipam_pool_id" { - description = "Set to use IPAM to get CIDR IPV6 block." + description = "Set to use IPAM to get an IPv6 CIDR block." type = string default = null } variable "vpc_ipv6_cidr_block" { - description = "CIDR range to assign to VPC if creating VPC or to associte as a secondary CIDR. Overridden by var.vpc_id output from data.aws_vpc." + description = "IPv6 CIDR range to assign to VPC if creating VPC or to associate as a secondary IPv6 CIDR. Can be set explicitly or derived from IPAM using `vpc_ipv6_ipam_pool_id`. Overridden by `var.vpc_id` output from `data.aws_vpc`." type = string default = null } variable "vpc_ipv6_netmask_length" { - description = "Set to use IPAM to get CIDR block using a specified netmask. Must be set with var.vpc_ipv6_ipam_pool_id." + description = "Set to use IPAM to get an IPv6 CIDR block using a specified netmask. Must be set with `var.vpc_ipv6_ipam_pool_id`." type = string default = null } variable "vpc_egress_only_internet_gateway" { - description = "Set to use the egress only gateway for all traffic Ipv6 going to the Internet." + description = "Set to use the Egress-only Internet gateway for all IPv6 traffic going to the Internet." type = bool default = false } variable "subnets" { description = <<-EOF - Configuration of subnets to build in VPC. 1 Subnet per AZ is created. Subnet types are defined as maps with the available keys: "private", "public", "transit_gateway". Each Subnet type offers its own set of available arguments detailed below. + Configuration of subnets to build in VPC. 1 Subnet per AZ is created. Subnet types are defined as maps with the available keys: "private", "public", "transit_gateway", "core_network". Each Subnet type offers its own set of available arguments detailed below. **Attributes shared across subnet types:** - - `cidrs` = (Optional|list(string)) **Cannot set if `netmask` is set.** List of CIDRs to set to subnets. Count of CIDRs defined must match quatity of azs in `az_count`. - - `netmask` = (Optional|Int) Netmask of the `var.cidr_block` to calculate for each subnet. **Cannot set if `cidrs` is set.** - - `assign_ipv6_cidr` = (Optional|bool) - - `name_prefix` = (Optional|String) A string prefix to use for the name of your subnet and associated resources. Subnet type key name is used if omitted (aka private, public, transit_gateway). Example `name_prefix = "private"` for `var.subnets.private` is redundant. - - `tags` = (Optional|map(string)) Tags to set on the subnet and associated resources. + - `cidrs` = (Optional|list(string)) **Cannot set if `netmask` is set.** List of IPv4 CIDRs to set to subnets. Count of CIDRs defined must match quantity of azs in `az_count`. + - `netmask` = (Optional|Int) **Cannot set if `cidrs` is set.** Netmask of the `var.cidr_block` to calculate for each subnet. + - `assign_ipv6_cidr` = (Optional|bool) **Cannot set if `ipv6_cidrs` is set.** If true, it will calculate a /64 block from the IPv6 VPC CIDR to set in the subnets. + - `ipv6_cidrs` = (Optional|list(string)) **Cannot set if `assign_ipv6_cidr` is set.** List of IPv6 CIDRs to set to subnets. The subnet size must use a /64 prefix length. Count of CIDRs defined must match quantity of azs in `az_count`. + - `name_prefix` = (Optional|String) A string prefix to use for the name of your subnet and associated resources. Subnet type key name is used if omitted (aka private, public, transit_gateway). Example `name_prefix = "private"` for `var.subnets.private` is redundant. + - `tags` = (Optional|map(string)) Tags to set on the subnet and associated resources. **Any private subnet type options:** - All shared keys above - - `connect_to_public_natgw` = (Optional|string) Determines if routes to NAT Gateways should be created. Specify the CIDR range or a prefix-list-id that you want routed to nat gateway. Usually `0.0.0.0/0`. Must also set `var.subnets.public.nat_gateway_configuration`. + - `connect_to_public_natgw` = (Optional|bool) Determines if routes to NAT Gateways should be created. Must also set `var.subnets.public.nat_gateway_configuration` in public subnets. + - `ipv6_native` = (Optional|bool) Indicates whether to create an IPv6-ony subnet. Either `var.assign_ipv6_cidr` or `var.ipv6_cidrs` should be defined to allocate an IPv6 CIDR block. + - `connect_to_eigw` = (Optional|bool) Determines if routes to the Egress-only Internet gateway should be created. Must also set `var.vpc_egress_only_internet_gateway`. **public subnet type options:** - All shared keys above - `nat_gateway_configuration` = (Optional|string) Determines if NAT Gateways should be created and in how many AZs. Valid values = `"none"`, `"single_az"`, `"all_azs"`. Default = "none". Must also set `var.subnets.private.connect_to_public_natgw = true`. + - `ipv6_native` = (Optional|bool) Indicates whether to create an IPv6-ony subnet. Either `var.assign_ipv6_cidr` or `var.ipv6_cidrs` should be defined to allocate an IPv6 CIDR block. **transit_gateway subnet type options:** - All shared keys above @@ -132,7 +136,6 @@ variable "subnets" { **core_network subnet type options:** - All shared keys abovce - `connect_to_public_natgw` = (Optional|string) Determines if routes to NAT Gateways should be created. Specify the CIDR range or a prefix-list-id that you want routed to nat gateway. Usually `0.0.0.0/0`. Must also set `var.subnets.public.nat_gateway_configuration`. - - `ipv6_support` = (Optional|bool) Boolean whether IPv6 is supported or not in the Cloud WAN's VPC attachment. Default to `false`. - `appliance_mode_support` = (Optional|bool) Indicates whether appliance mode is supported. If enabled, traffic flow between a source and destination use the same Availability Zone for the VPC attachment for the lifetime of that flow. Defaults to `false`. - `require_acceptance` = (Optional|bool) Boolean whether the core network VPC attachment to create requires acceptance or not. Defaults to `false`. - `accept_attachment` = (Optional|bool) Boolean whether the core network VPC attachment is accepted or not in the segment. Only valid if `require_acceptance` is set to `true`. Defaults to `true`. @@ -140,27 +143,36 @@ variable "subnets" { Example: ``` subnets = { + # Dual-stack subnet public = { netmask = 24 + assign_ipv6_cidr = true nat_gateway_configuration = "single_az" } - + # IPv4 only subnet private = { netmask = 24 connect_to_public_natgw = true } - + # IPv6 only subnet + ipv6 = { + ipv6_native = true + assign_ipv6_cidr = true + connect_to_eigw = true + } + # Transit gateway subnets (dual-stack) transit_gateway = { netmask = 24 + assign_ipv6_cidr = true connect_to_public_natgw = true transit_gateway_default_route_table_association = true transit_gateway_default_route_table_propagation = true } - + # Core Network subnets (dual-stack) core_network = { netmask = 24 + assign_ipv6_cidr = true connect_to_public_natgw = true - ipv6_support = true appliance_mode_support = true require_acceptance = true accept_attachment = true From 119391e2892fb4ae6c00d2920395b36966077bfa Mon Sep 17 00:00:00 2001 From: Pablo Sanchez Carmona Date: Sat, 8 Apr 2023 11:02:59 +0200 Subject: [PATCH 5/6] minor update README --- README.md | 6 +++--- variables.tf | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1dd778b..5350ef7 100644 --- a/README.md +++ b/README.md @@ -373,11 +373,11 @@ Please see our [developer documentation](https://github.com/aws-ia/terraform-aws | [subnets](#input\_subnets) | Configuration of subnets to build in VPC. 1 Subnet per AZ is created. Subnet types are defined as maps with the available keys: "private", "public", "transit\_gateway", "core\_network". Each Subnet type offers its own set of available arguments detailed below.

**Attributes shared across subnet types:**
- `cidrs` = (Optional\|list(string)) **Cannot set if `netmask` is set.** List of IPv4 CIDRs to set to subnets. Count of CIDRs defined must match quantity of azs in `az_count`.
- `netmask` = (Optional\|Int) **Cannot set if `cidrs` is set.** Netmask of the `var.cidr_block` to calculate for each subnet.
- `assign_ipv6_cidr` = (Optional\|bool) **Cannot set if `ipv6_cidrs` is set.** If true, it will calculate a /64 block from the IPv6 VPC CIDR to set in the subnets.
- `ipv6_cidrs` = (Optional\|list(string)) **Cannot set if `assign_ipv6_cidr` is set.** List of IPv6 CIDRs to set to subnets. The subnet size must use a /64 prefix length. Count of CIDRs defined must match quantity of azs in `az_count`.
- `name_prefix` = (Optional\|String) A string prefix to use for the name of your subnet and associated resources. Subnet type key name is used if omitted (aka private, public, transit\_gateway). Example `name_prefix = "private"` for `var.subnets.private` is redundant.
- `tags` = (Optional\|map(string)) Tags to set on the subnet and associated resources.

**Any private subnet type options:**
- All shared keys above
- `connect_to_public_natgw` = (Optional\|bool) Determines if routes to NAT Gateways should be created. Must also set `var.subnets.public.nat_gateway_configuration` in public subnets.
- `ipv6_native` = (Optional\|bool) Indicates whether to create an IPv6-ony subnet. Either `var.assign_ipv6_cidr` or `var.ipv6_cidrs` should be defined to allocate an IPv6 CIDR block.
- `connect_to_eigw` = (Optional\|bool) Determines if routes to the Egress-only Internet gateway should be created. Must also set `var.vpc_egress_only_internet_gateway`.

**public subnet type options:**
- All shared keys above
- `nat_gateway_configuration` = (Optional\|string) Determines if NAT Gateways should be created and in how many AZs. Valid values = `"none"`, `"single_az"`, `"all_azs"`. Default = "none". Must also set `var.subnets.private.connect_to_public_natgw = true`.
- `ipv6_native` = (Optional\|bool) Indicates whether to create an IPv6-ony subnet. Either `var.assign_ipv6_cidr` or `var.ipv6_cidrs` should be defined to allocate an IPv6 CIDR block.

**transit\_gateway subnet type options:**
- All shared keys above
- `connect_to_public_natgw` = (Optional\|string) Determines if routes to NAT Gateways should be created. Specify the CIDR range or a prefix-list-id that you want routed to nat gateway. Usually `0.0.0.0/0`. Must also set `var.subnets.public.nat_gateway_configuration`.
- `transit_gateway_default_route_table_association` = (Optional\|bool) Boolean whether the VPC Attachment should be associated with the EC2 Transit Gateway association default route table. This cannot be configured or perform drift detection with Resource Access Manager shared EC2 Transit Gateways.
- `transit_gateway_default_route_table_propagation` = (Optional\|bool) Boolean whether the VPC Attachment should propagate routes with the EC2 Transit Gateway propagation default route table. This cannot be configured or perform drift detection with Resource Access Manager shared EC2 Transit Gateways.
- `transit_gateway_appliance_mode_support` = (Optional\|string) Whether Appliance Mode is enabled. If enabled, a traffic flow between a source and a destination uses the same Availability Zone for the VPC attachment for the lifetime of that flow. Valid values: `disable` (default) and `enable`.
- `transit_gateway_dns_support` = (Optional\|string) DNS Support is used if you need the VPC to resolve public IPv4 DNS host names to private IPv4 addresses when queried from instances in another VPC attached to the transit gateway. Valid values: `enable` (default) and `disable`.

**core\_network subnet type options:**
- All shared keys abovce
- `connect_to_public_natgw` = (Optional\|string) Determines if routes to NAT Gateways should be created. Specify the CIDR range or a prefix-list-id that you want routed to nat gateway. Usually `0.0.0.0/0`. Must also set `var.subnets.public.nat_gateway_configuration`.
- `appliance_mode_support` = (Optional\|bool) Indicates whether appliance mode is supported. If enabled, traffic flow between a source and destination use the same Availability Zone for the VPC attachment for the lifetime of that flow. Defaults to `false`.
- `require_acceptance` = (Optional\|bool) Boolean whether the core network VPC attachment to create requires acceptance or not. Defaults to `false`.
- `accept_attachment` = (Optional\|bool) Boolean whether the core network VPC attachment is accepted or not in the segment. Only valid if `require_acceptance` is set to `true`. Defaults to `true`.

Example:
subnets = {
# Dual-stack subnet
public = {
netmask = 24
assign_ipv6_cidr = true
nat_gateway_configuration = "single_az"
}
# IPv4 only subnet
private = {
netmask = 24
connect_to_public_natgw = true
}
# IPv6 only subnet
ipv6 = {
ipv6_native = true
assign_ipv6_cidr = true
connect_to_eigw = true
}
# Transit gateway subnets (dual-stack)
transit_gateway = {
netmask = 24
assign_ipv6_cidr = true
connect_to_public_natgw = true
transit_gateway_default_route_table_association = true
transit_gateway_default_route_table_propagation = true
}
# Core Network subnets (dual-stack)
core_network = {
netmask = 24
assign_ipv6_cidr = true
connect_to_public_natgw = true
appliance_mode_support = true
require_acceptance = true
accept_attachment = true
}
}
| `any` | n/a | yes | | [cidr\_block](#input\_cidr\_block) | IPv4 CIDR range to assign to VPC if creating VPC or to associte as a secondary IPv6 CIDR. Overridden by var.vpc\_id output from data.aws\_vpc. | `string` | `null` | no | | [core\_network](#input\_core\_network) | AWS Cloud WAN's core network information - to create a VPC attachment. Required when `cloud_wan` subnet is defined. Two attributes are required: the `id` and `arn` of the resource. |
object({
id = string
arn = string
})
|
{
"arn": null,
"id": null
}
| no | -| [core\_network\_ipv6\_routes](#input\_core\_network\_ipv6\_routes) | Configuration of IPv6 route(s) to AWS Cloud WAN's core network.
For each `public` and/or `private` subnets named in the `subnets` variable, optionally create routes from the subnet to the core network.
You can specify either a CIDR range or a prefix-list-id that you want routed to the core network.
Example:
core_network_routes = {
public = "::/0"
private = "pl-123"
}
| `any` | `{}` | no | +| [core\_network\_ipv6\_routes](#input\_core\_network\_ipv6\_routes) | Configuration of IPv6 route(s) to AWS Cloud WAN's core network.
For each `public` and/or `private` subnets named in the `subnets` variable, optionally create routes from the subnet to the core network.
You can specify either a CIDR range or a prefix-list-id that you want routed to the core network.
Example:
core_network_ivp6_routes = {
public = "::/0"
private = "pl-123"
}
| `any` | `{}` | no | | [core\_network\_routes](#input\_core\_network\_routes) | Configuration of route(s) to AWS Cloud WAN's core network.
For each `public` and/or `private` subnets named in the `subnets` variable, optionally create routes from the subnet to the core network.
You can specify either a CIDR range or a prefix-list-id that you want routed to the core network.
Example:
core_network_routes = {
public = "10.0.0.0/8"
private = "pl-123"
}
| `any` | `{}` | no | | [tags](#input\_tags) | Tags to apply to all resources. | `map(string)` | `{}` | no | | [transit\_gateway\_id](#input\_transit\_gateway\_id) | Transit gateway id to attach the VPC to. Required when `transit_gateway` subnet is defined. | `string` | `null` | no | -| [transit\_gateway\_ipv6\_routes](#input\_transit\_gateway\_ipv6\_routes) | Configuration of IPv6 route(s) to transit gateway.
For each `public` and/or `private` subnets named in the `subnets` variable,
Optionally create routes from the subnet to transit gateway. Specify the CIDR range or a prefix-list-id that you want routed to the transit gateway.
Example:
transit_gateway_routes = {
public = "::/0"
private = "pl-123"
}
| `any` | `{}` | no | +| [transit\_gateway\_ipv6\_routes](#input\_transit\_gateway\_ipv6\_routes) | Configuration of IPv6 route(s) to transit gateway.
For each `public` and/or `private` subnets named in the `subnets` variable,
Optionally create routes from the subnet to transit gateway. Specify the CIDR range or a prefix-list-id that you want routed to the transit gateway.
Example:
transit_gateway_ipv6_routes = {
public = "::/0"
private = "pl-123"
}
| `any` | `{}` | no | | [transit\_gateway\_routes](#input\_transit\_gateway\_routes) | Configuration of route(s) to transit gateway.
For each `public` and/or `private` subnets named in the `subnets` variable,
Optionally create routes from the subnet to transit gateway. Specify the CIDR range or a prefix-list-id that you want routed to the transit gateway.
Example:
transit_gateway_routes = {
public = "10.0.0.0/8"
private = "pl-123"
}
| `any` | `{}` | no | | [vpc\_assign\_generated\_ipv6\_cidr\_block](#input\_vpc\_assign\_generated\_ipv6\_cidr\_block) | Requests and Amazon-provided IPv6 CIDR block with a /56 prefix length. You cannot specify the range of IP addresses, or the size of the CIDR block. Conflics with `vpc_ipv6_ipam_pool_id`. | `bool` | `null` | no | | [vpc\_egress\_only\_internet\_gateway](#input\_vpc\_egress\_only\_internet\_gateway) | Set to use the Egress-only Internet gateway for all IPv6 traffic going to the Internet. | `bool` | `false` | no | @@ -388,7 +388,7 @@ Please see our [developer documentation](https://github.com/aws-ia/terraform-aws | [vpc\_instance\_tenancy](#input\_vpc\_instance\_tenancy) | The allowed tenancy of instances launched into the VPC. | `string` | `"default"` | no | | [vpc\_ipv4\_ipam\_pool\_id](#input\_vpc\_ipv4\_ipam\_pool\_id) | Set to use IPAM to get an IPv4 CIDR block. | `string` | `null` | no | | [vpc\_ipv4\_netmask\_length](#input\_vpc\_ipv4\_netmask\_length) | Set to use IPAM to get an IPv4 CIDR block using a specified netmask. Must be set with var.vpc\_ipv4\_ipam\_pool\_id. | `string` | `null` | no | -| [vpc\_ipv6\_cidr\_block](#input\_vpc\_ipv6\_cidr\_block) | IPv6 CIDR range to assign to VPC if creating VPC or to associate as a secondary IPv6 CIDR. Can be set explicitly or derived from IPAM using `vpc_ipv6_ipam_pool_id`. Overridden by `var.vpc_id` output from `data.aws_vpc`. | `string` | `null` | no | +| [vpc\_ipv6\_cidr\_block](#input\_vpc\_ipv6\_cidr\_block) | IPv6 CIDR range to assign to VPC if creating VPC. You need to use `vpc_ipv6_ipam_pool_id` and set explicitly the CIDR block to use, or derived from IPAM using using `vpc_ipv6_netmask_lenght`. | `string` | `null` | no | | [vpc\_ipv6\_ipam\_pool\_id](#input\_vpc\_ipv6\_ipam\_pool\_id) | Set to use IPAM to get an IPv6 CIDR block. | `string` | `null` | no | | [vpc\_ipv6\_netmask\_length](#input\_vpc\_ipv6\_netmask\_length) | Set to use IPAM to get an IPv6 CIDR block using a specified netmask. Must be set with `var.vpc_ipv6_ipam_pool_id`. | `string` | `null` | no | | [vpc\_secondary\_cidr](#input\_vpc\_secondary\_cidr) | If `true` the module will create a `aws_vpc_ipv4_cidr_block_association` and subnets for that secondary cidr. If using IPAM for both primary and secondary CIDRs, you may only call this module serially (aka using `-target`, etc). | `bool` | `false` | no | diff --git a/variables.tf b/variables.tf index 739a4df..672c34c 100644 --- a/variables.tf +++ b/variables.tf @@ -85,7 +85,7 @@ variable "vpc_ipv6_ipam_pool_id" { } variable "vpc_ipv6_cidr_block" { - description = "IPv6 CIDR range to assign to VPC if creating VPC or to associate as a secondary IPv6 CIDR. Can be set explicitly or derived from IPAM using `vpc_ipv6_ipam_pool_id`. Overridden by `var.vpc_id` output from `data.aws_vpc`." + description = "IPv6 CIDR range to assign to VPC if creating VPC. You need to use `vpc_ipv6_ipam_pool_id` and set explicitly the CIDR block to use, or derived from IPAM using using `vpc_ipv6_netmask_lenght`." type = string default = null } @@ -313,7 +313,7 @@ variable "transit_gateway_ipv6_routes" { Optionally create routes from the subnet to transit gateway. Specify the CIDR range or a prefix-list-id that you want routed to the transit gateway. Example: ``` - transit_gateway_routes = { + transit_gateway_ipv6_routes = { public = "::/0" private = "pl-123" } @@ -360,7 +360,7 @@ variable "core_network_ipv6_routes" { You can specify either a CIDR range or a prefix-list-id that you want routed to the core network. Example: ``` - core_network_routes = { + core_network_ivp6_routes = { public = "::/0" private = "pl-123" } From a67936af2ac7431d5a6e7f4b129397f16779751e Mon Sep 17 00:00:00 2001 From: Pablo Sanchez Carmona Date: Sun, 9 Apr 2023 12:14:34 +0200 Subject: [PATCH 6/6] tflint fixes --- modules/calculate_subnets/main.tf | 2 +- modules/calculate_subnets_ipv6/main.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/calculate_subnets/main.tf b/modules/calculate_subnets/main.tf index f8119ea..7ddfff1 100644 --- a/modules/calculate_subnets/main.tf +++ b/modules/calculate_subnets/main.tf @@ -21,7 +21,7 @@ locals { } module "subnet_calculator" { - count = local.types_to_calculate == [] ? 0 : 1 + count = length(local.types_to_calculate) == 0 ? 0 : 1 source = "drewmullen/subnets/cidr" version = "1.0.2" diff --git a/modules/calculate_subnets_ipv6/main.tf b/modules/calculate_subnets_ipv6/main.tf index 9f94352..90f3dbd 100644 --- a/modules/calculate_subnets_ipv6/main.tf +++ b/modules/calculate_subnets_ipv6/main.tf @@ -18,7 +18,7 @@ locals { } module "subnet_calculator" { - count = local.types_to_calculate == [] ? 0 : 1 + count = length(local.types_to_calculate) == 0 ? 0 : 1 source = "drewmullen/subnets/cidr" version = "1.0.2"