Terraform

Getting Started

Terraform is a tool for building, changing, and versioning infrastructure safely and efficiently.

Terraform can manage existing and popular service providers as well as custom in-house solutions.

Terraform generates an execution plan describing what it will do to reach the desired state, and then executes it to build the described infrastructure.

As the configuration changes, Terraform is able to determine what changed and create incremental execution plans which can be applied.

The key features of Terraform are:

Infrastructure as Code: Infrastructure is described using a high-level configuration syntax. This allows a blueprint of your datacenter to be versioned and treated as you would any other code. Additionally, infrastructure can be shared and re-used.

Execution Plans: Terraform has a "planning" step where it generates an execution plan. The execution plan shows what Terraform will do when you call apply. This lets you avoid any surprises when Terraform manipulates infrastructure.

Resource Graph: Terraform builds a graph of all your resources, and parallelizes the creation and modification of any non-dependent resources. Because of this, Terraform builds infrastructure as efficiently as possible, and operators get insight into dependencies in their infrastructure.

Change Automation: Complex changesets can be applied to your infrastructure with minimal human interaction. With the previously mentioned execution plan and resource graph, you know exactly what Terraform will change and in what order, avoiding many possible human errors.

Install Terraform

Download Terraform Binary for your platform

Copy the binary at a folder reachable from PATH environment variable.

Verify Installation

$ terraform

usage: terraform [--version] [--help] <command> [args]

The available commands for execution are listed below.

The most common, useful commands are shown first, followed by

less common or more advanced commands. If you're just getting

started with Terraform, stick with the common commands. For the

other commands, please read the help and docs before usage.

Common commands:

apply Builds or changes infrastructure

destroy Destroy Terraform-managed infrastructure

fmt Rewrites config files to canonical format

get Download and install modules for the configuration

graph Create a visual graph of Terraform resources

import Import existing infrastructure into Terraform

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

untaint Manually unmark a resource as tainted

validate Validates the Terraform files

version Prints the Terraform version

All other commands:

state Advanced state management

Providers

Terraform is used to create, manage, and manipulate infrastructure resources. Examples of resources include physical machines, VMs, network switches, containers, etc. Almost any infrastructure noun can be represented as a resource in Terraform.

Terraform is agnostic to the underlying platforms by supporting providers.

A provider is responsible for understanding API interactions and exposing resources.

Providers generally are an IaaS (e.g. AWS, GCP, Microsoft Azure, OpenStack), PaaS (e.g. Heroku), or SaaS services (e.g. Atlas, DNSimple, CloudFlare).

Configuration

The set of files used to describe infrastructure in Terraform is simply known as a Terraform configuration.

Terraform uses text files to describe infrastructure and to set variables. These text files are called Terraform configurations and end in .tf. This section talks about the format of these files as well as how they're loaded.

The format of the configuration files are able to be in two formats: Terraform format and JSON.

The Terraform format is more human-readable, supports comments, and is the generally recommended format for most Terraform files.

The JSON format is meant for machines to create, modify, and update, but can also be done by Terraform operators if you prefer.

Terraform format ends in .tf and JSON format ends in .tf.json.

Build Infrastructure

Terraform can manage many providers, including multiple providers in a single configuration. Some examples of this are in the use cases section.

If you don't have an AWS account, create one now. For the getting started guide, we'll only be using resources which qualify under the AWS free-tier, meaning it will be free.

Configuration

We're going to write our first configuration now to launch a single AWS EC2 instance.

The format of the configuration files is documented here. Configuration files can also be JSON, but we recommend only using JSON when the configuration is generated by a machine.

Example: example.tf

provider "aws" { access_key = "ACCESS_KEY_HERE" secret_key = "SECRET_KEY_HERE" region = "us-east-1" } resource "aws_instance" "example" { ami = "ami-0d729a60" instance_type = "t2.micro" }

Note: The above configuration is designed to work on most EC2 accounts, with access to a default VPC. For EC2 Classic users, please use t1.micro for instance_type, and ami-408c7f28 for the ami.

Replace the ACCESS_KEY_HERE and SECRET_KEY_HERE with your AWS access key and secret key, available from this page.

provider "aws" {

access_key = "ACCESS_KEY_HERE"

secret_key = "SECRET_KEY_HERE"

region = "us-east-1"

}

resource "aws_instance" "example" {

ami = "ami-0d729a60"

instance_type = "t2.micro"

}

This is a complete configuration that Terraform is ready to apply. The general structure should be intuitive and straightforward.

The provider block is used to configure the named provider, in our case "aws." A provider is responsible for creating and managing resources. Multiple provider blocks can exist if a Terraform configuration is composed of multiple providers, which is a common situation.

The resource block defines a resource that exists within the infrastructure. A resource might be a physical component such as an EC2 instance, or it can be a logical resource such as a Heroku application.

The resource block has two strings before opening the block: the resource type and the resource name. In our example, the resource type is "aws_instance" and the name is "example." The prefix of the type maps to the provider.

In our case "aws_instance" automatically tells Terraform that it is managed by the "aws" provider.

Within the resource block itself is configuration for that resource. This is dependent on each resource provider and is fully documented within our providers reference. For our EC2 instance, we specify an AMI for Ubuntu, and request a "t2.micro" instance so we qualify under the free tier.

Execution Plan

Next, let's see what Terraform would do if we asked it to apply this configuration.

In the same directory as the example.tf file you created, run terraform plan.

You should see output similar to what is copied below. We've truncated some of the output to save space.

$ terraform plan

Refreshing Terraform state in-memory prior to plan...

The refreshed state will be used to calculate this plan, but

will not be persisted to local or remote state storage.

The Terraform execution plan has been generated and is shown below.

Resources are shown in alphabetical order for quick scanning. Green resources

will be created (or destroyed and then created if an existing resource

exists), yellow resources are being changed in-place, and red resources

will be destroyed. Cyan entries are data sources to be read.

Note: You didn't specify an "-out" parameter to save this plan, so when

"apply" is called, Terraform can't guarantee this is what will execute.

+ aws_instance.example

ami: "ami-0d729a60"

availability_zone: "<computed>"

ebs_block_device.#: "<computed>"

ephemeral_block_device.#: "<computed>"

instance_state: "<computed>"

instance_type: "t2.micro"

key_name: "<computed>"

network_interface_id: "<computed>"

placement_group: "<computed>"

private_dns: "<computed>"

private_ip: "<computed>"

public_dns: "<computed>"

public_ip: "<computed>"

root_block_device.#: "<computed>"

security_groups.#: "<computed>"

source_dest_check: "true"

subnet_id: "<computed>"

tenancy: "<computed>"

vpc_security_group_ids.#: "<computed>"

Plan: 1 to add, 0 to change, 0 to destroy.

terraform plan shows what changes Terraform will apply to your infrastructure given the current state of your infrastructure as well as the current contents of your configuration.

If terraform plan failed with an error, read the error message and fix the error that occurred. At this stage, it is probably a syntax error in the configuration. The output format is similar to the diff format generated by tools such as Git.

The output has a "+" next to "aws_instance.example", meaning that Terraform will create this resource. Beneath that, it shows the attributes that will be set. When the value displayed is <computed>, it means that the value won't be known until the resource is created.

Apply

The plan looks good, our configuration appears valid, so it's time to create real resources. Run terraform apply in the same directory as your example.tf, and watch it go! It will take a few minutes since Terraform waits for the EC2 instance to become available.

$ terraform apply

aws_instance.example: Creating...

ami: "" => "ami-0d729a60"

availability_zone: "" => "<computed>"

ebs_block_device.#: "" => "<computed>"

ephemeral_block_device.#: "" => "<computed>"

instance_state: "" => "<computed>"

instance_type: "" => "t2.micro"

key_name: "" => "<computed>"

network_interface_id: "" => "<computed>"

placement_group: "" => "<computed>"

private_dns: "" => "<computed>"

private_ip: "" => "<computed>"

public_dns: "" => "<computed>"

public_ip: "" => "<computed>"

root_block_device.#: "" => "<computed>"

security_groups.#: "" => "<computed>"

source_dest_check: "" => "true"

subnet_id: "" => "<computed>"

tenancy: "" => "<computed>"

vpc_security_group_ids.#: "" => "<computed>"

aws_instance.example: Still creating... (10s elapsed)

aws_instance.example: Still creating... (20s elapsed)

aws_instance.example: Still creating... (30s elapsed)

aws_instance.example: Creation complete

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

The state of your infrastructure has been saved to the path

below. This state is required to modify and destroy your

infrastructure, so keep it safe. To inspect the complete state

use the `terraform show` command.

State path: terraform.tfstate

Done! Check the instance from AWS console

AWS console snapshot

Terraform also puts some state into the terraform.tfstate file by default. This state file is extremely important; it maps various resource metadata to actual resource IDs so that Terraform knows what it is managing. This file must be saved and distributed to anyone who might run Terraform. We recommend simply putting it into version control, since it generally isn't too large.

You can inspect the state using terraform show:

$ terraform show

aws_instance.example:

id = i-0bda2366d74f6005b

ami = ami-0d729a60

availability_zone = us-east-1d

disable_api_termination = false

ebs_block_device.# = 0

ebs_optimized = false

ephemeral_block_device.# = 0

iam_instance_profile =

instance_state = running

instance_type = t2.micro

key_name =

monitoring = false

network_interface_id = eni-ebeef52a

private_dns = ip-172-31-28-160.ec2.internal

private_ip = 172.31.28.160

public_dns = ec2-107-23-6-129.compute-1.amazonaws.com

public_ip = 107.23.6.129

root_block_device.# = 1

root_block_device.0.delete_on_termination = true

root_block_device.0.iops = 0

root_block_device.0.volume_size = 8

root_block_device.0.volume_type = standard

security_groups.# = 0

source_dest_check = true

subnet_id = subnet-7ed56c25

tags.% = 0

tenancy = default

vpc_security_group_ids.# = 1

vpc_security_group_ids.2439997441 = sg-e2f0959f

You can see that by creating our resource, we've also gathered a lot more metadata about it. This metadata can actually be referenced for other resources or outputs.

Provisioning

The EC2 instance we launched at this point is based on the AMI given, but has no additional software installed.

If you're running an image-based infrastructure (perhaps creating images with Packer), then this is all you need.

However, many infrastructures still require some sort of initialization or software provisioning step. Terraform supports provisioners.

Congratulations!

You've built your first infrastructure with Terraform. You've seen the configuration syntax, an example of a basic execution plan, and understand the state file.

Change Infrastructure

In this section, we're going to modify that resource, and see how Terraform handles change.

Infrastructure is continuously evolving, and Terraform was built to help manage and enact that change. As you change Terraform configurations, Terraform builds an execution plan that only modifies what is necessary to reach your desired state.

By using Terraform to change infrastructure, you can version control not only your configurations but also your state so you can see how the infrastructure evolved over time.

Configuration

Let's modify the ami of our instance. Edit the "aws_instance.example" resource in your configuration and change it to the following:

resource "aws_instance" "example" {

ami = "ami-656be372"

instance_type = "t1.micro"

}

Note: EC2 Classic users please use AMI ami-656be372 and type t1.micro

We've changed the AMI from being an Ubuntu 14.04 LTS AMI to being an Ubuntu 16.04 LTS AMI.

Terraform configurations are meant to be changed like this.

You can also completely remove resources and Terraform will know to destroy the old one.

Execution Plan

Let's see what Terraform will do with the change we made.

$ terraform plan

Refreshing Terraform state in-memory prior to plan...

The refreshed state will be used to calculate this plan, but

will not be persisted to local or remote state storage.

aws_instance.example: Refreshing state... (ID: i-0bda2366d74f6005b)

The Terraform execution plan has been generated and is shown below.

Resources are shown in alphabetical order for quick scanning. Green resources

will be created (or destroyed and then created if an existing resource

exists), yellow resources are being changed in-place, and red resources

will be destroyed. Cyan entries are data sources to be read.

Note: You didn't specify an "-out" parameter to save this plan, so when

"apply" is called, Terraform can't guarantee this is what will execute.

-/+ aws_instance.example

ami: "ami-0d729a60" => "ami-656be372" (forces new resource)

availability_zone: "us-east-1d" => "<computed>"

ebs_block_device.#: "0" => "<computed>"

ephemeral_block_device.#: "0" => "<computed>"

instance_state: "running" => "<computed>"

instance_type: "t2.micro" => "t1.micro" (forces new resource)

key_name: "" => "<computed>"

network_interface_id: "eni-ebeef52a" => "<computed>"

placement_group: "" => "<computed>"

private_dns: "ip-172-31-28-160.ec2.internal" => "<computed>"

private_ip: "172.31.28.160" => "<computed>"

public_dns: "ec2-107-23-6-129.compute-1.amazonaws.com" => "<computed>"

public_ip: "107.23.6.129" => "<computed>"

root_block_device.#: "1" => "<computed>"

security_groups.#: "0" => "<computed>"

source_dest_check: "true" => "true"

subnet_id: "subnet-7ed56c25" => "<computed>"

tenancy: "default" => "<computed>"

vpc_security_group_ids.#: "1" => "<computed>"

Plan: 1 to add, 0 to change, 1 to destroy.

The prefix "-/+" means that Terraform will destroy and recreate the resource, versus purely updating it in-place. While some attributes can do in-place updates (which are shown with a "~" prefix), AMI changing on EC2 instance requires a new resource. Terraform handles these details for you, and the execution plan makes it clear what Terraform will do.

Additionally, the plan output shows that the AMI change is what necessitated the creation of a new resource. Using this information, you can tweak your changes to possibly avoid destroy/create updates if you didn't want to do them at this time.

Apply

From the plan, we know what will happen. Let's apply and enact the change.

$ terraform apply

aws_instance.example: Refreshing state... (ID: i-0bda2366d74f6005b)

aws_instance.example: Destroying...

aws_instance.example: Still destroying... (10s elapsed)

aws_instance.example: Still destroying... (20s elapsed)

aws_instance.example: Still destroying... (30s elapsed)

aws_instance.example: Destruction complete

aws_instance.example: Creating...

ami: "" => "ami-656be372"

availability_zone: "" => "<computed>"

ebs_block_device.#: "" => "<computed>"

ephemeral_block_device.#: "" => "<computed>"

instance_state: "" => "<computed>"

instance_type: "" => "t1.micro"

key_name: "" => "<computed>"

network_interface_id: "" => "<computed>"

placement_group: "" => "<computed>"

private_dns: "" => "<computed>"

private_ip: "" => "<computed>"

public_dns: "" => "<computed>"

public_ip: "" => "<computed>"

root_block_device.#: "" => "<computed>"

security_groups.#: "" => "<computed>"

source_dest_check: "" => "true"

subnet_id: "" => "<computed>"

tenancy: "" => "<computed>"

vpc_security_group_ids.#: "" => "<computed>"

aws_instance.example: Still creating... (7s elapsed)

aws_instance.example: Still creating... (10s elapsed)

aws_instance.example: Still creating... (17s elapsed)

aws_instance.example: Still creating... (20s elapsed)

aws_instance.example: Still creating... (27s elapsed)

aws_instance.example: Still creating... (30s elapsed)

aws_instance.example: Still creating... (37s elapsed)

aws_instance.example: Creation complete

Apply complete! Resources: 1 added, 0 changed, 1 destroyed.

The state of your infrastructure has been saved to the path

below. This state is required to modify and destroy your

infrastructure, so keep it safe. To inspect the complete state

use the `terraform show` command.

State path: terraform.tfstate

As the plan predicted, Terraform started by destroying our old instance, then creating the new one.

You can use terraform show again to see the new properties associated with this instance.

$ terraform show

aws_instance.example:

id = i-0734bf7c1cabf83b0

ami = ami-656be372

availability_zone = us-east-1b

disable_api_termination = false

ebs_block_device.# = 0

ebs_optimized = false

ephemeral_block_device.# = 0

iam_instance_profile =

instance_state = running

instance_type = t1.micro

key_name =

monitoring = false

network_interface_id = eni-5f9068a1

private_dns = ip-172-31-62-118.ec2.internal

private_ip = 172.31.62.118

public_dns = ec2-54-221-4-107.compute-1.amazonaws.com

public_ip = 54.221.4.107

root_block_device.# = 1

root_block_device.0.delete_on_termination = true

root_block_device.0.iops = 100

root_block_device.0.volume_size = 8

root_block_device.0.volume_type = gp2

security_groups.# = 0

source_dest_check = true

subnet_id = subnet-38da5e15

tags.% = 0

tenancy = default

vpc_security_group_ids.# = 1

vpc_security_group_ids.2439997441 = sg-e2f0959f

Destroy Infrastructure

Destroying your infrastructure is a rare event in production environments.

But if you're using Terraform to spin up multiple environments such as development, test, QA environments, then destroying is a useful action.

Plan

Before destroying our infrastructure, we can use the plan command to see what resources Terraform will destroy.

$ terraform plan -destroy

Refreshing Terraform state in-memory prior to plan...

The refreshed state will be used to calculate this plan, but

will not be persisted to local or remote state storage.

aws_instance.example: Refreshing state... (ID: i-0734bf7c1cabf83b0)

The Terraform execution plan has been generated and is shown below.

Resources are shown in alphabetical order for quick scanning. Green resources

will be created (or destroyed and then created if an existing resource

exists), yellow resources are being changed in-place, and red resources

will be destroyed. Cyan entries are data sources to be read.

Note: You didn't specify an "-out" parameter to save this plan, so when

"apply" is called, Terraform can't guarantee this is what will execute.

- aws_instance.example

Plan: 0 to add, 0 to change, 1 to destroy.

With the -destroy flag, we're asking Terraform to plan a destroy, where all resources under Terraform management are destroyed.

You can use this output to verify exactly what resources Terraform is managing and will destroy.

Destroy

Let's destroy the infrastructure now:

$ terraform destroy

Do you really want to destroy?

Terraform will delete all your managed infrastructure.

There is no undo. Only 'yes' will be accepted to confirm.

Enter a value: yes

aws_instance.example: Refreshing state... (ID: i-0734bf7c1cabf83b0)

aws_instance.example: Destroying...

aws_instance.example: Still destroying... (10s elapsed)

aws_instance.example: Still destroying... (20s elapsed)

aws_instance.example: Still destroying... (30s elapsed)

aws_instance.example: Still destroying... (40s elapsed)

aws_instance.example: Still destroying... (50s elapsed)

aws_instance.example: Still destroying... (1m0s elapsed)

aws_instance.example: Destruction complete

Apply complete! Resources: 0 added, 0 changed, 1 destroyed.

The terraform destroy command should ask you to verify that you really want to destroy the infrastructure. Terraform only accepts the literal "yes" as an answer as a safety mechanism. Once entered, Terraform will go through and destroy the infrastructure.

Just like with apply, Terraform is smart enough to determine what order things should be destroyed. In our case, we only had one resource, so there wasn't any ordering necessary. But in more complicated cases with multiple resources, Terraform will destroy in the proper order.

The terraform show will give empty result

Terraform Vs Others

Chef, Puppet, Ansible Playbook etc

Configuration management tools install and manage software on a machine that already exists.

Terraform is not a configuration management tool, and it allows existing tooling to focus on their strengths: bootstrapping and initializing resources.

Using provisioners, Terraform enables any configuration management tool to be used to setup a resource once it has been created.

Terraform focuses on the higher-level abstraction of the datacenter and associated services, without sacrificing the ability to use configuration management tools to do what they do best. It also embraces the same codification that is responsible for the success of those tools, making entire infrastructure deployments easy and reliable.

Cloudformation, Heat etc

Tools like CloudFormation, Heat, etc. allow the details of an infrastructure to be codified into a configuration file.

The configuration files allow the infrastructure to be elastically created, modified and destroyed.

Terraform is inspired by the problems they solve.

Terraform similarly uses configuration files to detail the infrastructure setup, but it goes further by being both cloud-agnostic and enabling multiple providers and services to be combined and composed.

For example, Terraform can be used to orchestrate an AWS and OpenStack cluster simultaneously, while enabling 3rd-party providers like CloudFlare and DNSimple to be integrated to provide CDN and DNS services.

This enables Terraform to represent and manage the entire infrastructure with its supporting services, instead of only the subset that exists within a single provider.

It provides a single unified syntax, instead of requiring operators to use independent and non-inter-operable tools for each platform and service.

Terraform also separates the planning phase from the execution phase, by using the concept of an execution plan.

By running terraform plan, the current state is refreshed and the configuration is consulted to generate an action plan.

The plan includes all actions to be taken: which resources will be created, destroyed or modified. It can be inspected by operators to ensure it is exactly what is expected. Using terraform graph, the plan can be visualized to show dependent ordering.

Once the plan is captured, the execution phase can be limited to only the actions in the plan.

Other tools combine the planning and execution phases, meaning operators are forced to mentally reason about the effects of a change, which quickly becomes intractable in large infrastructures.

Terraform lets operators apply changes with confidence, as they know exactly what will happen beforehand.

Support for AWS Resources is high in Cloudformation compared to Terraform. This might be one reason to consider Cloudformation based on your use-case.

State management is done by state file in Terraform, making multi developer to share the state file and also manual changes go un-noticed

Cloudformation does not have state management and manual changes go into consideration.

Getting Started

Choosing right tool to provision AWS infrastructure