Skip to content

digitalis-io/terraform-aws-vpc

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AWS VPC Module

This module can be used to deploy a pragmatic VPC with various subnets types in # AZs. Common deployment examples can be found in examples/. Subnet CIDRs can be explicitly set via list of string argument cidrs or set via a number netmask argument.

__Note: For information regarding the 4.0 upgrade see our upgrade guide.__

Usage

The example below builds a VPC with public and private subnets in 3 AZs. Each subnet calulates a CIDR based on the netmask argument passed. The public subnets build nat gateways in each AZ but optionally can be switched to single_az.

module "vpc" {
  source   = "aws-ia/vpc/aws"
  version = ">= 4.0.0"

  name       = "multi-az-vpc"
  cidr_block = "10.0.0.0/20"
  az_count   = 3

  subnets = {
    public = {
      name_prefix               = "my_public" # omit to prefix with "public"
      netmask                   = 24
      nat_gateway_configuration = "all_azs" # options: "single_az", "none"
    }

    private = {
      # omitting name_prefix defaults value to "private"
      # name_prefix  = "private_with_egress"
      netmask      = 24
      connect_to_public_natgw = true
    }
  }

  vpc_flow_logs = {
    log_destination_type = "cloud-watch-logs"
    retention_in_days    = 180
  }
}

Reserved Subnet Key Names

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.

transit_gateway_id = <>
transit_gateway_routes = {
  public = "pl-123"
  vpce   = "10.0.0.0/8"
}

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 = {
    netmask                                         = 28
    connect_to_public_natgw                         = 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"
}

subnets = {
  workload = {
    name_prefix = "workload-private"
    netmask     = 24
  }

  core_network = {
    netmask            = 28
    ipv6_support       = false
    require_acceptance = true
    accept_attachment  = true

    tags = {
      env = "prod"
    }
}

Updating a VPC with new or removed subnets

If using netmask 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 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"
az_count = 2

subnets = {
  public = {
   netmask = 24
  }

  private = {
   netmask = 24
  }
}

After:

cidr_block = "10.0.0.0/16"
az_count = 3

subnets = {
  public = {
   cidrs = ["10.0.0.0/24", "10.0.1.0/24", "10.0.4.0/24"]
  }

  private = {
   cidrs = ["10.0.2.0/24", "10.0.3.0/24", "10.0.5.0/24"]
  }
}

The above example will cause only creating 2 new subnets in az c of the region being used.

Output usage examples

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:

Extracting subnet IDs for private subnets

Example Configuration:

module "vpc" {
  source  = "aws-ia/vpc/aws"
  version = ">= 4.0.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
...

Common Errors and their Fixes

Error creating routes to Core Network

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 and accept_attachment to true.
subnets = {
  core_network = {
    netmaks            = 28
    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 to true and accept_attachment to false.
subnets = {
  core_network = {
    netmaks            = 28
    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 = {
    netmaks            = 28
    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.

Contributing

Please see our developer documentation for guidance on contributing to this module

Requirements

Name Version
terraform >= 1.3.0
aws >= 4.27.0

Providers

Name Version
aws >= 4.27.0

Modules

Name Source Version
calculate_subnets ./modules/calculate_subnets n/a
flow_logs ./modules/flow_logs n/a
subnet_tags aws-ia/label/aws 0.0.5
tags aws-ia/label/aws 0.0.5

Resources

Name Type
aws_ec2_transit_gateway_route_table_association.tgw resource
aws_ec2_transit_gateway_vpc_attachment.tgw resource
aws_eip.nat resource
aws_internet_gateway.main resource
aws_nat_gateway.main resource
aws_networkmanager_attachment_accepter.cwan resource
aws_networkmanager_vpc_attachment.cwan resource
aws_route.cwan_to_nat resource
aws_route.private_to_cwan resource
aws_route.private_to_nat resource
aws_route.private_to_tgw resource
aws_route.public_to_cwan resource
aws_route.public_to_igw resource
aws_route.public_to_tgw resource
aws_route.tgw_to_nat resource
aws_route_table.cwan resource
aws_route_table.private resource
aws_route_table.public resource
aws_route_table.tgw resource
aws_route_table_association.cwan resource
aws_route_table_association.private resource
aws_route_table_association.public resource
aws_route_table_association.tgw resource
aws_subnet.cwan resource
aws_subnet.private resource
aws_subnet.public resource
aws_subnet.tgw resource
aws_vpc.main resource
aws_vpc_ipv4_cidr_block_association.secondary resource
aws_availability_zones.current data source
aws_vpc.main data source

Inputs

Name Description Type Default Required
az_count Searches region for # of AZs to use and takes a slice based on count. Assume slice is sorted a-z. number n/a yes
name Name to give VPC. Note: does not effect subnet names, which get assigned name based on name_prefix. string n/a yes
subnets Configuration of subnets to build in VPC. 1 Subnet per AZ is created. Subnet types are defined as maps with the available keys: "private", "public", "transit_gateway". Each Subnet type offers its own set of available arguments detailed below.

Attributes shared across subnet types:
- cidrs = (Optional|list(string)) Cannot set if netmask is set. List of CIDRs to set to subnets. Count of CIDRs defined must match quatity of azs in az_count.
- netmask = (Optional|Int) Netmask of the var.cidr_block to calculate for each subnet. Cannot set if cidrs is set.
- name_prefix = (Optional|String) A string prefix to use for the name of your subnet and associated resources. Subnet type key name is used if omitted (aka private, public, transit_gateway). Example name_prefix = "private" for var.subnets.private is redundant.
- tags = (Optional|map(string)) Tags to set on the subnet and associated resources.

Any private subnet type options:
- All shared keys above
- connect_to_public_natgw = (Optional|string) Determines if routes to NAT Gateways should be created. Specify the CIDR range or a prefix-list-id that you want routed to nat gateway. Usually 0.0.0.0/0. Must also set var.subnets.public.nat_gateway_configuration.

public subnet type options:
- All shared keys above
- nat_gateway_configuration = (Optional|string) Determines if NAT Gateways should be created and in how many AZs. Valid values = "none", "single_az", "all_azs". Default = "none". Must also set var.subnets.private.connect_to_public_natgw = true.

transit_gateway subnet type options:
- All shared keys above
- connect_to_public_natgw = (Optional|string) Determines if routes to NAT Gateways should be created. Specify the CIDR range or a prefix-list-id that you want routed to nat gateway. Usually 0.0.0.0/0. Must also set var.subnets.public.nat_gateway_configuration.
- transit_gateway_default_route_table_association = (Optional|bool) Boolean whether the VPC Attachment should be associated with the EC2 Transit Gateway association default route table. This cannot be configured or perform drift detection with Resource Access Manager shared EC2 Transit Gateways.
- transit_gateway_default_route_table_propagation = (Optional|bool) Boolean whether the VPC Attachment should propagate routes with the EC2 Transit Gateway propagation default route table. This cannot be configured or perform drift detection with Resource Access Manager shared EC2 Transit Gateways.
- transit_gateway_appliance_mode_support = (Optional|string) Whether Appliance Mode is enabled. If enabled, a traffic flow between a source and a destination uses the same Availability Zone for the VPC attachment for the lifetime of that flow. Valid values: disable (default) and enable.
- transit_gateway_dns_support = (Optional|string) DNS Support is used if you need the VPC to resolve public IPv4 DNS host names to private IPv4 addresses when queried from instances in another VPC attached to the transit gateway. Valid values: enable (default) and disable.

core_network subnet type options:
- All shared keys abovce
- connect_to_public_natgw = (Optional|string) Determines if routes to NAT Gateways should be created. Specify the CIDR range or a prefix-list-id that you want routed to nat gateway. Usually 0.0.0.0/0. Must also set var.subnets.public.nat_gateway_configuration.
- ipv6_support = (Optional|bool) Boolean whether IPv6 is supported or not in the Cloud WAN's VPC attachment. Default to false.
- require_acceptance = (Optional|bool) Boolean whether the core network VPC attachment to create requires acceptance or not. Defaults to false.
- accept_attachment = (Optional|bool) Boolean whether the core network VPC attachment is accepted or not in the segment. Only valid if require_acceptance is set to true. Defaults to true.

Example:
subnets = {
public = {
netmask = 24
nat_gateway_configuration = "single_az"
}

private = {
netmask = 24
connect_to_public_natgw = true
}

transit_gateway = {
netmask = 24
connect_to_public_natgw = true
transit_gateway_default_route_table_association = true
transit_gateway_default_route_table_propagation = true
}

core_network = {
netmask = 24
connect_to_public_natgw = true
ipv6_support = true
require_acceptance = true
accept_attachment = true
}
}
any n/a yes
cidr_block CIDR range to assign to VPC if creating VPC or to associte as a secondary CIDR. Overridden by var.vpc_id output from data.aws_vpc. string null no
core_network AWS Cloud WAN's core network information - to create a VPC attachment. Required when cloud_wan subnet is defined. Two attributes are required: the id and arn of the resource.
object({
id = string
arn = string
})
{
"arn": null,
"id": null
}
no
core_network_routes Configuration of route(s) to AWS Cloud WAN's core network.
For each public and/or private subnets named in the subnets variable, optionally create routes from the subnet to the core network.
You can specify either a CIDR range or a prefix-list-id that you want routed to the core network.
Example:
core_network_routes = {
public = "10.0.0.0/8"
private = "pl-123"
}
any {} no
tags Tags to apply to all resources. map(string) {} no
transit_gateway_id Transit gateway id to attach the VPC to. Required when transit_gateway subnet is defined. string null no
transit_gateway_routes Configuration of route(s) to transit gateway.
For each public and/or private subnets named in the subnets variable,
Optionally create routes from the subnet to transit gateway. Specify the CIDR range or a prefix-list-id that you want routed to the transit gateway.
Example:
transit_gateway_routes = {
public = "10.0.0.0/8"
private = "pl-123"
}
any {} no
vpc_enable_dns_hostnames Indicates whether the instances launched in the VPC get DNS hostnames. If enabled, instances in the VPC get DNS hostnames; otherwise, they do not. Disabled by default for nondefault VPCs. bool true no
vpc_enable_dns_support Indicates whether the DNS resolution is supported for the VPC. If enabled, queries to the Amazon provided DNS server at the 169.254.169.253 IP address, or the reserved IP address at the base of the VPC network range "plus two" succeed. If disabled, the Amazon provided DNS service in the VPC that resolves public DNS hostnames to IP addresses is not enabled. Enabled by default. bool true no
vpc_flow_logs Whether or not to create VPC flow logs and which type. Options: "cloudwatch", "s3", "none". By default creates flow logs to cloudwatch. Variable overrides null value types for some keys, defined in defaults.tf.
object({
log_destination = optional(string)
iam_role_arn = optional(string)
kms_key_id = optional(string)

log_destination_type = string
retention_in_days = optional(number)
tags = optional(map(string))
traffic_type = optional(string, "ALL")
destination_options = optional(object({
file_format = optional(string, "plain-text")
hive_compatible_partitions = optional(bool, false)
per_hour_partition = optional(bool, false)
}))
})
{
"log_destination_type": "none"
}
no
vpc_id VPC ID to use if not creating VPC. string null no
vpc_instance_tenancy The allowed tenancy of instances launched into the VPC. string "default" no
vpc_ipv4_ipam_pool_id Set to use IPAM to get CIDR block. string null no
vpc_ipv4_netmask_length Set to use IPAM to get CIDR block using a specified netmask. Must be set with var.vpc_ipv4_ipam_pool_id. string null no
vpc_secondary_cidr If true the module will create a aws_vpc_ipv4_cidr_block_association and subnets for that secondary cidr. If using IPAM for both primary and secondary CIDRs, you may only call this module serially (aka using -target, etc). bool false no
vpc_secondary_cidr_natgw If attaching a secondary CIDR instead of creating a VPC, you can map private/ tgw subnets to your public NAT GW with this argument. Simply pass the output nat_gateway_attributes_by_az, ex: vpc_secondary_cidr_natgw = module.vpc.natgw_id_per_az. If you did not build your primary with this module, you must construct a map { az : { id : nat-123asdb }} for each az. any {} no

Outputs

Name Description
azs List of AZs where subnets are created.
core_network_attachment AWS Cloud WAN's core network attachment. Full output of aws_networkmanager_vpc_attachment.
core_network_subnet_attributes_by_az Map of all core_network subnets containing their attributes.

Example:
core_network_subnet_attributes = {
"us-east-1a" = {
"arn" = "arn:aws:ec2:us-east-1:<>:subnet/subnet-04a86315c4839b519"
"assign_ipv6_address_on_creation" = false
...
<all attributes of subnet: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet#attributes-reference>
}
"us-east-1b" = {...)
}
nat_gateway_attributes_by_az Map of nat gateway resource attributes by AZ.

Example:
nat_gateway_attributes_by_az = {
"us-east-1a" = {
"allocation_id" = "eipalloc-0e8b20303eea88b13"
"connectivity_type" = "public"
"id" = "nat-0fde39f9550f4abb5"
"network_interface_id" = "eni-0d422727088bf9a86"
"private_ip" = "10.0.3.40"
"public_ip" = <>
"subnet_id" = "subnet-0f11c92e439c8ab4a"
"tags" = tomap({
"Name" = "nat-my-public-us-east-1a"
})
"tags_all" = tomap({
"Name" = "nat-my-public-us-east-1a"
})
}
"us-east-1b" = { ... }
}
natgw_id_per_az Map of nat gateway IDs for each resource. Will be duplicate ids if your var.subnets.public.nat_gateway_configuration = "single_az".

Example:
natgw_id_per_az = {
"us-east-1a" = {
"id" = "nat-0fde39f9550f4abb5"
}
"us-east-1b" = {
"id" = "nat-0fde39f9550f4abb5"
}
}
private_subnet_attributes_by_az Map of all private subnets containing their attributes.

Example:
private_subnet_attributes = {
"private/us-east-1a" = {
"arn" = "arn:aws:ec2:us-east-1:<>:subnet/subnet-04a86315c4839b519"
"assign_ipv6_address_on_creation" = false
...
<all attributes of subnet: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet#attributes-reference>
}
"us-east-1b" = {...)
}
public_subnet_attributes_by_az Map of all public subnets containing their attributes.

Example:
public_subnet_attributes = {
"us-east-1a" = {
"arn" = "arn:aws:ec2:us-east-1:<>:subnet/subnet-04a86315c4839b519"
"assign_ipv6_address_on_creation" = false
...
<all attributes of subnet: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet#attributes-reference>
}
"us-east-1b" = {...)
}
rt_attributes_by_type_by_az Map of route tables by type => az => route table attributes. Example usage: module.vpc.route_table_by_subnet_type.private.id

Example:
route_table_attributes_by_type_by_az = {
"private" = {
"us-east-1a" = {
"id" = "rtb-0e77040c0598df003"
"route_table_id" = "rtb-0e77040c0598df003"
"tags" = tolist([
{
"key" = "Name"
"value" = "private-us-east-1a"
},
])
"vpc_id" = "vpc-033e054f49409592a"
}
"us-east-1b" = { ... }
"public" = { ... }
tgw_subnet_attributes_by_az Map of all tgw subnets containing their attributes.

Example:
tgw_subnet_attributes = {
"us-east-1a" = {
"arn" = "arn:aws:ec2:us-east-1:<>:subnet/subnet-04a86315c4839b519"
"assign_ipv6_address_on_creation" = false
...
<all attributes of subnet: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet#attributes-reference>
}
"us-east-1b" = {...)
}
transit_gateway_attachment_id Transit gateway attachment id.
vpc_attributes VPC resource attributes. Full output of aws_vpc.

Releases

No releases published

Packages

No packages published

Languages

  • HCL 91.6%
  • Go 6.6%
  • Python 1.1%
  • Jinja 0.7%