Terraform Practice pt1: Description & Deploying VPC/Subnets with Terraform on AWS
Tags: cloud, devops, terraform, aws
Published: 2022-06-12 08:38:12

Now that we are familiar with Terraform's blocks let's try using them. I will attempt to implement this infrastructure from [AWS official user guide](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Scenario2.html)

<br/>
Code be found on [Github](https://github.com/periwinkleFTW/terraform-path-to-cert/tree/master/practice-pt-1)

*Note: it is assumed that you have completed Installation & Usage tasks from this series*

<br/>
![Infrastructure Diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ijdg15irou6cgzmc7sl.png)


<br/>
My goal is to build a configuration that will:
1. Create a vpc (size /16 CIDR block 10.0.0.0/16).
2. Create 1 public subnet (for EC2 instance) in Availability Zone 1 (size /24 CIDR block 10.0.0.0/24). This subnet is associated with a route table that has a route to to an Internet Gateway.
3. Create 1 private subnet (for RDS) in Availability Zone 2 (size /24 CIDR block 10.0.1.0/24).
4. Create separate security groups for each subnet.
5. Create 3 EC2 (web servers) instances in public subnet in AZ 1 within subnet range. Each instances should have an Elastic IPv4 associated with it. Each instance must be accessible from the internet.
6. Install Docker on web servers & clone code from github repo.
7. Create a NAT Gateway in public subnet in AZ 1 with its own Elastic IPv4 address.
8. Create an Elastic IP for NAT Gateway.
9. Create 3 EC2 (db) instances in a private subnet in AZ 2 (within subnet range). Instances should be able to send requests to the internet using NAT Gateway.
10. Create route tables for each subnet.
11. Create an Internet Gateway that connects VPC to the internet and other AWS services.
12. Create main route table associated with private subnet. This table should contain an entry that ebables instances in the subnet to communicate with other instances in the VPC over IPv4, and an entry that enables instances in the subnet to communicate with the internet through the NAT Gateway over IPv4.
13. (Optional) Create subdomains for public subnet instances
14. (Optional) Output urls, public IPs, and other relevant information in a human readable format
<br/>

## Part 1: Initial setup

Let's get to work. First we need to create a directory for our project, change into it, and create 3 files: `main.tf`, `variables.tf`, and `outputs.tf`. I create those files with `touch` command, but you can use whatever way you prefer:
```bash
$ mkdir terraform-practice
$ cd terraform-practice
$ touch main.tf variables.tf outputs.tf
```

<br/>

## Part 2: Terraform Settings

First thing we will define would be the `terraform` block that will pin the versions of `aws provider` and `terraform` that we will use. This block will go into its own file called `terraform.tf`.

Open `terraform.tf` and put this code in it:
```terraform
# terraform.tf

terraform {
  # Provider-specific settings
  required_providers {
    aws = {
      version = ">= 2.7.0"
      source  = "hashicorp/aws"
    }
  }
  # Terraform version
  required_version = ">= 0.14.9"
}
```

<br/>
Now we can initialize Terraform with:
```bash
$ terraform init
# Output
Terraform has been successfully initialized!
```

<br/> 
## Part 3: Region, VPC, and Subnets

To make it a better practice project we will make use of the `variable` block by putting some of the resource-specific settings in there. We will keep adding more variables as the project goes on. Open `varaibles.tf` file and put this code there:

```terraform
# variables.tf

variable "aws_region" {
  type    = string
  default = "ca-central-1"
}

variable "vpc_name" {
  type    = string
  default = "TerraformCertPrep_vpc"
}

variable "vpc_cidr" {
  type    = string
  default = "10.0.0.0/16"
}

variable "public_subnet" {
  default = {
    "public_subnet_1" = 1
  }
}

variable "public_subnet_cidr" {
  type    = string
  default = "10.0.0.0/24"
}

variable "private_subnet_cidr" {
  type    = string
  default = "10.0.1.0/24"
}

variable "private_subnet" {
  default = {
    "private_subnet_1" = 1
  }
}
```

<br/>
We can finally start working on our resources. This code will belong in the `main.tf` file. Next we will start work on vpc, official [docs here](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc). We will use our newly defined variables:

```terraform
# main.tf

provider "aws" {
  # This is how we access variables
  region = var.aws_region
}

data "aws_availability_zones" "available" {}
data "aws_region" "current" {}

resource "aws_vpc" "vpc" {
  cidr_block = var.vpc_cidr

  tags = {
    Name        = var.vpc_name
    Environment = "demo_environment"
    Terraform   = "true"
  }
}

resource "aws_subnet" "public_subnet" {
  vpc_id                  = aws_vpc.vpc.id
  cidr_block              = var.public_subnet_cidr
  availability_zone       = data.aws_availability_zones.available.names[0]
  map_public_ip_on_launch = true

  tags = {
    Name      =  "My Public Subnet"
    Terraform = "true"
  }
}

resource "aws_subnet" "private_subnet" {
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = var.private_subnet_cidr
  availability_zone = data.aws_availability_zones.available.names[1]

  tags = {
    Name      = "My Private Subnet"
    Terraform = "true"
  }
}
```


<br/>
We can run format and validate command to check our code:
```bash
$ terraform fmt
$ terraform validate
```

<br/>
Then we run:
```bash
$ terraform plan
# Output
Plan: 3 to add, 0 to change, 0 to destroy.
```
You should see a detailed report with 3 resources that will be created: 1 vpc and 2 subnets.

<br/>
Now we can create those resources with `apply` command (you will need to type `yes` when prompted to approve):
```bash
$ terraform apply
# Output
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
```

<br/>
Let's check AWS console to verify the results.

VPC was created:
![VPC on aws](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3erdypjzud8fxe6er8au.png)

And so were our subnets:
![Subnets on aws](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k33qeigmvs8z9e6gdcax.png)

<br/>
This part of the code seems to be working fine. Since I have no need for these resources at the moment, I will destroy them:
```bash
$ terraform destroy
```

<br/>
We have completed the first 3 tasks of our goal. In the next article I will try to deploy route tables, NAT Gateway, and an Internet Gateway.

See you soon!