Terraform module to create AWS VPC with 1-tier, 2-tier, or 3-tier architecture. Automatically calculates subnet CIDRs and distributes them across availability zones.
- Multi-tier architecture: 1-tier (public), 2-tier (public+private), 3-tier (public+private+database)
- Dynamic subnet calculation: Automatically calculates subnet CIDRs from VPC CIDR
- Flexible AZ selection: Use specific AZs or auto-select by count
- Consistent naming:
vpc-name-type-number-azconvention - Individual route tables: One route table per subnet for maximum flexibility
- Flexible NAT Gateway: Single NAT Gateway or one per AZ
- Public subnets with Internet Gateway
- Use case: Static websites, load balancers
- Public subnets with Internet Gateway
- Private subnets with NAT Gateway
- Use case: Web applications with backend services
- Public subnets with Internet Gateway
- Private subnets with NAT Gateway
- Database subnets (isolated)
- Use case: Full web applications with database
module "vpc" {
source = "./terraform-aws-vpc"
name = "static-site"
tier = 1
az_count = 1
}module "vpc" {
source = "./terraform-aws-vpc"
name = "my-app"
tier = 2
az_count = 2
tags = {
Environment = "prod"
Project = "my-app"
}
}module "vpc" {
source = "./terraform-aws-vpc"
name = "my-app"
tier = 3
azs = ["us-east-1a", "us-east-1b", "us-east-1c"]
vpc_cidr = "10.0.0.0/16"
tags = {
Environment = "prod"
}
}module "vpc" {
source = "./terraform-aws-vpc"
name = "my-app"
tier = 3
public_subnet_tags = {
Type = "public"
Role = "web"
}
private_subnet_tags = {
Type = "private"
Role = "app"
}
db_subnet_tags = {
Type = "database"
Role = "data"
}
}Subnets follow the pattern: {vpc-name}-{type}{number}-{az}
Examples:
my-app-public1-us-east-1amy-app-private2-us-east-1bmy-app-db3-us-east-1c
Subnets are automatically calculated from VPC CIDR:
VPC: 10.0.0.0/16 + subnet_newbits: 8 = /24 subnets
Public: 10.0.0.0/24, 10.0.1.0/24, 10.0.2.0/24
Private: 10.0.3.0/24, 10.0.4.0/24, 10.0.5.0/24
DB: 10.0.6.0/24, 10.0.7.0/24, 10.0.8.0/24
- Single NAT Gateway (default): Cost-effective, single point of failure
- NAT Gateway per AZ: Higher availability, higher cost
# Single NAT Gateway (default)
enable_nat_gateway_per_az = false
# One NAT Gateway per AZ
enable_nat_gateway_per_az = trueEach subnet gets its own route table for maximum flexibility:
- Public subnets: Route to Internet Gateway
- Private subnets: Route to NAT Gateway
- Database subnets: No internet routes (isolated)
For RDS, create a DB subnet group separately:
module "vpc" {
source = "./terraform-aws-vpc"
name = "my-app"
tier = 3
}
resource "aws_db_subnet_group" "main" {
name = "my-app-db-group"
subnet_ids = [for subnet in module.vpc.db_subnets : subnet.id]
}See examples/ directory for complete usage examples:
examples/1-tier/- Public subnets onlyexamples/2-tier/- Public + Private subnetsexamples/3-tier/- Public + Private + Database subnets
| Name | Version |
|---|---|
| terraform | >= 1.0 |
| aws | ~> 5.0 |
├── main.tf # Core VPC resources
├── variables.tf # Input variables
├── outputs.tf # Module outputs
├── locals.tf # Local calculations
├── versions.tf # Provider requirements
├── README.md # This documentation
└── examples/ # Usage examples
├── 1-tier/
├── 2-tier/
└── 3-tier/
| Name | Version |
|---|---|
| terraform | >= 1.3 |
| aws | >= 5.0, < 6.0 |
| Name | Version |
|---|---|
| aws | >= 5.0, < 6.0 |
No modules.
| Name | Type |
|---|---|
| aws_eip.nat | resource |
| aws_internet_gateway.main | resource |
| aws_nat_gateway.this | resource |
| aws_route_table.db | resource |
| aws_route_table.private | resource |
| aws_route_table.public | resource |
| aws_route_table_association.db | resource |
| aws_route_table_association.private | resource |
| aws_route_table_association.public | resource |
| aws_subnet.db | resource |
| aws_subnet.private | resource |
| aws_subnet.public | resource |
| aws_vpc.main | resource |
| aws_availability_zones.available | data source |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| az_count | Number of availability zones to use (ignored if azs is provided) | number |
2 |
no |
| azs | Availability zones to spread subnets across (if empty, uses all available AZs) | list(string) |
[] |
no |
| db_subnet_tags | Additional tags for database subnets | map(string) |
{} |
no |
| enable_nat_gateway_per_az | Create one NAT Gateway per AZ (true) or single NAT Gateway (false) | bool |
false |
no |
| name | Name prefix for resources | string |
n/a | yes |
| private_subnet_tags | Additional tags for private subnets | map(string) |
{} |
no |
| public_subnet_tags | Additional tags for public subnets | map(string) |
{} |
no |
| subnet_newbits | Number of additional bits for subnetting | number |
8 |
no |
| tags | n/a | map(string) |
{} |
no |
| tier | Number of tiers (1=public, 2=public+private, 3=public+private+db) | number |
n/a | yes |
| vpc_cidr | CIDR block for the VPC | string |
"10.0.0.0/16" |
no |
| Name | Description |
|---|---|
| db_subnets | n/a |
| private_subnets | n/a |
| public_subnets | n/a |
| vpc_id | n/a |