diff --git a/README.md b/README.md index 39a3ed1..bc70071 100644 --- a/README.md +++ b/README.md @@ -294,6 +294,7 @@ Available targets: | [aws_network_acl.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl) | resource | | [aws_route.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | | [aws_route.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | +| [aws_route.public_ipv6](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | | [aws_route_table.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource | | [aws_route_table.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource | | [aws_route_table_association.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | @@ -317,6 +318,8 @@ Available targets: | [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no | | [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).
Set to `0` for unlimited length.
Set to `null` for keep the existing setting, which defaults to `0`.
Does not affect `id_full`. | `number` | `null` | no | | [igw\_id](#input\_igw\_id) | Internet Gateway ID that is used as a default route when creating public subnets (e.g. `igw-9c26a123`) | `string` | `""` | no | +| [ipv6\_cidr\_block](#input\_ipv6\_cidr\_block) | Base IPv6 CIDR block which is divided into /64 subnet CIDR blocks | `string` | `null` | no | +| [ipv6\_enabled](#input\_ipv6\_enabled) | Flag to enable/disable IPv6 creation in public subnets | `bool` | `false` | no | | [label\_key\_case](#input\_label\_key\_case) | Controls the letter case of the `tags` keys (label names) for tags generated by this module.
Does not affect keys of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper`.
Default value: `title`. | `string` | `null` | no | | [label\_order](#input\_label\_order) | The order in which the labels (ID elements) appear in the `id`.
Defaults to ["namespace", "environment", "stage", "name", "attributes"].
You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. | `list(string)` | `null` | no | | [label\_value\_case](#input\_label\_value\_case) | Controls the letter case of ID elements (labels) as included in `id`,
set as tag values, and output by this module individually.
Does not affect values of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper` and `none` (no transformation).
Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs.
Default value: `lower`. | `string` | `null` | no | @@ -347,6 +350,7 @@ Available targets: | [az\_subnet\_arns](#output\_az\_subnet\_arns) | Map of AZ names to subnet ARNs | | [az\_subnet\_cidr\_blocks](#output\_az\_subnet\_cidr\_blocks) | Map of AZ names to subnet CIDR blocks | | [az\_subnet\_ids](#output\_az\_subnet\_ids) | Map of AZ names to subnet IDs | +| [az\_subnet\_ipv6\_cidr\_blocks](#output\_az\_subnet\_ipv6\_cidr\_blocks) | Map of AZ names to subnet IPv6 CIDR blocks | | [az\_subnet\_map](#output\_az\_subnet\_map) | Map of AZ names to map of information about subnets | diff --git a/docs/terraform.md b/docs/terraform.md index d016ded..e7a9c7b 100644 --- a/docs/terraform.md +++ b/docs/terraform.md @@ -32,6 +32,7 @@ | [aws_network_acl.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl) | resource | | [aws_route.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | | [aws_route.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | +| [aws_route.public_ipv6](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | | [aws_route_table.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource | | [aws_route_table.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource | | [aws_route_table_association.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | @@ -55,6 +56,8 @@ | [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no | | [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).
Set to `0` for unlimited length.
Set to `null` for keep the existing setting, which defaults to `0`.
Does not affect `id_full`. | `number` | `null` | no | | [igw\_id](#input\_igw\_id) | Internet Gateway ID that is used as a default route when creating public subnets (e.g. `igw-9c26a123`) | `string` | `""` | no | +| [ipv6\_cidr\_block](#input\_ipv6\_cidr\_block) | Base IPv6 CIDR block which is divided into /64 subnet CIDR blocks | `string` | `null` | no | +| [ipv6\_enabled](#input\_ipv6\_enabled) | Flag to enable/disable IPv6 creation in public subnets | `bool` | `false` | no | | [label\_key\_case](#input\_label\_key\_case) | Controls the letter case of the `tags` keys (label names) for tags generated by this module.
Does not affect keys of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper`.
Default value: `title`. | `string` | `null` | no | | [label\_order](#input\_label\_order) | The order in which the labels (ID elements) appear in the `id`.
Defaults to ["namespace", "environment", "stage", "name", "attributes"].
You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. | `list(string)` | `null` | no | | [label\_value\_case](#input\_label\_value\_case) | Controls the letter case of ID elements (labels) as included in `id`,
set as tag values, and output by this module individually.
Does not affect values of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper` and `none` (no transformation).
Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs.
Default value: `lower`. | `string` | `null` | no | @@ -85,5 +88,6 @@ | [az\_subnet\_arns](#output\_az\_subnet\_arns) | Map of AZ names to subnet ARNs | | [az\_subnet\_cidr\_blocks](#output\_az\_subnet\_cidr\_blocks) | Map of AZ names to subnet CIDR blocks | | [az\_subnet\_ids](#output\_az\_subnet\_ids) | Map of AZ names to subnet IDs | +| [az\_subnet\_ipv6\_cidr\_blocks](#output\_az\_subnet\_ipv6\_cidr\_blocks) | Map of AZ names to subnet IPv6 CIDR blocks | | [az\_subnet\_map](#output\_az\_subnet\_map) | Map of AZ names to map of information about subnets | diff --git a/examples/complete/fixtures.us-east-2.tfvars b/examples/complete/fixtures.us-east-2.tfvars index c5823f0..5f56e58 100644 --- a/examples/complete/fixtures.us-east-2.tfvars +++ b/examples/complete/fixtures.us-east-2.tfvars @@ -8,4 +8,6 @@ name = "multi-az-subnets" availability_zones = ["us-east-2a", "us-east-2b", "us-east-2c"] -cidr_block = "172.16.0.0/16" \ No newline at end of file +cidr_block = "172.16.0.0/16" + +ipv6_enabled = true diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 26205be..1f3724b 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -3,19 +3,24 @@ provider "aws" { } locals { - public_cidr_block = cidrsubnet(var.cidr_block, 2, 0) - public_only_cidr_block = cidrsubnet(var.cidr_block, 2, 1) - private_cidr_block = cidrsubnet(var.cidr_block, 2, 2) - private_only_cidr_block = cidrsubnet(var.cidr_block, 2, 3) + public_cidr_block = cidrsubnet(var.cidr_block, 2, 0) + public_only_cidr_block = cidrsubnet(var.cidr_block, 2, 1) + private_cidr_block = cidrsubnet(var.cidr_block, 2, 2) + private_only_cidr_block = cidrsubnet(var.cidr_block, 2, 3) + public_ipv6_cidr_block = module.this.enabled ? cidrsubnet(module.vpc.ipv6_cidr_block, 2, 0) : "" + public_only_ipv6_cidr_block = module.this.enabled ? cidrsubnet(module.vpc.ipv6_cidr_block, 2, 1) : "" + } module "vpc" { source = "cloudposse/vpc/aws" - version = "0.21.1" + version = "0.27.0" - cidr_block = var.cidr_block + cidr_block = var.cidr_block + assign_generated_ipv6_cidr_block = true context = module.this.context + } module "public_subnets" { @@ -27,6 +32,8 @@ module "public_subnets" { type = "public" igw_id = module.vpc.igw_id nat_gateway_enabled = true + ipv6_enabled = var.ipv6_enabled + ipv6_cidr_block = local.public_ipv6_cidr_block context = module.this.context } @@ -40,6 +47,8 @@ module "public_only_subnets" { type = "public" igw_id = module.vpc.igw_id nat_gateway_enabled = false + ipv6_enabled = var.ipv6_enabled + ipv6_cidr_block = local.public_only_ipv6_cidr_block context = module.this.context } @@ -51,6 +60,7 @@ module "private_subnets" { vpc_id = module.vpc.vpc_id cidr_block = local.private_cidr_block type = "private" + ipv6_enabled = var.ipv6_enabled # Map of AZ names to NAT Gateway IDs that was created in "public_subnets" module az_ngw_ids = module.public_subnets.az_ngw_ids @@ -65,10 +75,10 @@ module "private_only_subnets" { vpc_id = module.vpc.vpc_id cidr_block = local.private_only_cidr_block type = "private" + ipv6_enabled = false # No NAT gateways supplied, should create subnets with empty route tables # az_ngw_ids = module.public_subnets.az_ngw_ids context = module.this.context } - diff --git a/examples/complete/outputs.tf b/examples/complete/outputs.tf index f76982f..2a97c53 100644 --- a/examples/complete/outputs.tf +++ b/examples/complete/outputs.tf @@ -40,4 +40,8 @@ output "private_az_subnet_cidr_blocks" { output "public_az_subnet_cidr_blocks" { value = module.public_subnets.az_subnet_cidr_blocks -} \ No newline at end of file +} + +output "public_az_subnet_ipv6_cidr_blocks" { + value = module.public_subnets.az_subnet_ipv6_cidr_blocks +} diff --git a/examples/complete/variables.tf b/examples/complete/variables.tf index b0e215f..8e81974 100644 --- a/examples/complete/variables.tf +++ b/examples/complete/variables.tf @@ -12,3 +12,8 @@ variable "availability_zones" { type = list(string) description = "List of Availability Zones (e.g. `['us-east-1a', 'us-east-1b', 'us-east-1c']`)" } + +variable "ipv6_enabled" { + description = "Flag to enable/disable IPv6 creation in public subnets" + type = bool +} diff --git a/main.tf b/main.tf index d9fd82d..2e3d6ac 100644 --- a/main.tf +++ b/main.tf @@ -11,6 +11,8 @@ locals { subnet_cidr_block = local.public_enabled ? aws_subnet.public[az].cidr_block : aws_subnet.private[az].cidr_block route_table_id = local.public_enabled ? aws_route_table.public[az].id : aws_route_table.private[az].id ngw_id = local.public_enabled && var.nat_gateway_enabled ? aws_nat_gateway.public[az].id : null + + subnet_ipv6_cidr_block = local.public_ipv6_enabled ? aws_subnet.public[az].ipv6_cidr_block : null } } } diff --git a/outputs.tf b/outputs.tf index 95ac2c1..f54f0de 100644 --- a/outputs.tf +++ b/outputs.tf @@ -13,6 +13,11 @@ output "az_subnet_cidr_blocks" { description = "Map of AZ names to subnet CIDR blocks" } +output "az_subnet_ipv6_cidr_blocks" { + value = { for az, m in local.output_map : az => m.subnet_ipv6_cidr_block } + description = "Map of AZ names to subnet IPv6 CIDR blocks" +} + output "az_route_table_ids" { value = { for az, m in local.output_map : az => m.route_table_id } description = " Map of AZ names to Route Table IDs" diff --git a/public.tf b/public.tf index bff16da..b16a21f 100644 --- a/public.tf +++ b/public.tf @@ -1,6 +1,9 @@ locals { - public_azs = local.public_enabled ? { for idx, az in var.availability_zones : az => idx } : {} - public_nat_gateway_azs = local.public_enabled && var.nat_gateway_enabled ? local.public_azs : {} + public_azs = local.public_enabled ? { for idx, az in var.availability_zones : az => idx } : {} + public_nat_gateway_azs = local.public_enabled && var.nat_gateway_enabled ? local.public_azs : {} + public_ipv6_enabled = local.public_enabled && var.ipv6_enabled + public_ipv6_azs = local.public_ipv6_enabled ? local.public_azs : {} + public_ipv6_target_mask = 64 } module "public_label" { @@ -18,6 +21,9 @@ resource "aws_subnet" "public" { vpc_id = var.vpc_id availability_zone = each.key cidr_block = cidrsubnet(var.cidr_block, ceil(log(var.max_subnets, 2)), each.value) + ipv6_cidr_block = local.public_ipv6_enabled ? cidrsubnet(var.ipv6_cidr_block, ( + local.public_ipv6_target_mask - tonumber(split("/", var.ipv6_cidr_block)[1]) + ), each.value) : null tags = merge( module.public_label.tags, @@ -88,6 +94,15 @@ resource "aws_route" "public" { depends_on = [aws_route_table.public] } +resource "aws_route" "public_ipv6" { + for_each = local.public_ipv6_azs + + route_table_id = aws_route_table.public[each.key].id + gateway_id = var.igw_id + destination_ipv6_cidr_block = "::/0" + depends_on = [aws_route_table.public] +} + resource "aws_route_table_association" "public" { for_each = local.public_azs diff --git a/test/src/examples_complete_test.go b/test/src/examples_complete_test.go index 585d1bc..f820403 100644 --- a/test/src/examples_complete_test.go +++ b/test/src/examples_complete_test.go @@ -94,15 +94,10 @@ func TestExamplesComplete(t *testing.T) { // Run `terraform output` to get the value of an output variable privateSubnetIds := terraform.OutputMap(t, terraformOptions, "private_az_subnet_ids") - // Run `terraform output` to get the value of an output variable privateRouteTableIds := terraform.OutputMap(t, terraformOptions, "private_az_route_table_ids") - // Run `terraform output` to get the value of an output variable publicNATGateWayIds := terraform.OutputMap(t, terraformOptions, "public_az_ngw_ids") - // Run `terraform output` to get the value of an output variable publicOnlyNATGateWayIds := terraform.OutputMap(t, terraformOptions, "public_only_az_ngw_ids") - // Run `terraform output` to get the value of an output variable publicRouteTableIds := terraform.OutputMap(t, terraformOptions, "public_az_route_table_ids") - // Run `terraform output` to get the value of an output variable publicSubnetIds := terraform.OutputMap(t, terraformOptions, "public_az_subnet_ids") expectedAZs := []string{"us-east-2a", "us-east-2b", "us-east-2c"} @@ -146,7 +141,6 @@ func TestExamplesCompleteDisabledModule(t *testing.T) { Vars: map[string]interface{}{ "enabled": "false", }, - } // At the end of the test, run `terraform destroy` to clean up any resources that were created @@ -161,6 +155,7 @@ func TestExamplesCompleteDisabledModule(t *testing.T) { publicNATGateWayIds := terraform.OutputMap(t, terraformOptions, "public_az_ngw_ids") publicRouteTableIds := terraform.OutputMap(t, terraformOptions, "public_az_route_table_ids") publicSubnetIds := terraform.OutputMap(t, terraformOptions, "public_az_subnet_ids") + publicSubnetIpv6CidrBlocks := terraform.OutputMap(t, terraformOptions, "public_az_subnet_ipv6_cidr_blocks") assert.Empty(t, privateNATGateWayIds) assert.Empty(t, privateSubnetIds) @@ -168,4 +163,5 @@ func TestExamplesCompleteDisabledModule(t *testing.T) { assert.Empty(t, publicNATGateWayIds) assert.Empty(t, publicSubnetIds) assert.Empty(t, publicRouteTableIds) + assert.Empty(t, publicSubnetIpv6CidrBlocks) } diff --git a/variables.tf b/variables.tf index 261bd2e..9883486 100644 --- a/variables.tf +++ b/variables.tf @@ -121,3 +121,14 @@ variable "nat_gateway_enabled" { default = "true" } +variable "ipv6_enabled" { + description = "Flag to enable/disable IPv6 creation in public subnets" + type = bool + default = false +} + +variable "ipv6_cidr_block" { + type = string + description = "Base IPv6 CIDR block which is divided into /64 subnet CIDR blocks" + default = null +}