Terraform HCL Intro 1: Resources, Variables, Outputs
This is the start of a series of posts to help introduce and learn the Terraform HCL language. It is written for those with an elementary understanding of programming already. This allows us to cover things effectively instead of being stuck in the weeds.
In this post, we’ll cover the typical Terraform project structure, and provide a gentle introduction to Terraform resources, variables, and outputs. As a part of this, we’ll also go through the starter commands: terraform init, apply, and destroy.
Note, we’ll use a local backend statefile for simplicity. For more information about statefile management, these docs can be helpful: State File Approaches.
Structure Overview
When first learning a tool or language, it’s often useful to start with an overview of folders and files structure. The recommended terraform structure is:
demo/
├── main.tf
├── outputs.tf
└── variables.tf
What the Files Do
The main.tf
file is where you define the resources for Terraform to provision. Example:
resource "random_pet" "this" {
length = var.length
}
The random_pet resource is useful for testing. It creates random but deterministic “pet names”.
The variables.tf
file defines variables that can be used to dynamically change what is provisioned. Example:
variable "length" {
type = number
default = 2
}
The outputs.tf
file defines a list of outputs that will show up when deploying with terraform apply
. Example:
output "pet" {
value = random_pet.this
}
The code for these examples is available for BoltOps Learn subscribers: 1-intro-basics/1-separate
One File for Quick Testing
For quick testing and learning, sometimes people throw everything into one main.tf
file.
variable "length" {
type = number
default = 2
}
resource "random_pet" "this" {
length = var.length
}
output "pet" {
value = random_pet.this
}
This brings up a good point. Terraform loads and evaluates all .tf
files in the folder and “flattens” them down. The main.tf
, variables.tf
, and outputs.tf
are just recommended files. Generally, I recommend sticking to these files, though. It’s what others looking through your code will expect to see.
The code for these examples is available for BoltOps Learn subscribers: 1-intro-basics/2-combined
Deploy Terraform Code
To deploy:
$ terraform init
$ 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:
# random_pet.this will be created
+ resource "random_pet" "this" {
+ id = (known after apply)
+ length = 2
+ separator = "-"
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ pet = {
+ id = (known after apply)
+ keepers = null
+ length = 2
+ prefix = null
+ separator = "-"
}
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
random_pet.this: Creating...
random_pet.this: Creation complete after 0s [id=viable-possum]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
pet = {
"id" = "viable-possum"
"length" = 2
"separator" = "-"
}
$
The terraform init
command “initializes” a Terraform working directory. This step evaluates your terraform code and downloads dependencies. It loads remote state, modules, and provider plugins like AWS. This is how terraform knows how to create cloud-specific resources. In a way, it’s like a bundle install
, but it’s more complex.
The terraform apply
is when the cloud infrastructure is actually deployed and created.
Update
To update the infrastructure, we’ll introduce and use a tfvars
file. A tfvars file is a convenient way to set one or more variable values.
Create a dev.tfvars
with these contents:
length = 3
Then apply again using the -var-file
option.
$ terraform apply -var-file dev.tfvars
random_pet.this: Refreshing state... [id=viable-possum]
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# random_pet.this must be replaced
-/+ resource "random_pet" "this" {
~ id = "viable-possum" -> (known after apply)
~ length = 2 -> 3 # forces replacement
separator = "-"
}
Plan: 1 to add, 0 to change, 1 to destroy.
Changes to Outputs:
~ pet = {
~ id = "viable-possum" -> (known after apply)
keepers = null
~ length = 2 -> 3
prefix = null
separator = "-"
}
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
random_pet.this: Destroying... [id=viable-possum]
random_pet.this: Destruction complete after 0s
random_pet.this: Creating...
random_pet.this: Creation complete after 0s [id=heavily-beloved-egret]
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
Outputs:
pet = {
"id" = "heavily-beloved-egret"
"length" = 3
"separator" = "-"
}
$
Using tfvars files allow you to specify different variable values for different deployed modules. This is useful because it allows you to use the same code to create infrastructure with different settings, like for dev and prod environments.
Destroy
Finally, let’s clean up and destroy the resources.
$ terraform destroy
random_pet.this: Refreshing state... [id=heavily-beloved-egret]
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:
# random_pet.this will be destroyed
- resource "random_pet" "this" {
- id = "heavily-beloved-egret" -> null
- length = 3 -> null
- separator = "-" -> null
}
Plan: 0 to add, 0 to change, 1 to destroy.
Changes to Outputs:
- pet = {
- id = "heavily-beloved-egret"
- keepers = null
- length = 3
- prefix = null
- separator = "-"
} -> null
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
random_pet.this: Destroying... [id=heavily-beloved-egret]
random_pet.this: Destruction complete after 0s
Destroy complete! Resources: 1 destroyed.
$
The random_pet now has been cleaned up.
Summary
That’s an elementary but important introduction to Terraform main components: Resources, Variables, Outputs. In the next post, Terraform HCL Intro 2: Function Analogy, we’ll use an analogy to compare these components to a “function” and also introduce locals.
The source code for these examples is available for BoltOps Learn subscribers:: terraform-hcl-tutorials/1-intro-basics
Want It to be Easier to Work with Terraform?
Check out Terraspace: The Terraform Framework.
The Terraform HCL Language Intro Tutorials
Thanks for reading this far. If you found this article useful, I'd really appreciate it if you share this article so others can find it too! Thanks 😁 Also follow me on Twitter.
Got questions? Check out BoltOps.
You might also like
More tools:
-
Kubes
Kubes: Kubernetes Deployment Tool
Kubes is a Kubernetes Deployment Tool. It builds the docker image, creates the Kubernetes YAML, and runs kubectl apply. It automates the deployment process and saves you precious finger-typing energy.
-
Jets
Jets: The Ruby Serverless Framework
Ruby on Jets allows you to create and deploy serverless services with ease, and to seamlessly glue AWS services together with the most beautiful dynamic language: Ruby. It includes everything you need to build an API and deploy it to AWS Lambda. Jets leverages the power of Ruby to make serverless joyful for everyone.
-
Lono
Lono: The CloudFormation Framework
Building infrastructure-as-code is challenging. Lono makes it much easier and fun. It includes everything you need to manage and deploy infrastructure-as-code.