Creating our VPC Module
How you divide up the VPC into subnets can vary quite a lot, and should be driven by your needs.
In our case, we'll build a VPC with 2 subnets per availability zone. One subnet will be a "private" subnet, and the second will be a "public" subnet.
The private subnets will only allow network traffic among servers and resources within the private network. They'll (eventually) use a NAT gateway so the servers can talk to the outside internet, without the outside internet being able to reach those servers.
The public subnets will be able to communicate through both the private network and the public network. Instances in public subnets are reachable from the outside (public) internet.
VPC IP Address Ranges
We'll use this tool to see how we can split up networks to see how we'll create a smaller VPC (using half the possible IP addresses we can possibly use), which can help with VPC peering in the future.
We can also use that tool to see how we'll split our VPC available IP addresses into subnets.
A VPC's possible IP address range (in our example) is 10.0.0.0/16
We can split that in half using /17
to create these 2 halves:
-
10.0.0.0/17
(10.0.0.1
-10.0.127.254
) -
10.0.128.0/17
(10.0.128.1
-10.0.255.254
)
Availability Zones
We'll create 2 subnets per AZ (one public, one private).
To see the availability zones that exist for your account in any given region, use the following command:
1aws --profile=cloudcasts ec2 describe-availability-zones --region us-east-2
For bonus points, you can use the --query flag (which uses jmespath under the hood) to return only the AZ names:
1aws --profile=cloudcasts ec2 describe-availability-zones \ 2 --region us-east-2 \ 3 --query 'AvailabilityZones[].ZoneName' 4 5# Output: 6[ 7 "us-east-2a", 8 "us-east-2b", 9 "us-east-2c"10]
The Module
The following shows the variables.tf
, main.tf
, and outputs.tf
files that we create for the new vpc
module.
File variables.tf
:
1variable "public_subnet_numbers" { 2 type = map(number) 3 4 description = "Map of AZ to a number that should be used for public subnets" 5 6 default = { 7 "us-east-2a" = 1 8 "us-east-2b" = 2 9 "us-east-2c" = 310 }11}12 13variable "private_subnet_numbers" {14 type = map(number)15 16 description = "Map of AZ to a number that should be used for private subnets"17 18 default = {19 "us-east-2a" = 420 "us-east-2b" = 521 "us-east-2c" = 622 }23}24 25variable "vpc_cidr" {26 type = string27 description = "The IP range to use for the VPC"28 default = "10.0.0.0/16"29}30 31variable "infra_env" {32 type = string33 description = "infrastructure environment"34}
File main.tf
:
1# Create a VPC for the region associated with the AZ 2resource "aws_vpc" "vpc" { 3 cidr_block = var.vpc_cidr 4 5 tags = { 6 Name = "cloudcasts-${var.infra_env}-vpc" 7 Project = "cloudcasts.io" 8 Environment = var.infra_env 9 ManagedBy = "terraform"10 }11}12 13# Create 1 public subnets for each AZ within the regional VPC14resource "aws_subnet" "public" {15 for_each = var.public_subnet_numbers16 17 vpc_id = aws_vpc.vpc.id18 availability_zone = each.key19 20 # 2,048 IP addresses each21 cidr_block = cidrsubnet(aws_vpc.vpc.cidr_block, 4, each.value)22 23 tags = {24 Name = "cloudcasts-${var.infra_env}-public-subnet"25 Project = "cloudcasts.io"26 Role = "public"27 Environment = var.infra_env28 ManagedBy = "terraform"29 Subnet = "${each.key}-${each.value}"30 }31}32 33# Create 1 private subnets for each AZ within the regional VPC34resource "aws_subnet" "private" {35 for_each = var.private_subnet_numbers36 37 vpc_id = aws_vpc.vpc.id38 availability_zone = each.key39 40 # 2,048 IP addresses each41 cidr_block = cidrsubnet(aws_vpc.vpc.cidr_block, 4, each.value)42 43 tags = {44 Name = "cloudcasts-${var.infra_env}-private-subnet"45 Project = "cloudcasts.io"46 Role = "private"47 Environment = var.infra_env48 ManagedBy = "terraform"49 Subnet = "${each.key}-${each.value}"50 }51}
File outputs.tf
:
1output "vpc_id" { 2 value = aws_vpc.vpc.id 3} 4 5output "vpc_cidr" { 6 value = aws_vpc.vpc.cidr_block 7} 8 9output "vpc_public_subnets" {10 # Result is a map of subnet id to cidr block, e.g.11 # { "subnet_1234" => "10.0.1.0/4", ...}12 value = {13 for subnet in aws_subnet.public :14 subnet.id => subnet.cidr_block15 }16}17 18output "vpc_private_subnets" {19 # Result is a map of subnet id to cidr block, e.g.20 # { "subnet_1234" => "10.0.1.0/4", ...}21 value = {22 for subnet in aws_subnet.private :23 subnet.id => subnet.cidr_block24 }25}
File cloudcasts.tf
:
Finally, we can put everything together and reference this module in our cloudcasts.tf
file.
1module "vpc" {2 source = "./modules/vpc"3 4 infra_env = var.infra_env5 6 # Note we are /17, not /167 # So we're only using half of the available8 vpc_cidr = "10.0.0.0/17"9}
Then we can init
and plan
to see our changes:
1terraform init2terraform plan -var-file variables.tfvars