- Usage Examples
- Subnet Types
- Subnet Availability Zones
- PRIVATE Subnet Route Table Associations
- NAT Gateways
- VPC Peering
- Non-Configurable VPC-Default Resources
- AWS Service CIDR Blocks
- ⚙️ Module Usage
- 📝 License
- 💬 Contact
- Terragrunt: view Terragrunt usage exmaple
- Terraform: view vanilla Terraform usage exmaple
This module categorizes subnets into the three distinct types: PUBLIC, PRIVATE, and INTRA-ONLY.
These "Subnet-Type" categories are based on the target of a subnet's default route (0.0.0.0/0).
Subnet type |
Default Route Target | Public Ingress | Public Egress | Use Case Examples |
|---|---|---|---|---|
PUBLIC |
Internet Gateway | ✔️ | ✔️ | • Public-facing workloads • Load balancers • NAT gateways |
PRIVATE |
NAT Gateway | ❌ | ✔️ | • Non-public-facing workloads • Service layer components • Databases |
INTRA-ONLY |
None | ❌ | ❌ | • Workloads that don't require internet access • Privacy-critical workloads • Peered ops-management subnets |
Every subnet must be provided with a type value in the var.subnets input variable. This module uses subnet type values to automatically generate network ACLs and route tables with common rules and routes which reflect the subnet types you provide. These resources are referred to as Subnet-Type resources, all of which can be flexibly customized, disabled, or replaced.
All Subnet-Type resources are shown below. As documented in the relevant input variables, network ACLs and route tables don't have names in AWS, but this module requires user-provided names for both in order to facilitate a non-index-based organization of resource configs which implements sensible defaults without negatively impacting flexibility. Subnet-Type resources are not created for any types which are not included in the user's var.subnets inputs.
Subnet type |
Subnet-Type Network ACL |
Subnet-Type Route Table |
Notes |
|---|---|---|---|
PUBLIC |
Public_Subnets_NACL | Public_Subnets_RouteTable | |
PRIVATE |
Private_Subnets_NACL | PUBLIC subnet CIDR | PRIVATE subnet route table names = the CIDR of the PUBLIC subnet which contains the NAT gateway used as the default route target. |
INTRA-ONLY |
IntraOnly_Subnets_NACL | IntraOnly_Subnets_RouteTable | Custom rules/routes can not be added to INTRA-ONLY resources. You can, however, add tags. |
TO CUSTOMIZE any module-provided Subnet-Type resource, simply use its name as a key in the relevant input variable (var.network_acls or var.route_tables), and the rules/routes you provide will be merged into its configuration. User-provided inputs are given precedence, so you can add your own configs or override existing ones.
Do note, however, the resource configs listed below which can not be overridden:
- Route table default routes (0.0.0.0/0) are always defined by the
typeof subnets associated with it and therefore can not be customized. - INTRA-ONLY resources can not be customized, as subnets of this type are intended to be entirely closed systems which permit neither ingress nor egress traffic. If you find yourself wanting to add rules/routes to INTRA-ONLY resources, instead either switch the subnet type to PRIVATE and customize the PRIVATE resources, or create your own entirely custom NACLs/route tables.
TO DISABLE any module-provided Subnet-Type resource, simply create your own custom NACLs/route tables in var.network_acls/var.route_tables, and have every subnet of the relevant type configured to use your custom resource using the network_acl/route_table properties in var.subnets config objects.
-
Ingress:
Rule Num Protocol CIDR Block From Port To Port Description 100 tcp 0.0.0.0/0 80 80 HTTP from anywhere 200 tcp 0.0.0.0/0 443 443 HTTPS from anywhere 500 tcp 0.0.0.0/0 1024 65535 Ephemeral ports -
Egress:
Rule Num Protocol CIDR Block From Port To Port Description 100 tcp 0.0.0.0/0 80 80 HTTP to anywhere 200 tcp 0.0.0.0/0 443 443 HTTPS to anywhere 500 tcp 0.0.0.0/0 1024 65535 Ephemeral ports
-
Ingress:
Rule Num Protocol CIDR Block From Port To Port Description 500 tcp 0.0.0.0/0 1024 65535 Ephemeral ports -
Egress:
Rule Num Protocol CIDR Block From Port To Port Description 100 tcp 0.0.0.0/0 80 80 HTTP to anywhere 200 tcp 0.0.0.0/0 443 443 HTTPS to anywhere 500 tcp 0.0.0.0/0 1024 65535 Ephemeral ports
-
Ingress:
Rule Num Protocol CIDR Block From Port To Port Description - - - - - Ingress not permitted -
Egress:
Rule Num Protocol CIDR Block From Port To Port Description - - - - - Egress not permitted
For each subnet, a valid availability_zone must be specified.
PRIVATE Subnets: Same-AZ NAT Gateway Required
In each availability zone used by your PRIVATE subnets, there must also be at least one PUBLIC subnet configured with contains_nat_gateway = true.
This module does not support the creation of PRIVATE subnets with default routes pointing to NAT gateways in different availability zones, for two reasons:
- Cross-AZ NAT incurs a slight latency hit
- Such a design undermines high-availability
As described in the NAT Gateways section, all NAT gateways have a 1:1 relationship with a corresponding route table - we'll refer to these here as "NAT route tables".
To explicitly associate a PRIVATE subnet with any particular NAT route table, simply set the PRIVATE subnet's route_table property in var.subnets to the CIDR of the PUBLIC subnet which contains the NAT gateway of your choosing.
Any PRIVATE subnets which are not explicitly associated with a NAT are evenly distributed among the NAT route tables within their availability zone.
This module creates one NAT gateway for each PUBLIC subnet configured with contains_nat_gateway = true. This module identifies NAT gateways and their associated resources (below) by the CIDR of the PUBLIC subnet in which they're placed. For each NAT gateway, the following resources are also created:
- 1x elastic IP address
- 1x route table, for which the NAT gateway is the default route
As mentioned in the section on availability zones, there must be at least one PUBLIC subnet configured with contains_nat_gateway = true in each AZ used by your PRIVATE subnets.
There are two input variables which can be used to configure VPC peering connections:
peering_request_vpc_idsA map of peer VPC IDs to peering connection config objects; use this variable for peering connections in which your VPC is the requester VPC.peering_accept_connection_idsA map of VPC peering connection IDs to peering connection config objects; use this property for peering connections in which your VPC is the accepter VPC.
For both the requester and accepter VPCs, remote VPC DNS resolution (whereby public IPs map to private DNS hostnames) is enabled by default. Set allow_remote_vpc_dns_resolution to false in either VPC to disable this feature.
VPC Peering: Connection Auto-Accept
If the requester and accepter VPCs are both in the same region and owned by the same account, the peering connection will be configured to auto-accept, thereby streamlining the connection setup process:
- the requester VPC does not have to provide the peer VPC's region nor account ID
- the accepter VPC does not have to manually accept the connection request, unless it's desirable to disable remote VPC DNS resolution
VPC Peering: Required Subnet-Level Configs
Once a VPC peering connection has been established, the following subnet-level resource configs will need to be in place before peering connection traffic can proceed:
- To permit INGRESS traffic, a subnet within the VPC which is the target of a request must have a network ACL with at least one rule allowing the request using the sending VPC's CIDR (or a subset thereof). The NACL rule must also specify the correct protocol and port-range, which will of course depend on the nature of the request.
- To permit EGRESS traffic, a subnet within the VPC from which the request originates must have a route table with at least one route configured with the peering connection ID using the receiving VPC's CIDR (or a subset thereof).
AWS does not permit transitive routing through peering connections
For more info, see Unsupported VPC Peering Configurations.
To see how this module is used to configure VPC peering, check out this usage example.
In accordance with network security best practices, this module brings each VPC's default resources (listed below) under management, and locks them down by implementing configurations that deny all traffic which may otherwise be implicitly allowed. This behavior cannot be overridden, as it ensures that the only network traffic throughout the VPC is that which has been explicitly allowed.
🔒 Locked-Down Resources:
- Default Route Table
- Default Network ACL
- Default Security Group
For both Security Group and Network ACL rule config objects, if the CIDR target is one of the supported AWS Services listed in the table below, you can provide the service's enum value to the rule's relevant property: aws_service for Security Group rules, and cidr_block for Network ACL rules. When provided, this module will perform the CIDR lookup for you using the region of the calling AWS Provider. CloudFront also offers "global" CIDRs, which can be obtained via the cloudfront_global enum value.
Most of the services supported by the underlying data source return multiple CIDR block values, which is fine for Security Group rules which accept multiple CIDRs without issue, but this is problematic for NACL rules since we can only provide a single CIDR for any given NACL rule's CIDR parameter. Therefore, the list of supported services for Network ACL rules is restricted at this time to only those which return a single value.
Supported Services:
| Service | Service Enum | Supported for Security Group Rules | Supported for NACL Rules |
|---|---|---|---|
| Amazon (amazon.com) | amazon | ✔️ | ❌ |
| Amazon Connect | amazon_connect | ✔️ | ❌ |
| API Gateway | api_gateway | ✔️ | ❌ |
| Cloud9 | cloud9 | ✔️ | ❌ |
| CloudFront | cloudfront | ✔️ | ❌ |
| CloudFront (global) | cloudfront_global | ✔️ | ❌ |
| CodeBuild | codebuild | ✔️ | ❌ |
| EC2 | ec2 | ✔️ | ❌ |
| EC2 Instance Connect | ec2_instance_connect | ✔️ | ✔️ |
| DynamoDB | dynamodb | ✔️ | ❌ |
| GlobalAccelerator | globalaccelerator | ✔️ | ✔️ |
| Route53 | route53 | ✔️ | ❌ |
| Route53 HealthChecks | route53_healthchecks | ✔️ | ❌ |
| S3 | s3 | ✔️ | ❌ |
| Workspaces Gateways | workspaces_gateways | ✔️ | ❌ |
Note: Network ACL Unsupported Services
To address the NACL CIDR param issues, the following approaches are under consideration:
- When multiple CIDRs are returned,
- The module could create an allow rule for each CIDR.
Easiest to implement, but implicit rule decision-making = less caller control. - Or rules could be created for just a subset of the CIDRs.
How should caller determine the subset? - Or a rule could be created for just one CIDR.
How should caller determine which one?
- The module could create an allow rule for each CIDR.
- When zero CIDRs are returned,
- We could allow the data block to cause the plan/apply operation to error out.
More errors, never ideal. - Or the NACL rule could be skipped.
Fewer errors, but again, implicit rule decision-making = less caller control.
- We could allow the data block to cause the plan/apply operation to error out.
- Terragrunt: view Terragrunt usage exmaple
- Terraform: view vanilla Terraform usage exmaple
| Name | Version |
|---|---|
| terraform | 1.3.2 |
| aws | ~> 4.34.0 |
| Name | Version |
|---|---|
| aws | ~> 4.34.0 |
No modules.
| Name | Type |
|---|---|
| aws_default_network_acl.this | resource |
| aws_default_route_table.this | resource |
| aws_default_security_group.this | resource |
| aws_eip.nat_gw_elastic_ips | resource |
| aws_internet_gateway.list | resource |
| aws_nat_gateway.map | resource |
| aws_network_acl.map | resource |
| aws_route_table.map | resource |
| aws_route_table_association.map | resource |
| aws_security_group.map | resource |
| aws_security_group_rule.map | resource |
| aws_subnet.map | resource |
| aws_vpc.this | resource |
| aws_vpc_endpoint.map | resource |
| aws_vpc_peering_connection.map | resource |
| aws_vpc_peering_connection_accepter.map | resource |
| aws_vpc_peering_connection_options.map | resource |
| aws_ip_ranges.map | data source |
| aws_region.current | data source |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| default_network_acl_tags | Tags for the VPC's default network ACL. | map(string) |
null |
no |
| default_route_table_tags | Tags for the VPC's default route table. | map(string) |
null |
no |
| default_security_group_tags | Tags for the VPC's default security group. | map(string) |
null |
no |
| internet_gateway_tags | Tags for the internet gateway. | map(string) |
null |
no |
| nat_gateway_elastic_ip_tags | Map of NAT-containing public subnet CIDRs to tags for each respective subnet's NAT-associated elastic IP address. |
map(map(string)) |
null |
no |
| nat_gateway_tags | Map of NAT-containing public subnet CIDRs to tags for each respective subnet's NAT gateway. |
map(map(string)) |
null |
no |
| network_acls | Map of network ACL names to config objects. Network ACLs do not have names in AWS, but the name values you provide will be used by this module to properly identify network ACLs for the purposes of rule customization and subnet association. "access.ingress" and "access.egress" map quoted rule numbers (e.g., "100") to objects configuring each respective rule. For each rule, if "from_port" and "to_port" are the same, you can simply provide just "port" which will be mapped to both. "protocol" defaults to "tcp" if not provided. |
map( |
{} |
no |
| peering_accept_connection_ids | (Optional) VPC Peering connection accepts config; use this variable for peering connections in which your VPC is the ACCEPTER VPC. Map peering connection IDs to config objects for each respective peering connection to accept. If the peering connection was configured for auto-acceptance, manual acceptance is not required to establish the connection. "allow_remote_vpc_dns_resolution" defaults to "true". For more info, see the VPC Peering section of the README. |
map( |
{} |
no |
| peering_request_vpc_ids | (Optional) VPC Peering connection requests config; use this variable for peering connections in which your VPC is the REQUESTER VPC. Map accepter VPC IDs to config objects for each respective peering connection request. "allow_remote_vpc_dns_resolution" defaults to "true". For more info, see the VPC Peering section of the README. |
map( |
{} |
no |
| route_tables | Map of route table names to route table config objects. Route tables do not have names in AWS, but the name values you provide will be used by this module to properly identify route tables for the purposes of route customization and subnet association. "routes" maps CIDRs of route destinations to objects configuring each respective route. Custom route tables for PRIVATE subnets can set their default route ("0.0.0.0/0") using "nat_gateway_subnet_cidr", which must be set to the CIDR of a NAT-containing PUBLIC subnet. Peering connection routes can be configured in one of two ways: if VPC is the peering REQUESTER, use "peering_request_vpc_id", otherwise if VPC is the peering ACCEPTER, use "peering_connection_id". |
map( |
{} |
no |
| security_groups | Map of Security Group names to config objects. For each ingress/egress rule, "protocol" defaults to "tcp" if not provided. If "from_port" and "to_port" are the same, you can provide just "port" which will be mapped to both. If a rule will use the CIDR block of an AWS service, you can provide an enum string to "aws_service" to have the lookup performed by the module; see the AWS Service CIDR Blocks section of the README for a list of supported services and their enum values. |
map( |
{} |
no |
| subnets | Map of subnet CIDRs to subnet config objects. For each subnet, "type" must be either "PUBLIC", "PRIVATE", or "INTRA-ONLY". The properties "map_public_ip_on_launch" and "contains_nat_gateway" both default to false, and have no effect on non-public subnets. Public and private subnets can be configured to use specific route tables and/or NACLs via the "route_table" and "network_acl" properties respectively; these have no effect on INTRA-ONLY subnets. |
map( |
n/a | yes |
| vpc | Config object for the VPC. The optional bools "enable_dns_support" and "enable_dns_hostnames" both default to "true". |
object({ |
n/a | yes |
| vpc_endpoints | Map of VPC Endpoint services to endpoint config objects. Service-keys are all normalized to lower-case and are therefore case-insensitive. A list of valid services is available at the link below. https://docs.aws.amazon.com/vpc/latest/privatelink/aws-services-privatelink-support.html "type" can be "Interface" (default), "Gateway", or "GatewayLoadBalancer". Gateway endpoints can only be created for the S3 and DynamoDB services. All "Gateway" and some "Interface" endpoints can provide "policy", which if provided must be a valid IAM policy formatted as a JSON string. If the endpoint and service are owned by the same account, "auto-accept" can be used to either enable or disable automatic acceptance of the connection. "enable_private_dns" is only applicable to "Interface" endpoints and defaults to true. "timeouts" are all optional and default to "10m" if not provided. Endpoint Resource Associations "Interface" and "GatewayLoadBalancer" endpoints must provide "subnet_cidrs", a list of subnet CIDRs in which to place the interface/GWLB. "Interface" endpoints must additionally provide "security_groups", a list of names of security groups which should be associated with the endpoint's interface. "Gateway" endpoints must specify "route_tables"; AWS will automatically add/remove routes to these route tables which connect the service's AWS-managed prefix-list to the gateway endpoint. |
map(object({ |
{} |
no |
| Name | Description |
|---|---|
| Default_NetworkACL | The VPC's default network ACL resource object. |
| Default_RouteTable | The VPC's default route table resource object. |
| Default_SecurityGroup | The VPC's default security group resource object. |
| Internet_Gateway | The internet gateway resource object. |
| NAT_Gateway_Elastic_IPs | Map of NAT gateway elastic IP address resource objects. |
| NAT_Gateways | Map of NAT gateway resource objects. |
| Network_ACLs | Map of network ACL resource objects. |
| RouteTables | Map of route table resource objects. |
| Security_Groups | Map of security group resource objects. |
| Subnets | Map of subnet resource objects merged with their respective input params. |
| VPC | The VPC resource object. |
| VPC_Endpoints | Map of VPC Endpoint resource objects. |
| VPC_Peering_Connection_Accepts | Map of VPC Peering Connection Accepter resource objects. |
| VPC_Peering_Connection_Options | Map of VPC Peering Connection Options resource objects. |
| VPC_Peering_Connection_Requests | Map of VPC Peering Connection resource objects. |
All scripts and source code contained herein are for commercial use only by Nerdware, LLC.
See LICENSE for more information.