Terraforming your fist AWS VPC

automated VPC creation with terraform

Fri, 24 Jan 2020

unsplash.com

Terra-forming planet mars looks promising for the future, but hey… let’s start by terraforming an Amazon VPC first!

What is terraform

From terraform website:

Terraform is a tool for building, changing, and versioning infrastructure safely and efficiently.

How terraform can help us to build and deploy our first Virtual Private Cloud on AWS?

Well instead of digging into the AWS console by hand to build our infrastructure, we can describe our solution in executable files using a CLI that allow us to easily create, refactor and destroy our infrastructure as code.

And why not using CloudFormation?

To be honest, there’s no a clear winner of a competition here. The advantage of using a non proprietary tool like Terraform is that it does support different providers (GCP, Azure and many more) and can also be used for On-premises solutions.

To me, terraform looks more high level and easier to use than CloudFormation.

Getting started

install terraform

project files

create a file examplevpc.tf, where we will describe our infrastructure.

mkdir /project_files
touch examplevpc.tf

adding a provider and initialization

The following will add AWS as a provider within our terraform, here you can define your AWS CLI profile and some information like the default region, i have chosen eu-central-1 (Frankfurt)

provider "aws" {
  profile = "default"
  region = "eu-central-1"
}

Let’s init our terraform project, by asking the cli to download necessary plugins to work with our provider.

cd /project_files
terraform init

creating our VPC resources

Let’s create our first resource, an AWS VPC. We give it a Classless Inter-Domain Routing (CIDR) of /16 ranging from 10.0.0.0. This will give us 65536 ip addresses (in theory, but remember that AWS will reserve the first 4 and the last ip of each subnet!)

resource "aws_vpc" "publicVPCEuCentral1" {
  cidr_block = "10.0.0.0/16"
  tags = {
    Name = "EuCentralVPC 1"
  }
}

The next resource to the list it’s our first public subnet inside our EuCentralVPC 1. For the public subnet i have chosen a range of /24 starting from 10.0.128.0 that give us 251 ip addresses (not 256, remember the reserved ip addresses!)

For vpc_id we specify the identifier of the vpc resource described above. The id will be resolved once we deploy our infrastructure. The variable can be written as

${aws_vpc.publicVPCEuCentral1.id}

or directly in the simplified as below:

resource "aws_subnet" "publicSubnet1a" {
  vpc_id = aws_vpc.publicVPCEuCentral1.id
  cidr_block = "10.0.128.0/24"
  availability_zone = "eu-central-1a"
  tags = {
    Name = "publicSubnet1a"
  }
}

We also want a private subnet

resource "aws_subnet" "privateSubnet1a" {
  vpc_id = aws_vpc.publicVPCEuCentral1.id
  cidr_block = "10.0.0.0/24"
  availability_zone = "eu-central-1a"
  tags = {
    Name = "privateSubnet1a"
  }
}

And now we define a Network ACL (NACL) to allow all traffic both egress and ingress.

resource "aws_network_acl" "default-network-acl" {
  vpc_id = aws_vpc.publicVPCEuCentral1.id
  egress {
    protocol = "-1"
    rule_no = 100
    action = "allow"
    cidr_block = "0.0.0.0/0"
    from_port = 0
    to_port = 0
  }
  ingress {
    protocol = "-1"
    rule_no = 100
    action = "allow"
    cidr_block = "0.0.0.0/0"
    from_port = 0
    to_port = 0
  }
  subnet_ids = [
    aws_subnet.publicSubnet1a.id,
    aws_subnet.privateSubnet1a.id
  ]
  tags = {
    Name = "default ACL"
  }
}

We would like to give access to the internet for our public subnet right? Then we need an internet gateway!

resource "aws_internet_gateway" "PublicVPCEuCentral1IGW" {
  vpc_id = aws_vpc.publicVPCEuCentral1.id
  tags = {
    Name = "EuCentralVPC1IGW"
  }
}

To route our public subnet to the IGW, we need to define a routing table for our VPC to allow routing at 0.0.0.0/0 CIDR address range.

First we create a route table

resource "aws_route_table" "publicVPCEuCentral1RT" {
  vpc_id = aws_vpc.publicVPCEuCentral1.id
  tags = {
    Name = "EuCentralVPC1RouteTable"
  }
}

An then we need to associate the route table and the gateway to the appropriate CIDR block. Keep in mind that AWS refers destination as “target”.

resource "aws_route" "publicVPCEuCentral1RTRoutes" {
  route_table_id = aws_route_table.publicVPCEuCentral1RT.id
  gateway_id = aws_internet_gateway.PublicVPCEuCentral1IGW.id
  destination_cidr_block = "0.0.0.0/0"
}

To complete our public subnet routing, we need to associate our publicSubnet1a to the Route table we just created

resource "aws_route_table_association" "publicSubnet1aRTAssociation" {
  subnet_id = aws_subnet.publicSubnet1a.id
  route_table_id = aws_route_table.publicVPCEuCentral1RT.id
}

Ready to send Nukes to Mars pole?

To terraform your cloud, in the shell launch:

terraform apply

When your’e done, you can teardown all the created stuff by using:

terraform destroy

The Mars face!

And there we go, to summarize:

  • We defined a VPC
  • We created two subnet, one private and one public
  • We added NACL (stateless firewall) to allow both inbound and outbound traffic from anywhere
  • We created an Internet Gateway
  • We created a routing table for our public subnet to target IGW for internet access

Keep in mind that:

  • for security reason you might restrict your Network ACL with more restrictions
  • The defined CIDR block for the VPC it’s the maximum range for a VPC (it’s huge!)
  • If you plan to extend your own network, make sure that the range does not overlap with your existing network.

The diagram above represents what we built so far.

What’s next

Our simple VPC is now ready, in the next chapter we will add EC2 compute instances and we will deploy an apache web-server to our public subnet!

Loading...
Yuri Blanc

Yuri Blanc - FullStack developer I’m a web developer with love and passion. My main focus is about Java (J2EE), TypeScript, Angular 2+, React, NodeJS, Kotlin, Cloud Architect Solutions. I’m someone usually described as “passionate” and “creative” in all things related to information technologies!

© Yuri Blanc