Requirements:
- Terraform 1.14.6
- Trivy >= 0.68.2
Trivy can be installed via Homebrew on macOS with the command:
brew install aquasecurity/trivy/trivyA practical AWS VPC module with first-class support for hub-and-spoke topologies and centralized NAT — share one NAT gateway across many VPCs via a Transit Gateway instead of paying for one per VPC.
Most VPC modules stop at "make me a VPC." Red Network goes further: spoke VPCs can skip their own NAT gateway entirely and route outbound traffic through a hub VPC's shared NAT over a Transit Gateway. At roughly $32/month per NAT gateway, that difference adds up fast once you have more than one VPC.
- Public IP assignment is scoped to subnets you explicitly declare as
type = "public"; private subnets never auto-assign - VPC Flow Logs are available opt-in via
enable_flow_logs(CloudWatch, configurable retention and traffic type) — off by default to avoid imposing cost, on when you want the visibility - An S3 Gateway VPC Endpoint keeps S3 traffic off the public internet
- Scanned with Trivy and gitleaks on every commit; integration-tested with Terratest across baseline, hub-and-spoke, and public-only topologies
- VPC with DNS hostnames and resolution
- Flexible subnet layout via a single
subnetsmap (public, private, or both) - Internet Gateway provisioned automatically when public subnets exist
- NAT Gateway with Elastic IP for private subnet egress, or skip it entirely
with
create_nat_gateway = falsefor public-only VPCs (no NAT cost) - S3 Gateway VPC Endpoint with route table associations
- Optional Transit Gateway creation for hub-and-spoke topologies
- Optional Transit Gateway attachment with automatic private-subnet selection
- Cross-VPC routing via configurable CIDR-based Transit Gateway routes
- Centralized NAT — spoke VPCs route outbound traffic through a hub VPC's shared NAT via the Transit Gateway
- Opt-in VPC Flow Logs to CloudWatch
- Configurable tags with project and orchestrator metadata
A VPC with public and private subnets — see
examples/complete:
provider "aws" {
region = "us-east-1"
}
module "network" {
source = "RussellGilmore/red-network/aws"
project_name = "my-project"
vpc_name = "my-project-vpc"
vpc_cidr = "10.0.0.0/16"
subnets = {
public-1a = {
name = "public-1a"
cidr_block = "10.0.1.0/24"
availability_zone = "us-east-1a"
type = "public"
}
private-1a = {
name = "private-1a"
cidr_block = "10.0.11.0/24"
availability_zone = "us-east-1a"
type = "private"
}
}
}The hub owns the Transit Gateway and a NAT gateway; each spoke attaches to
the hub's TGW and sets use_centralized_nat = true to route its private
subnets' outbound traffic through the hub instead of provisioning its own
NAT. See examples/hub-and-spoke for the full
two-VPC configuration.
For bastion or build hosts that only need an Internet Gateway and no private
egress, set create_nat_gateway = false to skip the NAT gateway entirely.
See examples/public-only.
Set enable_flow_logs = true to capture flow logs to CloudWatch. The module
creates the log group, IAM role, and flow log for you; retention
(flow_logs_retention_days), traffic type (flow_logs_traffic_type), and
log group name (flow_logs_log_group_name) are all configurable.
| Name | Version |
|---|---|
| terraform | >= 1.15.0 |
| aws | >= 6.47.0 |
| Name | Version |
|---|---|
| aws | >= 6.47.0 |
No modules.
| Name | Type |
|---|---|
| aws_cloudwatch_log_group.flow_logs | resource |
| aws_ec2_transit_gateway.main | resource |
| aws_ec2_transit_gateway_vpc_attachment.main | resource |
| aws_eip.nat | resource |
| aws_flow_log.main | resource |
| aws_iam_role.flow_logs | resource |
| aws_iam_role_policy.flow_logs | resource |
| aws_internet_gateway.main | resource |
| aws_nat_gateway.main | resource |
| aws_route.private_centralized_nat | resource |
| aws_route.private_nat | resource |
| aws_route.private_tgw | resource |
| aws_route.public_internet | resource |
| aws_route.public_tgw | resource |
| aws_route_table.private | resource |
| aws_route_table.public | resource |
| aws_route_table_association.private | resource |
| aws_route_table_association.public | resource |
| aws_subnet.subnets | resource |
| aws_vpc.main | resource |
| aws_vpc_endpoint.s3 | resource |
| aws_vpc_endpoint_route_table_association.s3_private | resource |
| aws_vpc_endpoint_route_table_association.s3_public | resource |
| aws_region.current | data source |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| additional_tags | Additional tags to apply to the resources | map(string) |
{} |
no |
| attach_to_transit_gateway | Whether to attach this VPC to a Transit Gateway | bool |
false |
no |
| create_nat_gateway | Whether to create a NAT gateway for private subnet outbound internet access. Defaults to true (created when public subnets exist). Set to false for public-only VPCs (e.g. bastion or build hosts) that have no private subnets needing egress, avoiding the NAT gateway hourly + data processing cost. Ignored when use_centralized_nat is true. | bool |
true |
no |
| create_transit_gateway | Whether to create a new Transit Gateway | bool |
false |
no |
| enable_flow_logs | Enable VPC Flow Logs to CloudWatch. When true, the module creates a CloudWatch log group, an IAM role, and the flow log itself. | bool |
false |
no |
| flow_logs_log_group_name | Name of the CloudWatch log group for VPC flow logs. When empty, defaults to /aws/vpc-flow-logs/<vpc_name>. Only used when enable_flow_logs is true. | string |
"" |
no |
| flow_logs_retention_days | Retention period in days for the flow logs CloudWatch log group. Only used when enable_flow_logs is true. | number |
30 |
no |
| flow_logs_traffic_type | Type of traffic to capture in flow logs: ALL, ACCEPT, or REJECT. | string |
"ALL" |
no |
| project_name | Set the project name. | string |
n/a | yes |
| subnets | Map of subnets to create. Each subnet should specify name, cidr_block, availability_zone, and type (public/private) | map(object({ |
n/a | yes |
| transit_gateway_asn | Amazon side ASN for the Transit Gateway | number |
64512 |
no |
| transit_gateway_id | ID of an existing Transit Gateway to attach to (required if attach_to_transit_gateway is true and create_transit_gateway is false) | string |
"" |
no |
| transit_gateway_name | Name for the Transit Gateway (only used if create_transit_gateway is true) | string |
"" |
no |
| transit_gateway_routes | List of CIDR blocks to route through the Transit Gateway (e.g., other VPC CIDRs) | list(string) |
[] |
no |
| use_centralized_nat | If true, this VPC will NOT create its own NAT gateway. Instead, a default route (0.0.0.0/0) on private subnets will point to the Transit Gateway, expecting a hub VPC to provide NAT. Only applies when attach_to_transit_gateway is true. | bool |
false |
no |
| vpc_cidr | CIDR block for the VPC | string |
n/a | yes |
| vpc_name | Name of the VPC | string |
n/a | yes |
| Name | Description |
|---|---|
| has_public_subnets | Boolean indicating if the VPC has any public subnets |
| internet_gateway_id | ID of the Internet Gateway (if public subnets exist) |
| nat_gateway_id | ID of the NAT Gateway (if created — not present when using centralized NAT) |
| nat_gateway_public_ip | Public IP address of the NAT Gateway (if created — not present when using centralized NAT) |
| private_route_table_id | ID of the private route table |
| private_subnet_ids | List of private subnet IDs |
| public_route_table_id | ID of the public route table (if public subnets exist) |
| public_subnet_ids | List of public subnet IDs |
| s3_vpc_endpoint_id | ID of the S3 VPC Endpoint |
| s3_vpc_endpoint_prefix_list_id | Prefix list ID of the S3 VPC Endpoint (useful for security groups) |
| subnet_arns | Map of subnet names to their ARNs |
| subnet_availability_zones | Map of subnet names to their availability zones |
| subnet_cidrs | Map of subnet names to their CIDR blocks |
| subnet_ids | Map of subnet names to their IDs |
| transit_gateway_arn | ARN of the Transit Gateway (if created) |
| transit_gateway_attachment_id | ID of the Transit Gateway VPC Attachment (if attached) |
| transit_gateway_id | ID of the Transit Gateway (if created) |
| using_centralized_nat | Boolean indicating if this VPC uses centralized NAT via Transit Gateway |
| vpc_arn | The ARN of the VPC |
| vpc_cidr | The CIDR block of the VPC |
| vpc_id | The ID of the VPC |