Contents

How to create AWS VPC with Terraform

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"
}