Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add IPv6 support (Terraform 0.12 edition) #300

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions examples/ipv6/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
provider "aws" {
region = "${var.region}"
}

data "aws_availability_zones" "available" {}

module "vpc" {
source = "../.."
name = "ipv6"
cidr = "10.0.0.0/16"
azs = ["${data.aws_availability_zones.available.names[0]}", "${data.aws_availability_zones.available.names[1]}"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
enable_nat_gateway = false

enable_ipv6 = true
assign_ipv6_address_on_creation = true
public_subnet_ipv6_prefixes = [0, 1]
private_subnet_ipv6_prefixes = [2, 3]

tags = {
Owner = "user"
Environment = "dev"
}
}
15 changes: 15 additions & 0 deletions examples/ipv6/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# VPC
output "vpc_id" {
description = "The ID of the VPC"
value = "${module.vpc.vpc_id}"
}

output "ipv6_association_id" {
description = "The IPv6 CIDR block"
value = ["${module.vpc.vpc_ipv6_cidr_block}"]
}

output "ipv6_cidr_block" {
description = "The association ID for the IPv6 CIDR block"
value = ["${module.vpc.vpc_ipv6_association_id}"]
}
3 changes: 3 additions & 0 deletions examples/ipv6/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
variable "region" {
default = "eu-west-1"
}
63 changes: 52 additions & 11 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ resource "aws_vpc" "this" {
instance_tenancy = var.instance_tenancy
enable_dns_hostnames = var.enable_dns_hostnames
enable_dns_support = var.enable_dns_support
assign_generated_ipv6_cidr_block = var.assign_generated_ipv6_cidr_block
assign_generated_ipv6_cidr_block = var.enable_ipv6

tags = merge(
{
Expand Down Expand Up @@ -95,6 +95,12 @@ resource "aws_internet_gateway" "this" {
)
}

resource "aws_egress_only_internet_gateway" "this" {
count = var.enable_ipv6 && length(var.database_subnets) + length(var.private_subnets) > 0 ? 1 : 0

vpc_id = element(concat(aws_vpc.this.*.id, [""]), 0)
}

################
# Publiс routes
################
Expand Down Expand Up @@ -124,6 +130,14 @@ resource "aws_route" "public_internet_gateway" {
}
}

resource "aws_route" "public_internet_gateway_ipv6" {
count = var.create_vpc && var.enable_ipv6 && length(var.public_subnets) > 0 ? 1 : 0

route_table_id = aws_route_table.public[0].id
destination_ipv6_cidr_block = "::/0"
gateway_id = aws_internet_gateway.this[0].id
}

#################
# Private routes
# There are as many routing tables as the number of NAT gateways
Expand Down Expand Up @@ -193,6 +207,14 @@ resource "aws_route" "database_nat_gateway" {
}
}

resource "aws_route" "database_ipv6_egress" {
count = var.enable_ipv6 ? length(var.database_subnets) : 0

route_table_id = element(aws_route_table.database.*.id, count.index)
destination_ipv6_cidr_block = "::/0"
egress_only_gateway_id = element(aws_egress_only_internet_gateway.this.*.id, 0)
}

#################
# Redshift routes
#################
Expand Down Expand Up @@ -250,10 +272,13 @@ resource "aws_route_table" "intra" {
resource "aws_subnet" "public" {
count = var.create_vpc && length(var.public_subnets) > 0 && (false == var.one_nat_gateway_per_az || length(var.public_subnets) >= length(var.azs)) ? length(var.public_subnets) : 0

vpc_id = local.vpc_id
cidr_block = element(concat(var.public_subnets, [""]), count.index)
availability_zone = element(var.azs, count.index)
map_public_ip_on_launch = var.map_public_ip_on_launch
vpc_id = local.vpc_id
cidr_block = element(concat(var.public_subnets, [""]), count.index)
availability_zone = element(var.azs, count.index)
map_public_ip_on_launch = var.map_public_ip_on_launch
assign_ipv6_address_on_creation = var.assign_ipv6_address_on_creation

ipv6_cidr_block = var.enable_ipv6 && length(var.public_subnet_ipv6_prefixes) > 0 ? cidrsubnet(aws_vpc.this[0].ipv6_cidr_block, 8, element(concat(var.public_subnet_ipv6_prefixes, list("0")), count.index)) : null

tags = merge(
{
Expand All @@ -274,9 +299,12 @@ resource "aws_subnet" "public" {
resource "aws_subnet" "private" {
count = var.create_vpc && length(var.private_subnets) > 0 ? length(var.private_subnets) : 0

vpc_id = local.vpc_id
cidr_block = var.private_subnets[count.index]
availability_zone = element(var.azs, count.index)
vpc_id = local.vpc_id
cidr_block = var.private_subnets[count.index]
availability_zone = element(var.azs, count.index)
assign_ipv6_address_on_creation = var.assign_ipv6_address_on_creation

ipv6_cidr_block = var.enable_ipv6 && length(var.private_subnet_ipv6_prefixes) > 0 ? cidrsubnet(aws_vpc.this[0].ipv6_cidr_block, 8, element(coalescelist(var.private_subnet_ipv6_prefixes, list("0")), count.index)) : null

tags = merge(
{
Expand All @@ -297,9 +325,12 @@ resource "aws_subnet" "private" {
resource "aws_subnet" "database" {
count = var.create_vpc && length(var.database_subnets) > 0 ? length(var.database_subnets) : 0

vpc_id = local.vpc_id
cidr_block = var.database_subnets[count.index]
availability_zone = element(var.azs, count.index)
vpc_id = local.vpc_id
cidr_block = var.database_subnets[count.index]
availability_zone = element(var.azs, count.index)
assign_ipv6_address_on_creation = var.assign_ipv6_address_on_creation

ipv6_cidr_block = var.enable_ipv6 && length(var.database_subnet_ipv6_prefixes) > 0 ? cidrsubnet(aws_vpc.this[0].ipv6_cidr_block, 8, element(concat(var.database_subnet_ipv6_prefixes, list("0")), count.index)) : null

tags = merge(
{
Expand Down Expand Up @@ -410,6 +441,8 @@ resource "aws_subnet" "intra" {
cidr_block = var.intra_subnets[count.index]
availability_zone = element(var.azs, count.index)

ipv6_cidr_block = var.enable_ipv6 && length(var.intra_subnet_ipv6_prefixes) > 0 ? cidrsubnet(aws_vpc.this[0].ipv6_cidr_block, 8, element(concat(var.intra_subnet_ipv6_prefixes, list("0")), count.index)) : null

tags = merge(
{
"Name" = format(
Expand Down Expand Up @@ -824,6 +857,14 @@ resource "aws_route" "private_nat_gateway" {
}
}

resource "aws_route" "private_ipv6_egress" {
count = var.enable_ipv6 ? length(var.private_subnets) : 0

route_table_id = element(aws_route_table.private.*.id, count.index)
destination_ipv6_cidr_block = "::/0"
egress_only_gateway_id = element(aws_egress_only_internet_gateway.this.*.id, 0)
}

######################
# VPC Endpoint for S3
######################
Expand Down
42 changes: 33 additions & 9 deletions outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ output "vpc_main_route_table_id" {
value = concat(aws_vpc.this.*.main_route_table_id, [""])[0]
}

//output "vpc_ipv6_association_id" {
// description = "The association ID for the IPv6 CIDR block"
// value = "${element(concat(aws_vpc.this.*.ipv6_association_id, list("")), 0)}"
//}
//
//output "vpc_ipv6_cidr_block" {
// description = "The IPv6 CIDR block"
// value = "${element(concat(aws_vpc.this.*.ipv6_cidr_block, list("")), 0)}"
//}
output "vpc_ipv6_association_id" {
description = "The association ID for the IPv6 CIDR block"
value = element(concat(aws_vpc.this.*.ipv6_association_id, list("")), 0)
}

output "vpc_ipv6_cidr_block" {
description = "The IPv6 CIDR block"
value = element(concat(aws_vpc.this.*.ipv6_cidr_block, list("")), 0)
}

output "vpc_secondary_cidr_blocks" {
description = "List of secondary CIDR blocks of the VPC"
Expand Down Expand Up @@ -624,3 +624,27 @@ output "azs" {
value = var.azs
}

output "ipv6_egress_only_igw_id" {
description = "The ID of the egress only Internet Gateway"
value = element(concat(aws_egress_only_internet_gateway.this.*.id, list("")), 0)
}

output "public_subnets_ipv6_cidr_blocks" {
description = "List of IPv6 cidr_blocks of public subnets in an IPv6 enabled VPC"
value = aws_subnet.public.*.ipv6_cidr_block
}

output "private_subnets_ipv6_cidr_blocks" {
description = "List of IPv6 cidr_blocks of private subnets in an IPv6 enabled VPC"
value = aws_subnet.private.*.ipv6_cidr_block
}

output "database_subnets_ipv6_cidr_blocks" {
description = "List of IPv6 cidr_blocks of database subnets in an IPv6 enabled VPC"
value = aws_subnet.database.*.ipv6_cidr_block
}

output "intra_subnets_ipv6_cidr_blocks" {
description = "List of IPv6 cidr_blocks of intra subnets in an IPv6 enabled VPC"
value = aws_subnet.intra.*.ipv6_cidr_block
}
34 changes: 34 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,40 @@ variable "assign_generated_ipv6_cidr_block" {
default = false
}

variable "enable_ipv6" {
description = "Assigns IPv6 subnets and routes"
default = false
}

variable "private_subnet_ipv6_prefixes" {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strictly speaking.. those are not prefixes, but more the.. indices of the prefixes? Suggestions?

Copy link

@dekimsey dekimsey Jul 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, they aren't. They are the last octet* of the given ipv6 prefix. IPv6 docs tends to be prefix based, so I chose that. Admittedly, it's not a great name, but I couldn't for the life of me figure out what a better way to describe the fact they are an enumerated list of the last octet in an IPv6 prefix such as 2001:db8::ff00:42:83XX. I explicitly didn't go with index or slice because I didn't want it to be confused as an index of another list.

So maybe ..._subnet_ipv6_octet?

(*) As a decimal because terraform didn't have functions for converting octal to dec.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about giving an example in the description? While it already suggests 0-256, an example like [0, 1, 2]might make it clearer. The description (and the topic) feels pretty complex :-)

description = "Assigns IPv6 subnet id based on the Amazon provided /56 prefix base 10 integer (0-256). Must be of equal length to the corresponding IPv4 subnet list"
default = []
type = "list"
}

variable "public_subnet_ipv6_prefixes" {
description = "Assigns IPv6 subnet id based on the Amazon provided /56 prefix base 10 integer (0-256). Must be of equal length to the corresponding IPv4 subnet list"
default = []
type = "list"
}

variable "database_subnet_ipv6_prefixes" {
description = "Assigns IPv6 subnet id based on the Amazon provided /56 prefix base 10 integer (0-256). Must be of equal length to the corresponding IPv4 subnet list"
default = []
type = "list"
}

variable "intra_subnet_ipv6_prefixes" {
description = "Assigns IPv6 subnet id based on the Amazon provided /56 prefix base 10 integer (0-256). Must be of equal length to the corresponding IPv4 subnet list"
default = []
type = "list"
}

variable "assign_ipv6_address_on_creation" {
description = "Assign IPv6 address on subnet, must be disabled to change IPv6 CIDRs. This is the IPv6 equivalent of map_public_ip_on_launch"
default = false
}

variable "secondary_cidr_blocks" {
description = "List of secondary CIDR blocks to associate with the VPC to extend the IP Address pool"
type = list(string)
Expand Down