Welcome to part 2 of VPC and Terraform with AWS. In this post, I’ll show you how to write a terraform scripts to provision our VPC infrastructure using code.

The first step is to create a folder “devops-aws” under your home directory and initialize the git repository under it. Next, create subdirectory “vpc” to store our configuration files

$ cd ~
$ mkdir devops-aws
$ cd devops-aws
$ git init
$ mkdir vpc

The first thing to do, is to create provider details with credentials to access it. Let’s create a file named provider.tf

$ touch provider.tf

the content should be like

provider "aws" {
    access_key = "${var.AWS_ACCESS_KEY}"
    secret_key = "${var.AWS_SECRET_KEY}"
    region = "${var.AWS_REGION}"
}

Here we defined our credential to access AWS plus region where we want to provision our infrastructure. To simplify the workflow, we provided them in form of variables. I’ll show you later how to define values.

Next, we will start to define our VPC configuration. Let’s create another file vpc.tf

$ touch vpc.tf

Let me describe the content line by line to understand the whole process.

First, we must define our VPC. We need to provide the name, the CIDR block as well as some other parameters:

# VPC
resource "aws_vpc" "devops-vpc" {
    cidr_block = "10.0.0.0/16"
    instance_tenancy = "default"
    enable_dns_support = "true"
    enable_dns_hostnames = "true"
    enable_classiclink = "false"
    tags {
        Name = "devops-vpc"
    }
}

Now we must define our subnets with the CIDR block, availability zones plus some other parameters:

# SUBNETS
resource "aws_subnet" "devops_pub_eu_west-1a" {
    vpc_id = "${aws_vpc.devops-vpc.id}"
    cidr_block = "10.0.0.0/24"
    map_public_ip_on_launch = "true"
    availability_zone = "eu-west-1a"

    tags {
        Name = "devops_pub_eu_west-1a"
    }
}

resource "aws_subnet" "devops_pub_eu_west-1b" {
    vpc_id = "${aws_vpc.devops-vpc.id}"
    cidr_block = "10.0.1.0/24"
    map_public_ip_on_launch = "true"
    availability_zone = "eu-west-1b"

    tags {
        Name = "devops_pub_eu_west-1b"
    }
}

resource "aws_subnet" "devops_priv_eu_west-1a" {
    vpc_id = "${aws_vpc.devops-vpc.id}"
    cidr_block = "10.0.128.0/24"
    map_public_ip_on_launch = "true"
    availability_zone = "eu-west-1a"

    tags {
        Name = "devops_priv_eu_west-1a"
    }
}

resource "aws_subnet" "devops_priv_eu_west-1b" {
    vpc_id = "${aws_vpc.devops-vpc.id}"
    cidr_block = "10.0.129.0/24"
    map_public_ip_on_launch = "true"
    availability_zone = "eu-west-1b"

    tags {
        Name = "devops_priv_eu_west-1b"
    }
}

Next Internet Gateway

# Internet GW
resource "aws_internet_gateway" "devops-ig" {
    vpc_id = "${aws_vpc.devops-vpc.id}"

    tags {
        Name = "devops-ig"
    }
}

NAT Gateways

# NAT Gatewys
resource "aws_eip" "nat1" {
  vpc      = true
}

resource "aws_eip" "nat2" {
  vpc      = true
}

resource "aws_nat_gateway" "devops-ng-az-1" {
  allocation_id = "${aws_eip.nat1.id}"
  subnet_id = "${aws_subnet.devops_pub_eu_west-1a.id}"
  depends_on = ["aws_internet_gateway.devops-ig"]
}

resource "aws_nat_gateway" "devops-ng-az-2" {
  allocation_id = "${aws_eip.nat2.id}"
  subnet_id = "${aws_subnet.devops_pub_eu_west-1b.id}"
  depends_on = ["aws_internet_gateway.devops-ig"]
}

And finally route tables

# MAIN-PUBLIC route table
resource "aws_route_table" "main-public" {
    vpc_id = "${aws_vpc.devops-vpc.id}"
    route {
        cidr_block = "0.0.0.0/0"
        gateway_id = "${aws_internet_gateway.devops-ig.id}"
    }
    tags {
        Name = "main-public"
    }
}

# Route association
resource "aws_route_table_association" "main-public-1-a" {
    subnet_id = "${aws_subnet.devops_pub_eu_west-1a.id}"
    route_table_id = "${aws_route_table.main-public.id}"
}

resource "aws_route_table_association" "main-public-1-b" {
    subnet_id = "${aws_subnet.devops_pub_eu_west-1b.id}"
    route_table_id = "${aws_route_table.main-public.id}"
}

# PRIVATE-AZ-1 route table
resource "aws_route_table" "private-az-1" {
    vpc_id = "${aws_vpc.devops-vpc.id}"
    route {
        cidr_block = "0.0.0.0/0"
        nat_gateway_id = "${aws_nat_gateway.devops-ng-az-1.id}"
    }

    tags {
        Name = "private-az-1"
    }
}

# Route association
resource "aws_route_table_association" "main-private-1-a" {
    subnet_id = "${aws_subnet.devops_priv_eu_west-1a.id}"
    route_table_id = "${aws_route_table.private-az-1.id}"
}

# PRIVATE-AZ-2 route table
resource "aws_route_table" "private-az-2" {
    vpc_id = "${aws_vpc.devops-vpc.id}"
    route {
        cidr_block = "0.0.0.0/0"
        nat_gateway_id = "${aws_nat_gateway.devops-ng-az-2.id}"
    }

    tags {
        Name = "private-az-2"
    }
}

# Route association
resource "aws_route_table_association" "main-private-1-b" {
    subnet_id = "${aws_subnet.devops_priv_eu_west-1b.id}"
    route_table_id = "${aws_route_table.private-az-2.id}"
}

Before we start to provision our infrastructure, we must define variables. Create another file vars.tf, in which we define our variable.

$ touch vars.tf
variable "AWS_ACCESS_KEY" {}
variable "AWS_SECRET_KEY" {}
variable "AWS_REGION" {}

And finally create a file terraform.tfvars, with variables values (AWS credentials are hidden due to security reason)

$ touch terraform.tfvars
AWS_ACCESS_KEY = "xxxxxxxxxx"
AWS_SECRET_KEY = "xxxxxxxxxx"
AWS_REGION = "eu-west-1"

That’s all for now. In the next post, I’ll show you how to use terraform to provision infrastructure based on our newly created code.

Let’s start to build our infrastructure. In order to use the AWS provider, we must force on terraform to download necessary plugins. Type below command to do this

$ terraform init

You should get the message, that Terraform has been successfully initialized!. Let’s run another command to check what terraform is going to configure in our infrastructure:

$ terraform plan

You should get a message that terraform is going to add 16 new items with the full description of each. Let’s run the final command to provision the infrastructure:

$ terraform apply

Terraform will prompt you for the confirmation. Type “yes” to continue. Now go to AWS Console and check if new resources have been successfully created.