Larry Ludden - Enterprise Java Developer - AWS Cloud Architect

Back to home

AWS Basic VPC CloudFormation Template ( Yet another )

As I plan to publish more content and templates referring to AWS, most of which will need a VPC to exist in, I decided to post my VPC template that has been working well for me up to this point. My goal was to develop a common VPC structure that could be used a majority of the time and that would cover most of use cases I was to encounter:

AWS VPC CloudFormation Template Goals

  • Avoid overlapping CIDR ranges: Simplify creating multiple VPCs that don't conflict with each other.
  • Sufficient IP addresses per subnet: Ensure VPC subnets are sized for most typical workloads.
  • Support for multiple subnet types: Include transit, public, private-nat, and private subnets (details below).
  • Multi-AZ deployment: Ensure subnets in the VPC are distributed across Availability Zones for high availability.

Solution

For most scenarios, I found that splitting a Class C CIDR range (approximately 255 IPs) works well for creating the required subnets in the VPC. The CloudFormation template provisions eight subnets: four in one AZ and four in another. Here's the breakdown of subnet types and their purposes:

  • transit : 22 IPs ( 11 in each subnet ). This would get used only for services that connect the VPC to something else. Examples would be Transit Gateway attachments or for AWS Firewall endpoints
  • public : 54 IPs ( 27 in each subnet ). This would be for anything in the VPC that needs a public IP address. Examples would be Load Balancer endpoints or bastion hosts ( although a future article will show most bastion hosts might not need to be public anymore )
  • private-nat : 118 IPs ( 59 in each subnet ). This would be where most resources in the VPC would get created. Resources would not be available from the public internet; but would be able to get to the public internet using either an AWS NAT Gateway or an alternative NAT solution. Examples would be application servers, ECS clusters, etc.
  • private : 22 IPs ( 11 in each subnet ). This would be for resources that aren't available to the public internet and also don't need to get to the public internet. Examples would be resources like Amazon RDS databases or AWS ElasticCache instances or clusters 

The CloudFormation template exports IDs for the various resources that were created with the goal of them being used as inputs for other types of templates. Designed properly, this allows creating templates that can "build" on each other somewhat automatically.

Usage

The CloudFormation template takes 4 parameters:

  • Environment : An ID that can differentiates the the potential various environments that might be needed. Current this is just used an identifier. Some examples would be sandbox, preprod, prod, etc.  
  • Application : An ID that will differentiate the resources of the VPC if there are multiple VPCs to be created. Otherwise, the value can just be left as default.
  • PrefixCidr : The first 3 octets of the Class C CIDR range that is to be used for the VPC. For example: 192.168.100
  • CreateNatGateway : A boolean value that determines if a NAT Gateway will get created and configured to allow resources in the private-nat subnet to get to the internet. The NAT Gateway has a substantial monthly cost ( roughly $35 ) which is why there's a parameter to enable it's creation. There are other NAT solutions you can use that offer similar functionality but more cost effective (like this one : Spot NAT Instances - A cheaper AWS NAT Gateway alternative

The CloudFormation template leverages AWS's GetAZs function to ensure subnets are distributed across the first two AZs of your region. It also creates foundational components like the Internet Gateway and route tables, and exports resource IDs for downstream use in other templates.

The template will export values that can be used as inputs to other CloudFormation templates. The values that are exported as below:

  • VpcId : The id for the VPC that gets created: ${Application}-vpcId
  • VpcCidr : The CIDR range that was used for the VPC : ${Application}-vpcCidr
  • Various subnet IDs :
    • ${Application}-az1PublicSubnetId
    • ${Application}-az1PrivateNatSubnetId
    • ${Application}-az1PrivateSubnetId
    • ${Application}-az2PublicSubnetId
    • ${Application}-az2PrivateNatSubnetId
    • ${Application}-az2PrivateSubnetId

Conclusion and Next Steps

Thanks for getting this far : ). Please reach out via the comments or GitHub issues if there are any questions, issues or suggestions. There are a lot of ways to go with VPCs and their structure and it would be great to get feedback. 

For more details and the template itself, check out my GitHub:
https://github.com/larryjkl/public/tree/main/basic-vpc

 

Articles