How to create AWS VPC with Terraform
Contents
Terraform script to create AWS VPC
Note
This Terraform script creates following resources
- Public and Private subnets inside Zone A and Zone B
- Internal and External Security Groups
- Internal SG allows all traffic from VPC CIDR
- External SG allows all traffic from VPC CIDR
- External SG allows all traffic from HTTP (80/TCP) and HTTPS (443/TCP)
- Route 53 private zone
- DHCP option for VPC so you can use Route 53 private zone
- NAT GW for private subnets (redundant, inside Zone A and Zone B)
- Internet GW
- SSH Key
Tip
- Usually you don’t need too many IP addresses on your public subnet, you can try to keep it small.
- Possibly you will need a lot of IP addresses inside your private subnet, /22 will gove you ~1000 IP adresses.
- You don’t need to worry about big subnets on AWS because AWS does not support broadcast.
terraform.tfvars
AWS_PROFILE = "dev-profile"
VPC_NAME = "development-vpc"
VPC_CIDR = "10.10.0.0/16"
VPC_PUBLIC_SUBNET_01 = "10.10.1.0/24"
VPC_PUBLIC_SUBNET_02 = "10.10.2.0/24"
VPC_PRIVATE_SUBNET_01 = "10.10.4.0/22"
VPC_PRIVATE_SUBNET_02 = "10.10.8.0/22"
DNS_ZONE = "development-vpc-dns"
var.tf
variable "AWS_CONFIG_FILE" {
default = "/var/lib/jenkins/.aws/credentials"
}
variable "AWS_REGION" {
default = "us-east-1"
}
variable "AWS_PROFILE" {
default = "dev-profile"
}
variable "VPC_NAME" {
default = null
type = string
}
variable "VPC_CIDR" {
default = null
type = string
}
variable "VPC_PUBLIC_SUBNET_01" {
default = null
type = string
}
variable "VPC_PUBLIC_SUBNET_02" {
default = null
type = string
}
variable "VPC_PRIVATE_SUBNET_01" {
default = null
type = string
}
variable "VPC_PRIVATE_SUBNET_02" {
default = null
type = string
}
variable "DNS_ZONE" {
default = null
type = string
}
main.tf
##### PROVIDER #####
provider "aws" {
shared_credentials_file= "${var.AWS_CONFIG_FILE}"
profile = "${var.AWS_PROFILE}"
region = "${var.AWS_REGION}"
}
##### MAIN VPC #####
resource "aws_vpc" "main" {
cidr_block = var.VPC_CIDR
instance_tenancy = "default"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = var.VPC_NAME
}
}
resource "aws_vpc_dhcp_options" "main_dhcp" {
domain_name = var.DNS_ZONE
domain_name_servers = ["AmazonProvidedDNS", ]
tags = {
Name = var.DNS_ZONE
}
}
resource "aws_vpc_dhcp_options_association" "main_dhcp_attach" {
vpc_id = aws_vpc.main.id
dhcp_options_id = aws_vpc_dhcp_options.main_dhcp.id
}
##### R53 ZONE #####
resource "aws_route53_zone" "private" {
name = var.DNS_ZONE
vpc {
vpc_id = aws_vpc.main.id
}
}
##### GATEWAYS #####
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.VPC_NAME}-IGW"
}
}
resource "aws_eip" "eip_natgwza" {
vpc = true
}
resource "aws_eip" "eip_natgwzb" {
vpc = true
}
resource "aws_nat_gateway" "natgwza" {
allocation_id = aws_eip.eip_natgwza.id
subnet_id = aws_subnet.private_01.id
tags = {
Name = "${var.VPC_NAME}-NATGW-(Za)"
}
# To ensure proper ordering, it is recommended to add an explicit dependency
# on the Internet Gateway for the VPC.
depends_on = [aws_internet_gateway.gw]
}
resource "aws_nat_gateway" "natgwzb" {
allocation_id = aws_eip.eip_natgwzb.id
subnet_id = aws_subnet.private_02.id
tags = {
Name = "${var.VPC_NAME}-NATGW-(Zb)"
}
# To ensure proper ordering, it is recommended to add an explicit dependency
# on the Internet Gateway for the VPC.
depends_on = [aws_internet_gateway.gw]
}
##### ROUTE TABLES #####
resource "aws_route_table" "public_rt" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.gw.id
}
tags = {
Name = "${var.VPC_NAME}-PUBLIC-RT"
}
}
resource "aws_route_table" "private_rt_za" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_nat_gateway.natgwza.id
}
tags = {
Name = "${var.VPC_NAME}-PRIVATE-RT-ZA"
}
}
resource "aws_route_table" "private_rt_zb" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_nat_gateway.natgwzb.id
}
tags = {
Name = "${var.VPC_NAME}-PRIVATE-RT-ZB"
}
}
##### SUBNETS #####
resource "aws_subnet" "public_01" {
vpc_id = aws_vpc.main.id
cidr_block = var.VPC_PUBLIC_SUBNET_01
availability_zone = "us-east-1a"
map_public_ip_on_launch = false
tags = {
Name = "${var.VPC_NAME}-PUBLIC-ZA"
}
}
resource "aws_route_table_association" "public_01_rt_attach" {
subnet_id = aws_subnet.public_01.id
route_table_id = aws_route_table.public_rt.id
}
resource "aws_subnet" "public_02" {
vpc_id = aws_vpc.main.id
cidr_block = var.VPC_PUBLIC_SUBNET_02
availability_zone = "us-east-1b"
map_public_ip_on_launch = false
tags = {
Name = "${var.VPC_NAME}-PUBLIC-ZB"
}
}
resource "aws_route_table_association" "public_02_rt_attach" {
subnet_id = aws_subnet.public_02.id
route_table_id = aws_route_table.public_rt.id
}
resource "aws_subnet" "private_01" {
vpc_id = aws_vpc.main.id
cidr_block = var.VPC_PRIVATE_SUBNET_01
availability_zone = "us-east-1a"
tags = {
Name = "${var.VPC_NAME}-PRIVATE-ZA"
}
}
resource "aws_route_table_association" "private_01_rt_attach" {
subnet_id = aws_subnet.private_01.id
route_table_id = aws_route_table.private_rt_za.id
}
resource "aws_subnet" "private_02" {
vpc_id = aws_vpc.main.id
cidr_block = var.VPC_PRIVATE_SUBNET_02
availability_zone = "us-east-1b"
tags = {
Name = "${var.VPC_NAME}-PRIVATE-ZB"
}
}
resource "aws_route_table_association" "private_02_rt_attach" {
subnet_id = aws_subnet.private_02.id
route_table_id = aws_route_table.private_rt_zb.id
}
##### SECURITY GROUPS #####
resource "aws_security_group" "internal_sg" {
name = "internal_sg"
description = "Internal SG"
vpc_id = aws_vpc.main.id
tags = {
"Name" = "${var.VPC_NAME}-INTERNAL"
"Terraform" = "Yes"
}
}
resource "aws_security_group_rule" "internal_sg_ingress_01" {
type = "ingress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["${var.VPC_CIDR}", ]
security_group_id = aws_security_group.internal_sg.id
}
resource "aws_security_group_rule" "internal_sg_egress_01" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.internal_sg.id
}
resource "aws_security_group" "external_sg" {
name = "external_sg"
description = "External SG"
vpc_id = aws_vpc.main.id
tags = {
"Name" = "${var.VPC_NAME}-EXTERNAL"
"Terraform" = "Yes"
}
}
resource "aws_security_group_rule" "external_sg_ingress_01" {
type = "ingress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["${var.VPC_CIDR}", ]
security_group_id = aws_security_group.external_sg.id
}
resource "aws_security_group_rule" "external_sg_ingress_02" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.external_sg.id
}
resource "aws_security_group_rule" "external_sg_ingress_03" {
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0}"]
security_group_id = aws_security_group.external_sg.id
}
resource "aws_security_group_rule" "external_sg_egress_01" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.external_sg.id
}
##### EC2 KEY #####
resource "aws_key_pair" "ec2_key" {
key_name = "OPS_MAIN_KEY"
public_key = "ssh-rsa YourSSHPublicKey"
}