AWS VPC Terraform module
Terraform module which creates VPC resources on AWS.
Usage
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = true
enable_vpn_gateway = true
tags = {
Terraform = "true"
Environment = "dev"
}
}
[!WARNING] v6.x of the module still supports creating a VPC Flow Log within the root (VPC) module. However, this is deprecated behavior and will be removed in v7.0.0. Please use the standalone flow log module instead.
External NAT Gateway IPs
By default this module will provision new Elastic IPs for the VPC's NAT Gateways. This means that when creating a new VPC, new IPs are allocated, and when that VPC is destroyed those IPs are released. Sometimes it is handy to keep the same IPs even after the VPC is destroyed and re-created. To that end, it is possible to assign existing IPs to the NAT Gateways. This prevents the destruction of the VPC from releasing those IPs, while making it possible that a re-created VPC uses the same IPs.
To achieve this, allocate the IPs outside the VPC module declaration.
resource "aws_eip" "nat" {
count = 3
vpc = true
}
Then, pass the allocated IPs as a parameter to this module.
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
# The rest of arguments are omitted for brevity
enable_nat_gateway = true
single_nat_gateway = false
reuse_nat_ips = true # <= Skip creation of EIPs for the NAT Gateways
external_nat_ip_ids = "${aws_eip.nat.*.id}" # <= IPs specified here as input to the module
}
Note that in the example we allocate 3 IPs because we will be provisioning 3 NAT Gateways (due to single_nat_gateway = false and having 3 subnets).
If, on the other hand, single_nat_gateway = true, then aws_eip.nat would only need to allocate 1 IP.
Passing the IPs into the module is done by setting two variables reuse_nat_ips = true and external_nat_ip_ids = "${aws_eip.nat.*.id}".
NAT Gateway Scenarios
This module supports three scenarios for creating NAT gateways. Each will be explained in further detail in the corresponding sections.
- One NAT Gateway per subnet (default behavior)
enable_nat_gateway = truesingle_nat_gateway = falseone_nat_gateway_per_az = false
- Single NAT Gateway
enable_nat_gateway = truesingle_nat_gateway = trueone_nat_gateway_per_az = false
- One NAT Gateway per availability zone
enable_nat_gateway = truesingle_nat_gateway = falseone_nat_gateway_per_az = true
If both single_nat_gateway and one_nat_gateway_per_az are set to true, then single_nat_gateway takes precedence.
One NAT Gateway per subnet (default)
By default, the module will determine the number of NAT Gateways to create based on the max() of the private subnet lists (database_subnets, elasticache_subnets, private_subnets, and redshift_subnets). The module does not take into account the number of intra_subnets, since the latter are designed to have no Internet access via NAT Gateway. For example, if your configuration looks like the following:
database_subnets = ["10.0.21.0/24", "10.0.22.0/24"]
elasticache_subnets = ["10.0.31.0/24", "10.0.32.0/24"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24", "10.0.4.0/24", "10.0.5.0/24"]
redshift_subnets = ["10.0.41.0/24", "10.0.42.0/24"]
intra_subnets = ["10.0.51.0/24", "10.0.52.0/24", "10.0.53.0/24"]
Then 5 NAT Gateways will be created since 5 private subnet CIDR blocks were specified.
Single NAT Gateway
If single_nat_gateway = true, then all private subnets will route their Internet traffic through this single NAT gateway. The NAT gateway will be placed in the first public subnet in your public_subnets block.
One NAT Gateway per availability zone
If one_nat_gateway_per_az = true and single_nat_gateway = false, then the module will place one NAT gateway in each availability zone you specify in var.azs. There are some requirements around using this feature flag:
- The variable
var.azsmust be specified. - The number of public subnet CIDR blocks specified in
public_subnetsmust be greater than or equal to the number of availability zones specified invar.azs. This is to ensure that each NAT Gateway has a dedicated public subnet to deploy to.
"private" versus "intra" subnets
By default, if NAT Gateways are enabled, private subnets will be configured with routes for Internet traffic that point at the NAT Gateways configured by use of the above options.
If you need private subnets that should have no Internet routing (in the sense of RFC1918 Category 1 subnets), intra_subnets should be specified. An example use case is configuration of AWS Lambda functions within a VPC, where AWS Lambda functions only need to pass traffic to internal resources or VPC endpoints for AWS services.
Since AWS Lambda functions allocate Elastic Network Interfaces in proportion to the traffic received (read more), it can be useful to allocate a large private subnet for such allocations, while keeping the traffic they generate entirely internal to the VPC.
Conditional creation
Prior to Terraform 0.13, you were unable to specify count in a module block. If you wish to toggle the creation of the module's resources in an older (pre 0.13) version of Terraform, you can use the create_vpc argument.
# This VPC will not be created
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
create_vpc = false
# ... omitted
}
Public access to RDS instances
Sometimes it is handy to have public access to RDS instances (it is not recommended for production) by specifying these arguments:
create_database_subnet_group = true
create_database_subnet_route_table = true
create_database_internet_gateway_route = true
enable_dns_hostnames = true
enable_dns_support = true
Network Access Control Lists (ACL or NACL)
This module can manage network ACL and rules. Once VPC is created, AWS creates the default network ACL, which can be controlled using this module (manage_default_network_acl = true).
Also, each type of subnet may have its own network ACL with custom rules per subnet. Eg, set public_dedicated_network_acl = true to use dedicated network ACL for the public subnets; set values of public_inbound_acl_rules and public_outbound_acl_rules to specify all the NACL rules you need to have on public subnets (see variables.tf for default values and structures).
By default, all subnets are associated with the default network ACL.
Public access to Redshift cluster
Sometimes it is handy to have public access to Redshift clusters (for example if you need to access it by Kinesis - VPC endpoint for Kinesis is not yet supported by Redshift) by specifying these arguments:
enable_public_redshift = true # <= By default Redshift subnets will be associated with the private route table
Transit Gateway (TGW) integration
It is possible to integrate this VPC module with terraform-aws-transit-gateway module which handles the creation of TGW resources and VPC attachments. See complete example there.
VPC CIDR from AWS IP Address Manager (IPAM)
It is possible to have your VPC CIDR assigned from an AWS IPAM Pool. However, In order to build subnets within this module Terraform must know subnet CIDRs to properly plan the amount of resources to build. Since CIDR is derived by IPAM by calling CreateVpc this is not possible within a module unless cidr is known ahead of time. You can get around this by "previewing" the CIDR and then using that as the subnet values.
Note: Due to race conditions with terraform plan, it is not possible to use ipv4_netmask_length or a pools allocation_default_netmask_length within this module. You must explicitly set the CIDRs for a pool to use.
# Find the pool RAM shared to your account
# Info on RAM sharing pools: https://docs.aws.amazon.com/vpc/latest/ipam/share-pool-ipam.html
data "aws_vpc_ipam_pool" "ipv4_example" {
filter {
name = "description"
values = ["*mypool*"]
}
filter {
name = "address-family"
values = ["ipv4"]
}
}
# Preview next CIDR from pool
data "aws_vpc_ipam_preview_next_cidr" "previewed_cidr" {
ipam_pool_id = data.aws_vpc_ipam_pool.ipv4_example.id
netmask_length = 24
}
data "aws_region" "current" {}
# Calculate subnet cidrs from previewed IPAM CIDR
locals {
partition = cidrsubnets(data.aws_vpc_ipam_preview_next_cidr.previewed_cidr.cidr, 2, 2)
private_subnets = cidrsubnets(local.partition[0], 2, 2)
public_subnets = cidrsubnets(local.partition[1], 2, 2)
azs = formatlist("${data.aws_region.current.name}%s", ["a", "b"])
}
module "vpc_cidr_from_ipam" {
source = "terraform-aws-modules/vpc/aws"
name = "vpc-cidr-from-ipam"
ipv4_ipam_pool_id = data.aws_vpc_ipam_pool.ipv4_example.id
azs = local.azs
cidr = data.aws_vpc_ipam_preview_next_cidr.previewed_cidr.cidr
private_subnets = local.private_subnets
public_subnets = local.public_subnets
}
Examples
- Block Public Access
- Complete VPC w/ VPC Endpoints
- VPC w/ Flow Log
- VPC using IPAM
- Dualstack IPv4/IPv6 VPC
- IPv6 only subnets VPC
- Manage Default VPC
- VPC w/ Network ACL
- VPC w/ Outpost
- VPC w/ secondary CIDR blocks
- VPC w/ unique route tables
- Simple VPC
Contributing
Report issues/questions/feature requests on in the issues section.
Full contributing guidelines are covered here.
Authors
Module is maintained by Anton Babenko with help from these awesome contributors.
License
Apache 2 Licensed. See LICENSE for full details.
Additional information for users from Russia and Belarus
- Russia has illegally annexed Crimea in 2014 and brought the war in Donbas followed by full-scale invasion of Ukraine in 2022.
- Russia has brought sorrow and devastations to millions of Ukrainians, killed thousands of innocent people, damaged thousands of buildings including critical infrastructure, caused ecocide by blowing up a dam, bombed theater in Mariupol that had "Children" marking on the ground, raped men and boys, deported children in the occupied territoris, and forced millions of people to flee.
- Putin khuylo!