Hashicorp, the cool guys behind Vagrant, Packer etc… have done it again and come up with a simple way build, modify and destroy cloud infrastructure. It is pure infrastructure as code which can be version controlled. But the best part is that it can work with many cloud platforms: AWS, Google Cloud, Digital Ocean, Heroku etc…
Getting Started
The official documentation can be found here.
Download the zip package from http://www.terraform.io/downloads.html for your O.S., extract and move the executables into your /usr/local/bin (or add the folder to your $PATH).
Verify the installation by executing:
1 2 |
$ terraform usage: terraform [--version] [--help] <command></command> [] |
We‘ll be using AWS as the provider so you will need an access and secret key for your user account.
Create a folder where you will keep all your .tf files. You can later add this folder to Git version control.
Provisioning a single EC2 instance
Create a file called web-instance.tf with the following:
1 2 3 4 5 6 7 8 9 10 |
provider "aws" { access_key = "xxxx" secret_key = "xxxx" region = "ap-southeast-2" } resource "aws_instance" "web" { ami = "ami-972444ad" instance_type = "t2.micro" } |
The provider line tells Terraform to use the aws provider using the keys and region provided.
The aws_instance instructs Terraform to create a instance with a tag “web” using the Ubuntu 14.04 LTS AMI.
Before building infrastructure Terraform can give you a preview of the changes it will apply to the infrastructure:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ terraform plan ... + aws_instance.web ami: "" => "ami-972444ad" availability_zone: "" => "computed" instance_type: "" => "t2.small" key_name: "" => "computed" private_dns: "" => "computed" private_ip: "" => "computed" public_dns: "" => "computed" public_ip: "" => "computed" security_groups.#: "" => "computed" subnet_id: "" => "computed" |
It uses Git diff syntaxing to show the differences with the current state of the infrastructure. Currently there’s no aws_instance tagged “web”, hence the line has a +
The “computed” values means that they will be known after the resource has been created.
To apply the .tf run:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$ terraform apply aws_instance.web: Creating... ami: "" => "ami-972444ad" availability_zone: "" => "computed" instance_type: "" => "t2.small" key_name: "" => "computed" private_dns: "" => "computed" private_ip: "" => "computed" public_dns: "" => "computed" public_ip: "" => "computed" security_groups.#: "" => "computed" subnet_id: "" => "computed" aws_instance.web: Creation complete Apply complete! Resources: 1 added, 0 changed, 0 destroyed. … State path: terraform.tfstate |
Now check the AWS console, the instance has successfully been created !
The terraform.tfstate file which gets generated is very important as it contains the current state of the infrastructure in AWS. For this reason it’s important to version control it in Git if others are working on the infrastructure (not at the same time).
Modifying the infrastructure
Edit your web-instance.tf file and changed the instance type from t2.micro to t2.small then check what will change:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$ terraform plan Refreshing Terraform state prior to plan... aws_instance.web: Refreshing state... (ID: i-976a32a9) ... -/+ aws_instance.web ami: "ami-972444ad" => "ami-972444ad" availability_zone: "ap-southeast-2b" => "computed" instance_type: "t2.micro" => "t2.small" (forces new resource) key_name: "" => "computed" private_dns: "ip-172-31-3-63.ap-southeast-2.compute.internal" => "computed" private_ip: "172.31.3.63" => "computed" public_dns: "ec2-54-206-108-0.ap-southeast-2.compute.amazonaws.com" => "computed" public_ip: "54.206.108.0" => "computed" security_groups.#: "1" => "computed" subnet_id: "subnet-c4bf8bad" => "computed" |
The value for instance_type has changed, apply the new changes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$ terraform apply aws_instance.web: Refreshing state... (ID: i-976a32a9) aws_instance.web: Destroying... aws_instance.web: Destruction complete aws_instance.web: Modifying... ami: "ami-972444ad" => "ami-972444ad" availability_zone: "ap-southeast-2b" => "computed" instance_type: "t2.micro" => "t2.small" key_name: "" => "computed" private_dns: "ip-172-31-3-63.ap-southeast-2.compute.internal" => "computed" private_ip: "172.31.3.63" => "computed" public_dns: "ec2-54-206-108-0.ap-southeast-2.compute.amazonaws.com" => "computed" public_ip: "54.206.108.0" => "computed" security_groups.#: "1" => "computed" subnet_id: "subnet-c4bf8bad" => "computed" aws_instance.web: Modifications complete Apply complete! Resources: 0 added, 1 changed, 1 destroyed. |
Instead of stopping the existing instance and upscaling it, it destroys and creates a new aws_instance resource.
Destroying the infrastructure
Before destroying infrastructure, Terraform needs to generate a destroy plan which lists the resources to destroy, to a file here named destroy.tplan
1 2 3 4 5 6 7 8 |
$ terraform plan -destroy -out=destroy.tfplan Refreshing Terraform state prior to plan... aws_instance.web: Refreshing state... (ID: i-3a6f3704) ... Path: destroy.tfplan - aws_instance.web |
The line with – confirms that the aws_instance resource tagged web will be destroyed, lets apply the destruction plan:
1 2 3 4 5 |
$ terraform apply destroy.tfplan aws_instance.web: Destroying... aws_instance.web: Destruction complete Apply complete! Resources: 0 added, 0 changed, 1 destroyed. |
Provisioning a multi-tiered VPC
Terraform can provision a VPC using most of the available VPC resources, except a few which are missing.
Here we provision 2 web instances, 2 application instances and a nat instance within 2 tier VPC, complete with appropriate security groups, load balancers, route tables, internet gateway, elastic IP etc…
The gist can be viewed here.
Conclusion
Terraform is an use out of the box ready and easy to use tool to build infrastructure using only a couple of lines. It’s very practical to quickly build a proof of concept infrastructure.
Using the AWS provider, it’s missing a couple of resources such as Elastic Network Interfaces, Access Control Lists, Security Groups egress rules, VPC DHCP options plus it does not support User Data (it can only execute remote ssh commands). However at version 0.2.1 it is still very early days and no doubt we will be seeing lots of improvements to Terraform in the future !