It's one of the most important things that everyone need to build VPC after applied a new aws account. The purpose of this blog is to help you build it using Terraform. All the codes have been put in github.
Design for networking
- Public subnet: Deploying websites can be accessed directly by the Internet
- Private subnet: Deploying applications only can be accessed by the websites in Public subnet. Also, the application can access to the Internet through NAT Gateway
How to build
Before creating a VPC, we need to install TerraForm first.
Code structure
infrastructure
├── module
│ └── networking
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── region
│ └── virginia
│ ├── main.tf
│ └── providers.tf
└── setup
├── main.tf
├── outputs.tf
├── providers.tf
└── variables.tf
Setup environment
The source codes in setup
folder are listed as below:
- main.tf
locals {
region = var.region
}
# Define a s3 bucket to store terraform state file.
resource "aws_s3_bucket" "terraform_state" {
bucket = format("terraform-state-%s", local.region)
force_destroy = false
lifecycle {
ignore_changes = [bucket]
}
}
resource "aws_s3_bucket_versioning" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.bucket
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
- outputs.tf
output "s3_bucket_terraform_state" {
value = aws_s3_bucket.terraform_state.bucket
}
- providers.tf
terraform {
required_version = ">= 0.13.7"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.8.0"
}
}
}
- variables.tf
variable "region" {
description = "The name of the aws Region"
type = string
}
And then, we need to create s3 bucket for remote backend, the s3 bucket name is the output of s3_bucket_terraform_state
.
$ terraform init
$ terraform apply
Networking
The source codes in networking
folder are listed as below:
- main.tf
locals {
name = var.name
cidr_block = var.vpc_cidr_block
public_subnets_cidr_block = var.public_subnets_cidr_block
private_subnets_cidr_block = var.private_subnets_cidr_block
availability_zones = var.availability_zones
tags = var.vpc_tags
}
# VPC
resource "aws_vpc" "vpc" {
cidr_block = local.cidr_block
enable_dns_hostnames = true
enable_dns_support = true
tags = merge( {
Name = local.name
}, local.tags)
}
# Subnets
# Internet Gateway for Public Subnet
resource "aws_internet_gateway" "internet_gateway" {
vpc_id = aws_vpc.vpc.id
tags = merge({
Name = "${local.name} Internet Gateway"
}, local.tags)
}
# EIP for NAT
resource "aws_eip" "nat_eip" {
vpc = true
depends_on = [aws_internet_gateway.internet_gateway]
}
# NAT
resource "aws_nat_gateway" "nat_gateway" {
allocation_id = aws_eip.nat_eip.id
subnet_id = element(aws_subnet.public_subnet.*.id, 0)
tags = merge({
Name = "${local.name} Nat Gateway"
}, local.tags)
}
# Public subnet
resource "aws_subnet" "public_subnet" {
vpc_id = aws_vpc.vpc.id
count = length(local.public_subnets_cidr_block)
cidr_block = element(local.public_subnets_cidr_block, count.index)
availability_zone = element(local.availability_zones, count.index)
map_public_ip_on_launch = true
tags = merge( {
Name = "${local.name} Public Subnet ${element(var.availability_zones, count.index)}"
}, local.tags)
}
# Private Subnet
resource "aws_subnet" "private_subnet" {
vpc_id = aws_vpc.vpc.id
count = length(local.private_subnets_cidr_block)
cidr_block = element(local.private_subnets_cidr_block, count.index)
availability_zone = element(local.availability_zones, count.index)
map_public_ip_on_launch = false
tags = merge({
Name = "${local.name} Private Subnet ${element(var.availability_zones, count.index)}"
}, local.tags)
}
# Routing tables to route traffic for Private Subnet
resource "aws_route_table" "private" {
vpc_id = aws_vpc.vpc.id
tags = merge({
Name = "${local.name} Private Route Table"
}, local.tags)
}
# Routing tables to route traffic for Public Subnet
resource "aws_route_table" "public" {
vpc_id = aws_vpc.vpc.id
tags = merge({
Name = "${local.name} Public Route Table"
}, local.tags)
}
# Route for Internet Gateway
resource "aws_route" "public_internet_gateway" {
route_table_id = aws_route_table.public.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.internet_gateway.id
}
# Route for NAT
resource "aws_route" "private_nat_gateway" {
route_table_id = aws_route_table.private.id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.nat_gateway.id
}
# Route table associations for Public Subnets
resource "aws_route_table_association" "public" {
count = length(local.public_subnets_cidr_block)
subnet_id = element(aws_subnet.public_subnet.*.id, count.index)
route_table_id = aws_route_table.public.id
}
# Route table associations for Private Subnets
resource "aws_route_table_association" "private" {
count = length(local.private_subnets_cidr_block)
subnet_id = element(aws_subnet.private_subnet.*.id, count.index)
route_table_id = aws_route_table.private.id
}
# Default Security Group of VPC
resource "aws_security_group" "security_group" {
name = "${local.name} Security Group"
description = "Default SG to allow traffic from the VPC"
vpc_id = aws_vpc.vpc.id
depends_on = [
aws_vpc.vpc
]
ingress {
from_port = "0"
to_port = "0"
protocol = "-1"
self = true
}
egress {
from_port = "0"
to_port = "0"
protocol = "-1"
self = "true"
}
tags = merge({}, local.tags)
}
- outputs.tf
output "vpc_id" {
value = aws_vpc.vpc.id
}
output "public_subnets_id" {
value = [aws_subnet.public_subnet.*.id]
}
output "private_subnets_id" {
value = [aws_subnet.private_subnet.*.id]
}
output "security_groups_ids" {
value = [aws_security_group.security_group.id]
}
output "public_route_table" {
value = aws_route_table.public.id
}
- variables.tf
variable "name" {
description = "The VPC name"
}
variable "vpc_cidr_block" {
description = "CIDR block of the vpc"
}
variable "public_subnets_cidr_block" {
type = list
description = "CIDR block for Public Subnet"
}
variable "private_subnets_cidr_block" {
type = list
description = "CIDR block for Private Subnet"
}
variable "availability_zones" {
type = list
description = "AZ in which all the resources will be deployed"
}
variable "vpc_tags" {
description = "A map of tags to add to VPC"
type = map(string)
default = {}
}
Build VPC
The source codes in virginia
folder are listed as below:
- main.tf
locals {
// the region code of virginia
region = "us-east-1"
availability_zones = ["${local.region}a", "${local.region}b", "${local.region}c"]
tags = {
"Environment" : "PROD"
"Project" : "Infrastructure"
}
}
provider "aws" {
region = local.region
}
module "Networking" {
source = "../../module/networking"
name = "VPC"
availability_zones = local.availability_zones
vpc_cidr_block = "10.0.0.0/16"
public_subnets_cidr_block = ["10.0.32.0/24", "10.0.96.0/24", "10.0.224.0/24"]
private_subnets_cidr_block = ["10.0.0.0/19", "10.0.64.0/19", "10.0.128.0/19"]
vpc_tags = local.tags
}
- providers.tf
terraform {
required_version = ">= 0.13.7"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.8.0"
}
}
backend "s3" {
// the s3 bucket name
bucket = "*********"
key = "terraform/backend.tfstate"
region = "us-east-1"
encrypt = "true"
}
}
Now, we can build the VPC
$ terraform init
$ terraform apply
Top comments (0)