Skip to content

Commit 9c5ef29

Browse files
Chris Dobbyncloudpossebotaknysh
authored
Add ipv6 support (#55)
* Add ipv6 support * Auto Format * Update examples/complete/variables.tf Co-authored-by: Andriy Knysh <[email protected]> * Fix output, update test, and remove data block * Auto Format * test private networks still work * Ignore terraform lock file in the test dir * Update .gitignore Co-authored-by: cloudpossebot <[email protected]> Co-authored-by: Andriy Knysh <[email protected]>
1 parent b2edab8 commit 9c5ef29

11 files changed

+75
-17
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ Available targets:
294294
| [aws_network_acl.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl) | resource |
295295
| [aws_route.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource |
296296
| [aws_route.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource |
297+
| [aws_route.public_ipv6](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource |
297298
| [aws_route_table.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
298299
| [aws_route_table.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
299300
| [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:
317318
| <a name="input_environment"></a> [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no |
318319
| <a name="input_id_length_limit"></a> [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).<br>Set to `0` for unlimited length.<br>Set to `null` for keep the existing setting, which defaults to `0`.<br>Does not affect `id_full`. | `number` | `null` | no |
319320
| <a name="input_igw_id"></a> [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 |
321+
| <a name="input_ipv6_cidr_block"></a> [ipv6\_cidr\_block](#input\_ipv6\_cidr\_block) | Base IPv6 CIDR block which is divided into /64 subnet CIDR blocks | `string` | `null` | no |
322+
| <a name="input_ipv6_enabled"></a> [ipv6\_enabled](#input\_ipv6\_enabled) | Flag to enable/disable IPv6 creation in public subnets | `bool` | `false` | no |
320323
| <a name="input_label_key_case"></a> [label\_key\_case](#input\_label\_key\_case) | Controls the letter case of the `tags` keys (label names) for tags generated by this module.<br>Does not affect keys of tags passed in via the `tags` input.<br>Possible values: `lower`, `title`, `upper`.<br>Default value: `title`. | `string` | `null` | no |
321324
| <a name="input_label_order"></a> [label\_order](#input\_label\_order) | The order in which the labels (ID elements) appear in the `id`.<br>Defaults to ["namespace", "environment", "stage", "name", "attributes"].<br>You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. | `list(string)` | `null` | no |
322325
| <a name="input_label_value_case"></a> [label\_value\_case](#input\_label\_value\_case) | Controls the letter case of ID elements (labels) as included in `id`,<br>set as tag values, and output by this module individually.<br>Does not affect values of tags passed in via the `tags` input.<br>Possible values: `lower`, `title`, `upper` and `none` (no transformation).<br>Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs.<br>Default value: `lower`. | `string` | `null` | no |
@@ -347,6 +350,7 @@ Available targets:
347350
| <a name="output_az_subnet_arns"></a> [az\_subnet\_arns](#output\_az\_subnet\_arns) | Map of AZ names to subnet ARNs |
348351
| <a name="output_az_subnet_cidr_blocks"></a> [az\_subnet\_cidr\_blocks](#output\_az\_subnet\_cidr\_blocks) | Map of AZ names to subnet CIDR blocks |
349352
| <a name="output_az_subnet_ids"></a> [az\_subnet\_ids](#output\_az\_subnet\_ids) | Map of AZ names to subnet IDs |
353+
| <a name="output_az_subnet_ipv6_cidr_blocks"></a> [az\_subnet\_ipv6\_cidr\_blocks](#output\_az\_subnet\_ipv6\_cidr\_blocks) | Map of AZ names to subnet IPv6 CIDR blocks |
350354
| <a name="output_az_subnet_map"></a> [az\_subnet\_map](#output\_az\_subnet\_map) | Map of AZ names to map of information about subnets |
351355
<!-- markdownlint-restore -->
352356

docs/terraform.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
| [aws_network_acl.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl) | resource |
3333
| [aws_route.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource |
3434
| [aws_route.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource |
35+
| [aws_route.public_ipv6](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource |
3536
| [aws_route_table.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
3637
| [aws_route_table.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
3738
| [aws_route_table_association.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
@@ -55,6 +56,8 @@
5556
| <a name="input_environment"></a> [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no |
5657
| <a name="input_id_length_limit"></a> [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).<br>Set to `0` for unlimited length.<br>Set to `null` for keep the existing setting, which defaults to `0`.<br>Does not affect `id_full`. | `number` | `null` | no |
5758
| <a name="input_igw_id"></a> [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 |
59+
| <a name="input_ipv6_cidr_block"></a> [ipv6\_cidr\_block](#input\_ipv6\_cidr\_block) | Base IPv6 CIDR block which is divided into /64 subnet CIDR blocks | `string` | `null` | no |
60+
| <a name="input_ipv6_enabled"></a> [ipv6\_enabled](#input\_ipv6\_enabled) | Flag to enable/disable IPv6 creation in public subnets | `bool` | `false` | no |
5861
| <a name="input_label_key_case"></a> [label\_key\_case](#input\_label\_key\_case) | Controls the letter case of the `tags` keys (label names) for tags generated by this module.<br>Does not affect keys of tags passed in via the `tags` input.<br>Possible values: `lower`, `title`, `upper`.<br>Default value: `title`. | `string` | `null` | no |
5962
| <a name="input_label_order"></a> [label\_order](#input\_label\_order) | The order in which the labels (ID elements) appear in the `id`.<br>Defaults to ["namespace", "environment", "stage", "name", "attributes"].<br>You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. | `list(string)` | `null` | no |
6063
| <a name="input_label_value_case"></a> [label\_value\_case](#input\_label\_value\_case) | Controls the letter case of ID elements (labels) as included in `id`,<br>set as tag values, and output by this module individually.<br>Does not affect values of tags passed in via the `tags` input.<br>Possible values: `lower`, `title`, `upper` and `none` (no transformation).<br>Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs.<br>Default value: `lower`. | `string` | `null` | no |
@@ -85,5 +88,6 @@
8588
| <a name="output_az_subnet_arns"></a> [az\_subnet\_arns](#output\_az\_subnet\_arns) | Map of AZ names to subnet ARNs |
8689
| <a name="output_az_subnet_cidr_blocks"></a> [az\_subnet\_cidr\_blocks](#output\_az\_subnet\_cidr\_blocks) | Map of AZ names to subnet CIDR blocks |
8790
| <a name="output_az_subnet_ids"></a> [az\_subnet\_ids](#output\_az\_subnet\_ids) | Map of AZ names to subnet IDs |
91+
| <a name="output_az_subnet_ipv6_cidr_blocks"></a> [az\_subnet\_ipv6\_cidr\_blocks](#output\_az\_subnet\_ipv6\_cidr\_blocks) | Map of AZ names to subnet IPv6 CIDR blocks |
8892
| <a name="output_az_subnet_map"></a> [az\_subnet\_map](#output\_az\_subnet\_map) | Map of AZ names to map of information about subnets |
8993
<!-- markdownlint-restore -->

examples/complete/fixtures.us-east-2.tfvars

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ name = "multi-az-subnets"
88

99
availability_zones = ["us-east-2a", "us-east-2b", "us-east-2c"]
1010

11-
cidr_block = "172.16.0.0/16"
11+
cidr_block = "172.16.0.0/16"
12+
13+
ipv6_enabled = true

examples/complete/main.tf

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,24 @@ provider "aws" {
33
}
44

55
locals {
6-
public_cidr_block = cidrsubnet(var.cidr_block, 2, 0)
7-
public_only_cidr_block = cidrsubnet(var.cidr_block, 2, 1)
8-
private_cidr_block = cidrsubnet(var.cidr_block, 2, 2)
9-
private_only_cidr_block = cidrsubnet(var.cidr_block, 2, 3)
6+
public_cidr_block = cidrsubnet(var.cidr_block, 2, 0)
7+
public_only_cidr_block = cidrsubnet(var.cidr_block, 2, 1)
8+
private_cidr_block = cidrsubnet(var.cidr_block, 2, 2)
9+
private_only_cidr_block = cidrsubnet(var.cidr_block, 2, 3)
10+
public_ipv6_cidr_block = module.this.enabled ? cidrsubnet(module.vpc.ipv6_cidr_block, 2, 0) : ""
11+
public_only_ipv6_cidr_block = module.this.enabled ? cidrsubnet(module.vpc.ipv6_cidr_block, 2, 1) : ""
12+
1013
}
1114

1215
module "vpc" {
1316
source = "cloudposse/vpc/aws"
14-
version = "0.21.1"
17+
version = "0.27.0"
1518

16-
cidr_block = var.cidr_block
19+
cidr_block = var.cidr_block
20+
assign_generated_ipv6_cidr_block = true
1721

1822
context = module.this.context
23+
1924
}
2025

2126
module "public_subnets" {
@@ -27,6 +32,8 @@ module "public_subnets" {
2732
type = "public"
2833
igw_id = module.vpc.igw_id
2934
nat_gateway_enabled = true
35+
ipv6_enabled = var.ipv6_enabled
36+
ipv6_cidr_block = local.public_ipv6_cidr_block
3037

3138
context = module.this.context
3239
}
@@ -40,6 +47,8 @@ module "public_only_subnets" {
4047
type = "public"
4148
igw_id = module.vpc.igw_id
4249
nat_gateway_enabled = false
50+
ipv6_enabled = var.ipv6_enabled
51+
ipv6_cidr_block = local.public_only_ipv6_cidr_block
4352

4453
context = module.this.context
4554
}
@@ -51,6 +60,7 @@ module "private_subnets" {
5160
vpc_id = module.vpc.vpc_id
5261
cidr_block = local.private_cidr_block
5362
type = "private"
63+
ipv6_enabled = var.ipv6_enabled
5464

5565
# Map of AZ names to NAT Gateway IDs that was created in "public_subnets" module
5666
az_ngw_ids = module.public_subnets.az_ngw_ids
@@ -65,10 +75,10 @@ module "private_only_subnets" {
6575
vpc_id = module.vpc.vpc_id
6676
cidr_block = local.private_only_cidr_block
6777
type = "private"
78+
ipv6_enabled = false
6879

6980
# No NAT gateways supplied, should create subnets with empty route tables
7081
# az_ngw_ids = module.public_subnets.az_ngw_ids
7182

7283
context = module.this.context
7384
}
74-

examples/complete/outputs.tf

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,8 @@ output "private_az_subnet_cidr_blocks" {
4040

4141
output "public_az_subnet_cidr_blocks" {
4242
value = module.public_subnets.az_subnet_cidr_blocks
43-
}
43+
}
44+
45+
output "public_az_subnet_ipv6_cidr_blocks" {
46+
value = module.public_subnets.az_subnet_ipv6_cidr_blocks
47+
}

examples/complete/variables.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,8 @@ variable "availability_zones" {
1212
type = list(string)
1313
description = "List of Availability Zones (e.g. `['us-east-1a', 'us-east-1b', 'us-east-1c']`)"
1414
}
15+
16+
variable "ipv6_enabled" {
17+
description = "Flag to enable/disable IPv6 creation in public subnets"
18+
type = bool
19+
}

main.tf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ locals {
1111
subnet_cidr_block = local.public_enabled ? aws_subnet.public[az].cidr_block : aws_subnet.private[az].cidr_block
1212
route_table_id = local.public_enabled ? aws_route_table.public[az].id : aws_route_table.private[az].id
1313
ngw_id = local.public_enabled && var.nat_gateway_enabled ? aws_nat_gateway.public[az].id : null
14+
15+
subnet_ipv6_cidr_block = local.public_ipv6_enabled ? aws_subnet.public[az].ipv6_cidr_block : null
1416
}
1517
}
1618
}

outputs.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ output "az_subnet_cidr_blocks" {
1313
description = "Map of AZ names to subnet CIDR blocks"
1414
}
1515

16+
output "az_subnet_ipv6_cidr_blocks" {
17+
value = { for az, m in local.output_map : az => m.subnet_ipv6_cidr_block }
18+
description = "Map of AZ names to subnet IPv6 CIDR blocks"
19+
}
20+
1621
output "az_route_table_ids" {
1722
value = { for az, m in local.output_map : az => m.route_table_id }
1823
description = " Map of AZ names to Route Table IDs"

public.tf

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
locals {
2-
public_azs = local.public_enabled ? { for idx, az in var.availability_zones : az => idx } : {}
3-
public_nat_gateway_azs = local.public_enabled && var.nat_gateway_enabled ? local.public_azs : {}
2+
public_azs = local.public_enabled ? { for idx, az in var.availability_zones : az => idx } : {}
3+
public_nat_gateway_azs = local.public_enabled && var.nat_gateway_enabled ? local.public_azs : {}
4+
public_ipv6_enabled = local.public_enabled && var.ipv6_enabled
5+
public_ipv6_azs = local.public_ipv6_enabled ? local.public_azs : {}
6+
public_ipv6_target_mask = 64
47
}
58

69
module "public_label" {
@@ -18,6 +21,9 @@ resource "aws_subnet" "public" {
1821
vpc_id = var.vpc_id
1922
availability_zone = each.key
2023
cidr_block = cidrsubnet(var.cidr_block, ceil(log(var.max_subnets, 2)), each.value)
24+
ipv6_cidr_block = local.public_ipv6_enabled ? cidrsubnet(var.ipv6_cidr_block, (
25+
local.public_ipv6_target_mask - tonumber(split("/", var.ipv6_cidr_block)[1])
26+
), each.value) : null
2127

2228
tags = merge(
2329
module.public_label.tags,
@@ -88,6 +94,15 @@ resource "aws_route" "public" {
8894
depends_on = [aws_route_table.public]
8995
}
9096

97+
resource "aws_route" "public_ipv6" {
98+
for_each = local.public_ipv6_azs
99+
100+
route_table_id = aws_route_table.public[each.key].id
101+
gateway_id = var.igw_id
102+
destination_ipv6_cidr_block = "::/0"
103+
depends_on = [aws_route_table.public]
104+
}
105+
91106
resource "aws_route_table_association" "public" {
92107
for_each = local.public_azs
93108

test/src/examples_complete_test.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,10 @@ func TestExamplesComplete(t *testing.T) {
9494

9595
// Run `terraform output` to get the value of an output variable
9696
privateSubnetIds := terraform.OutputMap(t, terraformOptions, "private_az_subnet_ids")
97-
// Run `terraform output` to get the value of an output variable
9897
privateRouteTableIds := terraform.OutputMap(t, terraformOptions, "private_az_route_table_ids")
99-
// Run `terraform output` to get the value of an output variable
10098
publicNATGateWayIds := terraform.OutputMap(t, terraformOptions, "public_az_ngw_ids")
101-
// Run `terraform output` to get the value of an output variable
10299
publicOnlyNATGateWayIds := terraform.OutputMap(t, terraformOptions, "public_only_az_ngw_ids")
103-
// Run `terraform output` to get the value of an output variable
104100
publicRouteTableIds := terraform.OutputMap(t, terraformOptions, "public_az_route_table_ids")
105-
// Run `terraform output` to get the value of an output variable
106101
publicSubnetIds := terraform.OutputMap(t, terraformOptions, "public_az_subnet_ids")
107102

108103
expectedAZs := []string{"us-east-2a", "us-east-2b", "us-east-2c"}
@@ -146,7 +141,6 @@ func TestExamplesCompleteDisabledModule(t *testing.T) {
146141
Vars: map[string]interface{}{
147142
"enabled": "false",
148143
},
149-
150144
}
151145

152146
// At the end of the test, run `terraform destroy` to clean up any resources that were created
@@ -161,11 +155,13 @@ func TestExamplesCompleteDisabledModule(t *testing.T) {
161155
publicNATGateWayIds := terraform.OutputMap(t, terraformOptions, "public_az_ngw_ids")
162156
publicRouteTableIds := terraform.OutputMap(t, terraformOptions, "public_az_route_table_ids")
163157
publicSubnetIds := terraform.OutputMap(t, terraformOptions, "public_az_subnet_ids")
158+
publicSubnetIpv6CidrBlocks := terraform.OutputMap(t, terraformOptions, "public_az_subnet_ipv6_cidr_blocks")
164159

165160
assert.Empty(t, privateNATGateWayIds)
166161
assert.Empty(t, privateSubnetIds)
167162
assert.Empty(t, privateRouteTableIds)
168163
assert.Empty(t, publicNATGateWayIds)
169164
assert.Empty(t, publicSubnetIds)
170165
assert.Empty(t, publicRouteTableIds)
166+
assert.Empty(t, publicSubnetIpv6CidrBlocks)
171167
}

variables.tf

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,14 @@ variable "nat_gateway_enabled" {
121121
default = "true"
122122
}
123123

124+
variable "ipv6_enabled" {
125+
description = "Flag to enable/disable IPv6 creation in public subnets"
126+
type = bool
127+
default = false
128+
}
129+
130+
variable "ipv6_cidr_block" {
131+
type = string
132+
description = "Base IPv6 CIDR block which is divided into /64 subnet CIDR blocks"
133+
default = null
134+
}

0 commit comments

Comments
 (0)