Hardik Darji



What is Terraform?

Terraform is an open-source IoC (infrastructure as code) tool by HashiCorp. It allows us to define and provision infrastructure using a high-level configuration language.

For example, the following piece of code will create a server on AWS.

provider "aws" {
  region = "us-east-1"
}

resource "aws_instance" "example" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"
}

To deploy it, we can just run one command:

> terraform apply

Great right? Here are some Terraform quick-basics:

We interact with Terraform with its CLI. Once we have created our .tf files (configuration files) in our Terraform project, we first need to run init command to initialize the directory as a starting point of our project.

> terraform init

The above command will download the provider and take all the actions required for project initialization.

Now Let's say we want to check what changes will be made in our environment before applying it to our environment. We can run the following command for that.

> terraform plan

After checking the configuration that will be applied, if we want to proceed with deployment, we can run the apply command.

> terraform apply

The above command will deploy our configuration.

Secure VPC design using Terraform

To deploy any kind of Infrastructure in AWS environment, first we need to create a VPC in AWS. We don't want to use default VPC provided by AWS as the default VPC is a VPC with public subnets only where each EC2 instance will automatically receive a public IP Address.

The following Terraform source creates a VPC with the following attributes:

- Has public and private subnets

- Has internet gateway

- EC2 instances in private subnets do not get public IP addresses

- Two route tables for public and private subnets

- Association of public route table to public subnets

- Association of private route table to private subnets

- NAT gateway in case EC2 instances in private subnets needs to connect to the internet (For example, fetching updates)

Create a main.tf file with the following configuration:

provider "aws" {
  region                  = "us-west-2"
  shared_credentials_file = "/home/user/.aws/credentials"
  profile                 = "default"
}

resource "aws_vpc" "aws_vpc_terraform" {
  cidr_block = "10.0.0.0/16"

  tags {
    Name  = "aws_vpc_terraform"
   }
}

resource "aws_internet_gateway" "aws_igw" {
  vpc_id = "${aws_vpc.aws_vpc_terraform.id}"

  tags {
    Name  = "aws_igw"
  }
}

resource "aws_subnet" "aws_public_1" {
  vpc_id                  = "${aws_vpc.aws_vpc_terraform.id}"
  cidr_block              = "10.0.1.0/24"
  map_public_ip_on_launch = true
  availability_zone       = "us-west-2a"

  tags = {
    Name  = "aws_public_1"
   }
}

resource "aws_subnet" "aws_public_3" {
  vpc_id                  = "${aws_vpc.aws_vpc_terraform.id}"
  cidr_block              = "10.0.5.0/24"
  map_public_ip_on_launch = true
  availability_zone       = "us-west-2c"

  tags = {
    Name  = "aws_public_3"
  }
}

resource "aws_subnet" "aws_public_2" {
  vpc_id                  = "${aws_vpc.aws_vpc_terraform.id}"
  cidr_block              = "10.0.3.0/24"
  map_public_ip_on_launch = true
  availability_zone       = "us-west-2b"

  tags = {
    Name  = "aws_public_2"
   }
}

resource "aws_subnet" "aws_private_1" {
  vpc_id            = "${aws_vpc.aws_vpc_terraform.id}"
  cidr_block        = "10.0.2.0/24"
  availability_zone = "us-west-2a"

  tags = {
    Name  = "aws_private_1"
  }
}

resource "aws_subnet" "aws_private_2" {
  vpc_id            = "${aws_vpc.aws_vpc_terraform.id}"
  cidr_block        = "10.0.4.0/24"
  availability_zone = "us-west-2b"

  tags = {
    Name  = "aws_private_2"
  }
}

resource "aws_subnet" "aws_private_3" {
  vpc_id            = "${aws_vpc.aws_vpc_terraform.id}"
  cidr_block        = "10.0.6.0/24"
  availability_zone = "us-west-2c"

  tags = {
    Name  = "aws_private_3"
  }
}

resource "aws_eip" "aws_eip_nat" {
  vpc        = true
  depends_on = ["aws_internet_gateway.aws_igw"]
}

resource "aws_nat_gateway" "aws_nat_gateway" {
  allocation_id = "${aws_eip.aws_eip_nat.id}"
  subnet_id     = "${aws_subnet.aws_public_3.id}"
  depends_on    = ["aws_internet_gateway.aws_igw"]
}

resource "aws_route_table" "aws_private_route_table" {
  vpc_id = "${aws_vpc.aws_vpc_terraform.id}"

  tags {
    Name  = "aws-3 private route table"
    Owner = "aws-3"
  }
}

resource "aws_route" "private_route" {
  route_table_id         = "${aws_route_table.aws_private_route_table.id}"
  destination_cidr_block = "0.0.0.0/0"
  nat_gateway_id         = "${aws_nat_gateway.aws_nat_gateway.id}"
}

resource "aws_route_table" "aws_public_route_table" {
  vpc_id = "${aws_vpc.aws_vpc_terraform.id}"

  tags {
    Name  = "aws-3 public route table"
  }
}

resource "aws_route" "public_route" {
  route_table_id         = "${aws_route_table.aws_public_route_table.id}"
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = "${aws_internet_gateway.aws_igw.id}"
}

resource "aws_route_table_association" "public_subnet_us_west_2a_association" {
  subnet_id      = "${aws_subnet.aws_public_1.id}"
  route_table_id = "${aws_route_table.aws_public_route_table.id}"
}

resource "aws_route_table_association" "public_subnet_us_west_2b_association" {
  subnet_id      = "${aws_subnet.aws_public_2.id}"
  route_table_id = "${aws_route_table.aws_public_route_table.id}"
}

resource "aws_route_table_association" "public_subnet_us_west_2c_association" {
  subnet_id      = "${aws_subnet.aws_public_3.id}"
  route_table_id = "${aws_route_table.aws_public_route_table.id}"
}

resource "aws_route_table_association" "private_subnet_us_west_2a_association" {
  subnet_id      = "${aws_subnet.aws_private_1.id}"
  route_table_id = "${aws_route_table.aws_private_route_table.id}"
}

resource "aws_route_table_association" "private_subnet_us_west_2b_association" {
  subnet_id      = "${aws_subnet.aws_private_2.id}"
  route_table_id = "${aws_route_table.aws_private_route_table.id}"
}

resource "aws_route_table_association" "private_subnet_us_west_2c_association" {
  subnet_id      = "${aws_subnet.aws_private_3.id}"
  route_table_id = "${aws_route_table.aws_private_route_table.id}"
}

We can now use terraform apply to create this VPC in our environment.