In this blog, you will learn to create AWS VPC using well structure terraform modules. It is a step by step guide for beginners with detailed information.
Prerequisites
To follow this guide you need to have the following.
- The latest Terraform binary is installed and configured in your system.
- AWS CLI installed and configured with a Valid AWS account with full permissions to create and manage AWS VPC service.
- If you are using terraform on an ec2 instance, ensure you have a valid IAM role attached to the instance with VPC provisioning permissions.
Terraform AWS VPC Code Repository
AWS VPC terraform code is part of the terraform AWS repository. Clone it to your workstation to follow the guide.
git clone https://github.com/techiescamp/terraform-aws.git
Fork and clone the repository if you intend to reuse and make changes as per your requirements.
Terraform AWS VPC Creation Workflow
The VPC terraform code is structured in the following way.
├── infra
│ └── vpc
│ ├── main.tf
│ └── variables.tf
├── modules
│ └── vpc
│ ├── endpoint.tf
│ ├── internet-gateway.tf
│ ├── nacl.tf
│ ├── nat-gateway.tf
│ ├── outputs.tf
│ ├── route-tables.tf
│ ├── subnet.tf
│ ├── variables.tf
│ └── vpc.tf
└── vars
└── dev
└── vpc.tfvars
vars
folder contains the variables file named vpc.tfvars
. It is the only file that needs modification
The modules/vpc
folder contains the following VPC related resources. All the resource provisioning logic is part of these resources.
- endpoint
- internet-gateway
- nacl
- nat-gateway
- route-tables
- subnet
- vpc
The infra/vpc/main.tf
file calls all the vpc
module with all the VPC resources using the variables we pass using the vpc.tfvars
file
Create VPC Using Terraform
Note: The VPC and subnets for this demo is created based on our VPC design document. Please refer it if you want to understand how to design a VPC.
We will be creating the VPC with the following
- CIDR Block: 10.0.0.0/16
- Region: us-west-2
- Availability Zones: us-west-2a, us-west-2b, us-west-2c
- Subnets: 15 Subnets (One per availability Zone)
- Public Sunets (3)
- App Subets (3)
- DB Subnets (3)
- Management Subnet (3)
- Platform Subnet (3)
- NAT Gateway for Private subnets
- Internet Gateway for public subnets.
- Enabled Endpoints: s3, Cloudwatch & Secrets Manager
- Dedicated NACLs for 4 set of subnets.
Follow the steps give below to create VPC using Terraform code.
Step 1: CD in the Cloned Repository
If you haven’t already cloned the repo, clone it using the following command.
git clone https://github.com/techiescamp/terraform-aws.git
Then cd in to the terraform-aws folder
cd terraform-aws
Step 2: Modify the vpc.tfvars
Note: Open the repository folder in your favorite IDE, as it makes editing and reviewing the code easier.
Modify the vars/dev/vpc.tfvars
file as per your requirements. I have highlighted the key parameters in bold.
If you dont want the NAT gateway, set create_nat_gateway
to false.
#vpc
region = "us-west-2"
vpc_cidr_block = "10.0.0.0/16"
instance_tenancy = "default"
enable_dns_support = true
enable_dns_hostnames = true
#elastic ip
domain = "vpc"
#nat-gateway
create_nat_gateway = true
#route-table
destination_cidr_block = "0.0.0.0/0"
#tags
owner = "techiescamp"
environment = "dev"
cost_center = "techiescamp-commerce"
application = "ecommerce"
#subnet
map_public_ip_on_launch = true
public_subnet_cidr_blocks = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
app_subnet_cidr_blocks = ["10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"]
db_subnet_cidr_blocks = ["10.0.7.0/24", "10.0.8.0/24", "10.0.9.0/24"]
management_subnet_cidr_blocks = ["10.0.10.0/24", "10.0.11.0/24", "10.0.12.0/24"]
platform_subnet_cidr_blocks = ["10.0.13.0/24", "10.0.14.0/24", "10.0.15.0/24"]
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
#public nacl
ingress_public_nacl_rule_no = [100]
ingress_public_nacl_action = ["allow"]
ingress_public_nacl_from_port = [0]
ingress_public_nacl_to_port = [0]
ingress_public_nacl_protocol = ["-1"]
ingress_public_nacl_cidr_block = ["0.0.0.0/0"]
egress_public_nacl_rule_no = [200]
egress_public_nacl_action = ["allow"]
egress_public_nacl_from_port = [0]
egress_public_nacl_to_port = [0]
egress_public_nacl_protocol = ["-1"]
egress_public_nacl_cidr_block = ["0.0.0.0/0"]
#app nacl
ingress_app_nacl_rule_no = [100]
ingress_app_nacl_action = ["allow"]
ingress_app_nacl_from_port = [0]
ingress_app_nacl_to_port = [0]
ingress_app_nacl_protocol = ["-1"]
ingress_app_nacl_cidr_block = ["0.0.0.0/0"]
egress_app_nacl_rule_no = [200]
egress_app_nacl_action = ["allow"]
egress_app_nacl_from_port = [0]
egress_app_nacl_to_port = [0]
egress_app_nacl_protocol = ["-1"]
egress_app_nacl_cidr_block = ["0.0.0.0/0"]
##db nacl
ingress_db_nacl_rule_no = [100]
ingress_db_nacl_action = ["allow"]
ingress_db_nacl_from_port = [0]
ingress_db_nacl_to_port = [0]
ingress_db_nacl_protocol = ["-1"]
ingress_db_nacl_cidr_block = ["0.0.0.0/0"]
egress_db_nacl_rule_no = [200]
egress_db_nacl_action = ["allow"]
egress_db_nacl_from_port = [0]
egress_db_nacl_to_port = [0]
egress_db_nacl_protocol = ["-1"]
egress_db_nacl_cidr_block = ["0.0.0.0/0"]
##management nacl
ingress_management_nacl_rule_no = [100]
ingress_management_nacl_action = ["allow"]
ingress_management_nacl_from_port = [0]
ingress_management_nacl_to_port = [0]
ingress_management_nacl_protocol = ["-1"]
ingress_management_nacl_cidr_block = ["0.0.0.0/0"]
egress_management_nacl_rule_no = [200]
egress_management_nacl_action = ["allow"]
egress_management_nacl_from_port = [0]
egress_management_nacl_to_port = [0]
egress_management_nacl_protocol = ["-1"]
egress_management_nacl_cidr_block = ["0.0.0.0/0"]
#platform nacl
ingress_platform_nacl_rule_no = [100]
ingress_platform_nacl_action = ["allow"]
ingress_platform_nacl_from_port = [0]
ingress_platform_nacl_to_port = [0]
ingress_platform_nacl_protocol = ["-1"]
ingress_platform_nacl_cidr_block = ["0.0.0.0/0"]
egress_platform_nacl_rule_no = [200]
egress_platform_nacl_action = ["allow"]
egress_platform_nacl_from_port = [0]
egress_platform_nacl_to_port = [0]
egress_platform_nacl_protocol = ["-1"]
egress_platform_nacl_cidr_block = ["0.0.0.0/0"]
#endpoint
create_s3_endpoint = true
create_secrets_manager_endpoint = true
create_cloudwatch_logs_endpoint = true
Step 2: Initialize Terraform and Execute the Plan
Now cd in to infra/vpc folder and execute the terraform plan to validate the configurations.
cd infra/vpc
Initialize Terraform
terraform init
Execute the plan
terraform plan -var-file=../../vars/dev/vpc.tfvars
You should see an output with all the resources that will be created by terraform.
Step 3: Create VPC With Terraform Apply
Lets create the VPC and related resources using terraform apply.
terraform apply -var-file=../../vars/dev/vpc.tfvars
Step 4: Validate VPC
Head over to the AWS Console the check the Resource Map of the VPC.
Click on the created VPC and scroll down to view the Resource Map.
You should see 15 subnets , 6 route tables, internet gateway and NAT gateway as shown beow.
Step 5: Cleanup the Resources
If you have created the VPC for learning purposes and wish to clean up the resources created by Terraform, execute the following command
terraform destroy -var-file=../../vars/dev/vpc.tfvars
Real World Terraform Code Reference
If you want to understand how Terraform code is structured and maintained in a real-world project environment, you should refer to the modernisation platform repository.
It contains code and standards that are actually used in the infrastructure management of the UK Ministry of Justice platform.
Refer to the architecture decision document to understand the reasoning behind each design.
https://github.com/ministryofjustice/modernisation-platform
Conclusion
In this guide, we looked at creating aws VPC using terraform.
When implemented in real projects, you may need to consider more VPC options, like flow logs, peering connections, etc.
For state management, you should use remote backend with s3 with dynamoDB lock.
Also, tweak the Terraform code according to your requirements. If you want to gain more knowledge, go through each resource under the VPC module
If you face any errors or have any queries or suggestions, drop a comment below.