In this tutorial, we will be creating a basic AWS VPC resources including the following:
GitHub source code: https://github.com/kalilou/TerraformtutorialVPC
Before we dive in, let's explain the two concepts, what is Terraform and what is a VPC in AWS
In order to understand terraform, let's take look at some of the Server Configuration Management such as Ansible, Chef, Puppet and Saltstack which are used to provision a single server maybe Virtual, Physical, Docker container or LXC container. But maybe you would like to have something higher than that, maybe something like provision your compute resources, storage resources and network resources. In a nutshell, you would like to describe your entire infrastructure topology in a more shareable way (having your infrastructure as code). And terraform uses a configuration language called HCL inspired by Nginx configuration way. But if you like JSON which is a machine friendly language, you can also export HCL to JSON. For more info on Terraform, please visit the following link Terraform (HashiCorp)
VPC stands for Virtual Private Cloud. With a AWS VPC you decide on how your network will look like and then you get to build it in the way that suits you well, for example, you get to choose the IP addresses you want, you can also decide on how to break down your VPC into subnets by having for instance non internet-facing subnets (Private subnets) and internet-facing subnets (Public subnets). We will take more about the VPC in the rest of this tutorial.
Let's get-started
AWS VPC stands for Virtual Private Cloud which basically provides a way to structure your own network the way you want in AWS. Before we dive in let's define some basic VPC concepts:
The figure below represent our VPC topology
Let's now install Terraform
Go to the following URL https://www.terraform.io/downloads.html to download Terraform according you OS, in this tutorial I am using MacOX.
After a successful installing you should be able to run and see the following output
$ terraform
usage: terraform [--version] [--help] <command> [<args>]
Available commands are:
apply Builds or changes infrastructure
destroy Destroy Terraform-managed infrastructure
get Download and install modules for the configuration
graph Create a visual graph of Terraform resources
init Initializes Terraform configuration from a module
output Read an output from a state file
plan Generate and show an execution plan
push Upload this Terraform module to Atlas to run
refresh Update local state file against real resources
remote Configure remote state storage
show Inspect Terraform state or plan
taint Manually mark a resource for recreation
validate Validates the Terraform files
version Prints the Terraform version
Now, we will be creating some AWS resources via Terraform
provider "aws" {
region = "${var.aws_region}"
access_key = "${var.access_key}"
secret_key = "${var.secret_key}"
}
The accesskey and secretkey are your AWS static credentials can be found under AWS Identy and Access Management in your AWS console. In case, your code will be versioned in GIT or BitBucket or Stash, I will recommend to use the environment variables AWS_ACCESS_KEY_ID
, AWS_SECRET_ACCESS_KEY
and AWS_DEFAULT_REGION
. Now let's reconfigure the aws provider one more time with the environment variables.
provider "aws" {}
And now we will export the environment variables (you may also wanna put them in your .bash_profile)
$ export AWS_ACCESS_KEY_ID="anaccesskey"
$ export AWS_SECRET_ACCESS_KEY="asecretkey"
$ export AWS_DEFAULT_REGION="us-west-2"
resource "aws_vpc" "vpc_tuto" {
cidr_block = "172.31.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "TestVPC"
}
}
resource "aws_subnet" "public_subnet_eu_west_1a" {
vpc_id = "${aws_vpc.vpc_tuto.id}"
cidr_block = "172.31.1.0/24"
map_public_ip_on_launch = true
availability_zone = "eu-west-1a"
tags = {
Name = "Subnet az 1a"
}
}
resource "aws_subnet" "private_1_subnet_eu_west_1a" {
vpc_id = "${aws_vpc.vpc_tuto.id}"
cidr_block = "172.31.2.0/24"
availability_zone = "eu-west-1a"
tags = {
Name = "Subnet private 1 az 1a"
}
}
resource "aws_subnet" "private_2_subnet_eu_west_1a" {
vpc_id = "${aws_vpc.vpc_tuto.id}"
cidr_block = "172.31.3.0/24"
availability_zone = "eu-west-1a"
tags = {
Name = "Subnet private 2 az 1a"
}
}
resource "aws_internet_gateway" "gw" {
vpc_id = "${aws_vpc.vpc_tuto.id}"
tags {
Name = "InternetGateway"
}
}
awsinternetgateway: Terraform notation for creating an Internet Gateway resource in AWS
resource "aws_route" "internet_access" {
route_table_id = "${aws_vpc.vpc_tuto.main_route_table_id}"
destination_cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.gw.id}"
}
We will create this IP to assign it the NAT Gateway
resource "aws_eip" "tuto_eip" {
vpc = true
depends_on = ["aws_internet_gateway.gw"]
}
Make sure to create the nat in a internet-facing subnet (public subnet)
resource "aws_nat_gateway" "nat" {
allocation_id = "${aws_eip.tuto_eip.id}"
subnet_id = "${aws_subnet.prublic_subnet_eu_west_1a.id}"
depends_on = ["aws_internet_gateway.gw"]
}
This will allow all traffics from the private subnets to the internet through the NAT Gateway (Network Address Translation)
resource "aws_route_table" "private_route_table" {
vpc_id = "${aws_vpc.vpc_tuto.id}"
tags {
Name = "Private route table"
}
}
resource "aws_route" "private_route" {
route_table_id = "${aws_route_table.private_route_table.id}"
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = "${aws_nat_gateway.nat.id}"
}
we will now associate our subnets to the different route tables
# Associate subnet public_subnet_eu_west_1a to public route table
resource "aws_route_table_association" "public_subnet_eu_west_1a_association" {
subnet_id = "${aws_subnet.public_subnet_eu_west_1a.id}"
route_table_id = "${aws_vpc.vpc_tuto.main_route_table_id}"
}
# Associate subnet private_1_subnet_eu_west_1a to private route table
resource "aws_route_table_association" "pr_1_subnet_eu_west_1a_association" {
subnet_id = "${aws_subnet.private_1_subnet_eu_west_1a.id}"
route_table_id = "${aws_route_table.private_route_table.id}"
}
# Associate subnet private_2_subnet_eu_west_1a to private route table
resource "aws_route_table_association" "pr_2_subnet_eu_west_1a_association" {
subnet_id = "${aws_subnet.private_2_subnet_eu_west_1a.id}"
route_table_id = "${aws_route_table.private_route_table.id}"
}
Now let's run Terraform to provide those resources in AWS
Let's run the following command to see how terraform plans to create the resources $ terraform plan -out tera
This will show the output of collection of resources which will be created
Now let's run the following command to create the resource $ terraform apply
Now in your AWS console you should see the resources created
VPC created
Subnets created
Route Tables created
You should be all resources created, now let's play with terraform command lines
In order to visualize the representation of the execution plan, run the following command $ terraform graph | dot -Tpng > graph.png
which will generate a png file as follow:
You can also run as simple as simple $ terraform graph
and you will get the following below:
Don't forget to destroy the resources if they are not needed, in my case since this is the end of the tutorial, I will use the following command to destroy the resources, otherwise you may get some bills from AWS. $ terraform destroy
Thanks you for reading and hope this will be helpful.