Terraform Associate: Top 5 Learnings
Cody Antonio Gagnon
Posted on April 13, 2024
Unlock the power of Terraform for your career, project, or any opportunity with these essential tips and tricks from my Terraform Associate exam journey!
Overview:
- 🔦 Essential Commands
- ⌨️ Command line options for days
- 🧮 Functions, functions, and more functions!
- 🌟 Splatting it up
- 🧠 Getting into the
terraform state
of mind
1. 🔦 Essential Commands
terraform validate
- Validates the syntax of terraform files statically (before running a plan)
- Helps (quickly!) uncover any potential issues with syntax (e.g. typos, undeclared variables, etc.)
- Does not determine issues that may occur at plan or apply time (e.g. circular resource dependencies in which resources depend on each other for creation, API considerations for deployment on a given cloud provider, etc.)
NOTE: ensure when running this command that you're inside the directory containing your Terraform files!! This command will output "Success!" even when no Terraform files exist in the current directory 🥲
terraform fmt
- Ensures consistency and adherence to HashiCorp's canonical file formatting standards by automatically formatting your Terraform files
- Using the
-recursive
flag will format all the Terraform files under the current directory and its subdirectories options - While you'll need to know this command for the exam, this functionality is built into HashiCorp's extension for VS Code and can automatically format your Terraform configuration on save!
terraform console
- Think of this as a playground for testing out expressions. You can take the current Terraform state you have and interact with its values.
- You can use variables in the current Terraform workspace as its input as well as other state that's been applied - this makes it super easy to evaluate expressions to see if they'll get the job done (all without needing to apply a plan!)
- Additionally, if no default values exist for input variables, they can be initialized using
-var 'name_of_defined_variable=value_of_defined_variable'
on the command line or using either*.auto.tfvars
orterraform.tfvars
files when runningterraform console
2. ⌨️ Command line options for days
Learning terraform's command line options can save you time, helping you get the most out of the tools at your disposal! Some of my favorites include:
terraform apply -auto-approve
- skip the plan, just apply (exercising caution where necessary)
terraform apply -destroy
or shall I say terraform destroy
?
- did you know
terraform destroy
is actually just an alias forterraform apply -destroy
? I didn't either! - you can also use
-auto-approve
here but "I sure hope you know what you're doing"
terraform apply -replace
- Super helpful command to indicate to Terraform that a resource needs to be marked for replacement
- Example could include a database that didn't get provisioned correctly when Terraform thinks it was. Just replace it!
terraform apply -refresh=false
- Instead of refreshing state during the apply stage, Terraform will accept whatever it has as its plan, reducing the time it would otherwise take to refresh resources.
- This can cause issues if the current state does not match the target environment, it's important to ensure that state is reconciled before using this option!
3. 🧮 Functions, functions, and more functions!
String Functions
format
This interesting command can come in handy when you want to output a variable in a specific way. For instance, I may have a variable pi
with a value of 3.14159265358979323846264338... well using the format command I can truncate, approximate, and manipulate the value for display in other ways:
variable "big_ol_pi" {
description = "A big 'ol pi 🥧"
type = number
default = 3.14159265358979323846264338
}
# running this in terraform console
> format("This 🥧 is approximated to %.2f", var.big_ol_pi)
"This 🥧 is approximated to 3.14"
trimprefix
Super simple command that allows you remove part of the beginning of a string. Say we have the following variable:
variable "string_of_pi" {
description = "A string of 🥧"
type = string
default = "🥧3.14159265358979323846264338🥧"
}
# running this in terraform console
> trimprefix(var.string_of_pi, "🥧")
"3.14159265358979323846264338🥧"
trimsuffix
Same as trimprefix
, but at the end rather than the beginning!
variable "string_of_pi" {
description = "A string of 🥧"
type = string
default = "🥧3.14159265358979323846264338🥧"
}
# running this in terraform console
> trimsuffix(var.string_of_pi, "🥧")
"🥧3.14159265358979323846264338"
replace
Allows you to transform your pie into cake, or rather, takes an input string and substitutes the value(s) you specify with another value!
variable "string_of_pi" {
description = "A string of 🥧"
type = string
default = "🥧3.14159265358979323846264338🥧"
}
# running this in terraform console
> replace(var.string_of_pi, "🥧", "🍰")
"🍰3.14159265358979323846264338🍰"
Collection Functions
keys
Helps get the keys of a map. Can really be useful in a cinch - notice the keys are sorted in lexicographical order, which is probably different than the order they're created in!
variable "map_of_pies" {
description = "lotsa pi 🥧"
type = map(any)
default = {
"3.141592653589793238462643384" : "the number pi"
"'3.14'" : "literal approximation of the number pi"
"🥧" : "emoji pie"
"🍰" : "not a pie"
}
}
# running this in terraform console
> keys(var.map_of_pies)
tolist([
"'3.14'",
"3.141592653589793238462643384",
"🍰",
"🥧",
])
lookup
Great little function that can help return a single value based on the input. What differentiates this from a typical map index (using var.map_of_pies["🍰"]
syntax) is that it can fall back to a default value:
variable "map_of_pies" {
description = "lotsa pi 🥧"
type = map(any)
default = {
"3.141592653589793238462643384" : "the number pi"
"'3.14'" : "literal approximation of the number pi"
"🥧" : "emoji pie"
"🍰" : "not a pie"
}
}
# running this in terraform console
> lookup(var.map_of_pies, "👾", "🥧")
"🥧"
When a key isn't present inside of var.map_of_pies
, we elect to always return a pie emoji!
I like that default value more, tastes better
concat
Simply take two lists and make them one!
variable "list_of_pies" {
description = "more 🥧"
type = list(string)
default = [
"'3.14'",
"3.141592653589793238462643384",
"🥧",
]
}
variable "list_of_cakes" {
description = "definitely not 🥧"
type = list(string)
default = [
"🎂",
"🍰",
"🍥",
]
}
# running this in terraform console
> concat(var.list_of_pies, var.list_of_cakes)
concat(var.list_of_pies, var.list_of_cakes)
tolist([
"'3.14'",
"3.141592653589793238462643384",
"🥧",
"🎂",
"🍰",
"🍥",
])
Additionally, we can also take other functions that return a list and use them here as well!
variable "map_of_pies" {
description = "lotsa pi 🥧"
type = map(any)
default = {
"3.141592653589793238462643384" : "the number pi"
"'3.14'" : "literal approximation of the number pi"
"🥧" : "emoji pie"
"🍰" : "not a pie"
}
}
# running this in terraform console
> concat(keys(var.map_of_pies), values(var.map_of_pies))
tolist([
"'3.14'",
"3.141592653589793238462643384",
"🍰",
"🥧",
"literal approximation of the number pi",
"the number pi",
"not a pie",
"emoji pie",
])
formatlist
Just like with the format
command, formatlist
can apply its specification to a whole list of strings!
variable "list_of_shadys" {
description = "All the shadys"
type = list(string)
default = [
"who",
"what",
"chka chka slim-shady!",
]
}
# running this in terraform console
> formatlist("hi my name is %s", var.list_of_shadys)
tolist([
"hi my name is who",
"hi my name is what",
"hi my name is chka chka slim-shady!",
])
4. 🌟 Splatting it up
Have you ever woken up and just thought: wow, splats are great? Me neither, but someday I might, because it turns out these expressions can really improve readability and make it easier to wrap your head around some loops in Terraform!
So, what is a splat expression really?
A splat is really just a shorthand for "all the things in this list."
It's a syntatical sugar that you can use to convert all types of for
expressions for in Terraform to even simpler splat expressions!
Here's an example of converting a variable to a splat
variable "desserts" {
description = "A list of favorite desserts"
type = list(object({
emoji = string
name = string
}))
default = [
{
emoji = "🍰"
name = "Cheesecake"
},
{
emoji = "🍪"
name = "Chocolate Chip Cookie"
},
{
emoji = "🍦"
name = "Ice Cream"
},
{
emoji = "🍩"
name = "Donut"
},
{
emoji = "🧁"
name = "Cupcake"
}
]
}
# running this in terraform console
> [ for dessert in var.desserts : dessert.emoji ]
[
"🍰",
"🍪",
"🍦",
"🍩",
"🧁",
]
>
# is equivalent to
> var.desserts[*].emoji
tolist([
"🍰",
"🍪",
"🍦",
"🍩",
"🧁",
])
As we can see in the above, our for
expression, while totally readable, is less concise. You know the saying though: "once you understand splat you'll never want to go back!"
Now, splats are great for replacing for
expressions on lists, but they can also be used for conditional inputs on dynamic blocks. While we have focused on Terraform being able to get all elements from a list, we haven't discussed the special behavior splat expressions exhibit when applied to a single (non-collection) value. Say we have the following resource definition fo an azurerm_key_vault
, which utilizes a dynamic block:
resource "azurerm_key_vault" "example" {
name = "examplekeyvault"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = "standard"
dynamic "contact" {
for_each = var.key_vault_contact[*]
content {
email = contact.value.email
}
}
}
If we look into our variable definition, we have what looks like only a single object, that can have its variable by default initialized to a default value of null
:
variable "key_vault_contact" {
type = object({
email = string
name = string
})
default = null
}
This will plan just fine. Buy why, you may ask?
The splat operator can actually do more than just handle lists. It can also take a single value, such as an object or a map, and convert it behind the scenes to a list of a single object! If the value the object is null
, Terraform will take and convert it into an empty list. This makes it possible to for_each
over more complex data types in Terraform!
Curious to splat it up even more? I recommend checking Terraform Docs!
5. 🧠 Getting into the terraform state
of mind
Last but not least, where would we be without out our beloved terraform state
command? While there is also now a moved
block, making some state transitions even more reliable/repeatable through GitOps, for the purpose of the Terraform associate exam here are three use cases of the imperative: a still valuable command to know for your state needs!
1. Change the name of a resource/module
terraform state mv my_provider.pie my_provider.cake
- Simple, on the tin changing of a resource's name.
- Additionally, we can provide
-dry-run
as an option to view what changes would be performed before doing so.
2. Move a resource into a module
terraform state mv my_provider.pie module.pie_soft.pie
- This can be super helpful if you are testing out the changes to a given resource, get it how you'd like it, and then want to bring those changes into a module.
- Or if you simply didn't have the module before to bring the changes into, this can be a lifesaver!
- Additionally, you can choose to rename the resource as you wish to fit the module's naming conventions
3. Move a module into another module
terraform state mv module.pie_soft.pie module.cake_soft.pie
- Finally, last, but certainly not least, we have the ability to move an entire module into another module!
- If we are at this point, we may be using the imperative command to move modules between two completely different terraform projects.
- For this we can use the options
-state=../my_dir/a -state-out=../my_dir/b
, meaning that we can tell Terraform to take existing module state from other projects and bring it into our current project accordingly!
- For this we can use the options
Those are my five learning takeaways from studying and taking the Terraform Associate exam!
I'm hopeful this post will serve as a valuable resource throughout your Terraform journey.
Please feel free to comment with your experience, suggestions, and anything else you'd be curious to learn or share!
-- 🗺️ 🧠 🤩 Happy Terraforming
I'd also like to shout out to a few awesome resources that helped me study along the way:
Posted on April 13, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.