When you get into infrastructure as code (IaC), you have a variety of tools at your disposal. Some may be just for a specific service, like CloudFormation is for AWS. Although these platform-specific tools may be faster in adopting the latest features, the knowledge you gather does not transfer over to other platforms. In this post, I will explain how to get started with Terraform.
What is Terraform
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.
Providers
Terraform has support for a variety of platforms. These integrations are called providers and can be used and contributed to by the community.
You can use Terraform to manage a list of major cloud vendors like AWS, Azure and Google Cloud Platform. You can manage your VCS installations and even your monitoring tools. The full list includes a whole lot of tools and if something is missing you can even create your own.
Concepts
To get started we first need to go over some basic concepts to understand how it all fits together.
HCL
Terraform uses configuration files in the HCL format to define the desired state you want to achieve. This HCL or Hashicorp Configuration Language is a basic and easy to understand syntax.
CLI
The Terraform CLI is an extensive tool. For simplicity, only the basic commands will be covered in this guide.
- init: Initialize a Terraform working directory
- plan: Generate and show an execution plan
- apply: Build or change infrastructure
State
Terraform stores the state of your infrastructure. This state is used to map the HCL configuration to the real-world resources. The state file can be stored locally for a single user scenario. In most cases however you’ll want to store the state in one centralized location. You can choose from a list of backends to do this.
Modules
Although not required to get started, modules allow you to re-use parts of your configuration to make sure you don’t duplicate your code and to maximize the similarity between multiple environments.
Basic
First, let’s create a simple file called main.tf
with the following content.
provider "aws" {
profile = "default"
region = "eu-west-1"
}
resource "aws_instance" "example" {
ami = "ami-07042e91d04b1c30d"
instance_type = "t2.micro"
}
This example shows the AWS provider configuration and a definition of an EC2 instance.
Note In the example we will be using AWS. If you want to follow along, make sure you have the AWS CLI configured. If you have multiple profiles, you can update the profile setting in the provider setting.
Plan
In order to see what the result of our configuration will be, we can run
terraform plan
and see what resources will be created.
$ 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.
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_instance.example will be created
+ resource "aws_instance" "example" {
+ ami = "ami-07042e91d04b1c30d"
+ arn = (known after apply)
...
}
Plan: 1 to add, 0 to change, 0 to destroy.
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
Once we have validated that the generated output is what we expected, we can now
run terraform apply
to allow Terraform to make the changes to our AWS account.
$ terraform apply
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_instance.example will be created
+ resource "aws_instance" "example" {
+ ami = "ami-07042e91d04b1c30d"
+ arn = (known after apply)
...
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
aws_instance.example: Creating...
aws_instance.example: Still creating... [10s elapsed]
...
aws_instance.example: Still creating... [3m30s elapsed]
aws_instance.example: Creation complete after 3m38s [id=i-013ab22e66500b50b]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
You should see the changes reflect in your AWS console.
Making a change
As you can see in the console, our instance does not have a Name set, so let’s define it and see how changes work.
Add the following block to the aws_instance
resource.
tags = {
Name = "TerraformBasic"
}
Let’s run another plan to see what changes will be 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-013ab22e66500b50b]
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# aws_instance.example will be updated in-place
~ resource "aws_instance" "example" {
...
~ tags = {
+ "Name" = "TerraformBasic"
}
...
}
Plan: 0 to add, 1 to change, 0 to destroy.
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
As you can see the type of change indicated in front of the resource:
+
: create~
: update in place-
: delete-/+
: delete then create+/-
: create then delete<=
: read data
Let’s see if it works by running terraform apply
.
$ terraform apply
aws_instance.example: Refreshing state... [id=i-013ab22e66500b50b]
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# aws_instance.example will be updated in-place
~ resource "aws_instance" "example" {
...
~ tags = {
+ "Name" = "TerraformBasic"
}
...
}
Plan: 0 to add, 1 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
aws_instance.example: Modifying... [id=i-013ab22e66500b50b]
aws_instance.example: Modifications complete after 2s [id=i-013ab22e66500b50b]
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
Destroy
Now that we have seen the basics at work, let’s do a cleanup of what we’ve just
created. Terraform provides the destroy
command for this action. Please note
this is a very dangerous action and any data/state inside the resources can get
lost.
$ terraform destroy
aws_instance.example: Refreshing state... [id=i-013ab22e66500b50b]
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
# aws_instance.example will be destroyed
- resource "aws_instance" "example" {
- ami = "ami-07042e91d04b1c30d" -> null
...
}
Plan: 0 to add, 0 to change, 1 to destroy.
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
aws_instance.example: Destroying... [id=i-013ab22e66500b50b]
aws_instance.example: Still destroying... [id=i-013ab22e66500b50b, 10s elapsed]
aws_instance.example: Still destroying... [id=i-013ab22e66500b50b, 20s elapsed]
aws_instance.example: Still destroying... [id=i-013ab22e66500b50b, 30s elapsed]
aws_instance.example: Destruction complete after 31s
Destroy complete! Resources: 1 destroyed.
More
In a future post we will be covering more complex setups on how to structure your projects as well as making your setup maintainable in the future.