This module can be used to deploy a pragmatic VPC with various subnets types in # AZs. Common deployment examples can be found in examples/.
Note: For information regarding the 4.0 upgrade see our upgrade guide.
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
.
module "vpc" {
source = "aws-ia/vpc/aws"
version = ">= 4.2.0"
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 = {
log_destination_type = "cloud-watch-logs"
retention_in_days = 180
}
}
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). Other custom subnet key names are valid are and those subnets will be private subnets.
subnets = {
public = {
name_prefix = "my-public" # omit to prefix with "public"
netmask = 24
nat_gateway_configuration = "all_azs" # options: "single_az", "none"
}
# naming private is not required, can use any key
private = {
# omitting name_prefix defaults value to "private"
# name_prefix = "private"
netmask = 24
connect_to_public_natgw = true
}
# can be any valid key name
privatetwo = {
# omitting name_prefix defaults value to "privatetwo"
# name_prefix = "private"
netmask = 24
}
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
assign_ipv6_cidr = true
transit_gateway_default_route_table_association = true
transit_gateway_default_route_table_propagation = true
transit_gateway_appliance_mode_support = "enable"
transit_gateway_dns_support = "disable"
tags = {
subnet_type = "tgw"
}
}
core_network = {
id = <>
arn = <>
}
core_network_routes = {
workload = "pl-123"
}
core_network_ipv6_routes = {
workload = "::/0"
}
subnets = {
workload = {
name_prefix = "workload-private"
netmask = 24
assign_ipv6_cidr = true
}
core_network = {
netmask = 28
assign_ipv6_cidr = true
appliance_mode_support = false
require_acceptance = true
accept_attachment = true
tags = {
env = "prod"
}
}
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.
Example: Changing from 2 azs to 3
Before:
cidr_block = "10.0.0.0/16"
vpc_assign_generated_ipv6_cidr_block = true
az_count = 2
subnets = {
public = {
netmask = 24
assign_ipv6_cidr = true
}
private = {
netmask = 24
assign_ipv6_cidr = true
}
}
After:
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"]
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"]
ipv6_cidrs = ["2a05:d01c:bc3:b202::/64", "2a05:d01c:bc3:b203::/64", "2a05:d01c:bc3:b205::/64"]
}
}
The above example will cause only creating 2 new subnets in az c
of the region being used.
The outputs in this module attempt to align to a methodology of outputting resource attributes in a reasonable collection. The benefit of this is that, most likely, attributes you want access to are already present without having to create new output {}
for each possible attribute. The [potential] downside is that you will have to extract it yourself using HCL logic. Below are some common examples:
For more examples and explanation see [output docs]((https://github.com/aws-ia/terraform-aws-vpc/blob/main/docs/how-to-use-module-outputs.md)
Example Configuration:
module "vpc" {
source = "aws-ia/vpc/aws"
version = ">= 4.2.0"
name = "multi-az-vpc"
cidr_block = "10.0.0.0/20"
az_count = 3
subnets = {
private = { netmask = 24 }
}
}
Extracting subnet_ids to a list (using terraform console
for example output):
> [ for _, value in module.vpc.private_subnet_attributes_by_az: value.id]
[
"subnet-04a86315c4839b519",
"subnet-02a7249c8652a7136",
"subnet-09af79b5329b3681f",
]
Alternatively, since these are maps, you can use key in another resource for_each
loop. The benefit here is that your dependent resource will have keys that match the AZ the subnet is in:
resource "aws_route53recoveryreadiness_cell" "cell_per_az" {
for_each = module.vpc.private_subnet_attributes_by_az
cell_name = "${each.key}-failover-cell-for-subnet-${each.value.id}"
}
...
Terraform Plan:
# aws_route53recoveryreadiness_cell.cell_per_az["us-east-1a"] will be created
+ resource "aws_route53recoveryreadiness_cell" "cell_per_az" {
+ cell_name = "us-east-1a-failover-cell-for-subnet-subnet-070696086c5864da1"
...
}
# aws_route53recoveryreadiness_cell.cell_per_az["us-east-1b"] will be created
...
Error:
error creating Route in Route Table (rtb-xxx) with destination (YYY): InvalidCoreNetworkArn.NotFound: The core network arn 'arn:aws:networkmanager::XXXX:core-network/core-network-YYYYY' does not exist.
This happens when the Core Network's VPC attachment requires acceptance, so it's not possible to create the routes in the VPC until the attachment is accepted. Check the following:
- If the VPC attachment requires acceptance and you want the module to automatically accept it, configure
require_acceptance
andaccept_attachment
totrue
.
subnets = {
core_network = {
netmask = 28
assign_ipv6_cidr = true
require_acceptance = true
accept_attachment = true
}
}
- If the VPC attachment requires acceptance but you want to accept it outside the module, first configure
require_acceptance
totrue
andaccept_attachment
tofalse
.
subnets = {
core_network = {
netmask = 28
assign_ipv6_cidr = true
require_acceptance = true
accept_attachment = true
}
}
After you apply and the attachment is accepted (outside the module), change the subnet configuration with require_acceptance
to false
.
subnets = {
core_network = {
netmask = 28
assign_ipv6_cidr = true
require_acceptance = false
}
}
- Alternatively, you can also not configure any subnet route (
var.core_network_routes
) to the Core Network until the attachment gets accepted.
Please see our developer documentation for guidance on contributing to this module.