Terraform Workbook - Your Guide to Infra as Code (IaC)
A companion that helps you create and run Terraform projects with ease. Provides simple explanations and sample contents for common Terraform files, useful related documentation links and essential management commands.
Terraform Project files and purposes
vars.tf- default variables declarationsterraform.tfvars- set or override default variables valuesterraform.tf- tfstate backends and providers declarationsversion.tf- terraform version constraints.terraform.lock.hcl- takes a picture of the installed dependencies for your Terraform project (providers versions essentially). Generated for the first time at Terraform initialization. Should also be place inside your SCV system (e.g, Git) to ensure others get the same providers versions.
A Terraform project also contains other files properly named (e.g, network.tf, database.tf, etc) depending on what you do. Inside thoses files you use 'modules' or specific providers documented 'resources' to manage your infrastructure:
Example vars.tf file
Contains default variables declarations.
# Default variables declarations
# String
variable "env" {
type = string
# Optionally set a description
description = "Environment of the platform"
# Optionally set a default value
default = "staging"
# Redact value from Terraform CLI log output
sensitive = true
# Mask the variable from plan outputs
# and do not store it in tfstate
ephemeral = true
}
# Map of string or object
variable "machine" {
type = map(string)
description = "Machine characteristics"
default = {
"machine_type" = "e2-highmem-2"
"image" = "ubuntu-os-cloud/ubuntu-2404-lts"
"data_disk_type" = "pd-standard"
}
}
# "machine" variable is an object of string.
# We could also declare that variable as follows:
variable "machine" {
type = object({
machine_type = string
image = string
data_disk_type = string
})
# optionally set default values
default = {
machine_type = "e2-highmem-2"
image = "ubuntu-os-cloud/ubuntu-2404-lts"
disk_type = "pd-standard"
}
}
Example terraform.tvars file
Used to set or override default variables values.
# Set or override default variables values
# Override string type variables
env = "prod"
# Override map or object type variables
machine = {
machine_type = "e2-highmem-4"
image = "ubuntu-os-cloud/ubuntu-2404-lts"
disk_type = "pd-ssd"
}
Example terraform.tf file
Contains Terraform's tfstate backends and providers declarations.
Useful documentations
Terraform.tf file for Microsoft Azure
# Tfstate backends and providers declarations (Azure)
terraform {
backend "azstorageaccount" {
storage_account_name = "my-azure-storage-account"
container_name = "tfstate"
key = "prod.my-project.tfstate"
}
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.46.0"
}
}
provider "azurerm" {
subscription_id = var.azure_subscription_id
features {}
}
}
Terraform.tf file for Google Cloud
# Tfstate backends and providers declarations (Google Cloud)
terraform {
backend "gcloudstorage" {
bucket = "mybucket"
prefix = "myproject/prod"
}
required_providers {
google = {
source = "hashicorp/google"
version = "~> 7.4.0"
}
}
provider "google" {
project = var.google_project_id
}
}
Example version.tf file
Contains constraints for Terraform version.
# Terraform version constraints
terraform {
required_version = "~> 1.9"
}
Terraform resources file
Where we declare the resources of the infrastructure Terraform is going to create (virtual machines, managed databases, etc).
- Terraform functions overview
- Terraform conditional expressions
- Terraform repeatable config blocks with dynamic
# File where you use modules or providers resources
# to create your infrastructure components
resource "google_sql_database_instance" "cloudsql-postgresql" {
(...)
name = "my-database-${var.env}"
project = var.gcp_project_id
# Get value from the postgredb map(string) or object
# type variable containing version as one of the keys
database_version = var.postgredb.version
# Set deletion_protection param value to true if
# var.env value is "prod" otherwise set to false
deletion_protection = var.env == "prod" ? true : false
(...)
}
Running Terraform
Installation
To install Terraform, have a look at Install Terraform.
You can also use the tfswitch utility to ease the process of installing and switching between different Terraform versions:
# Installing the latest tfswitch version on Linux
# This will deploy the tfswitch binary at /usr/local/bin/tfswitch
# Be root in order to write to /usr/local/bin/
sudo su
# Deploy tfswitch binary at /usr/local/bin/
curl -L https://raw.githubusercontent.com/warrensbox/terraform-switcher/master/install.sh | bash
# Go back to standard user
exit
# Run tfswitch and choose the Terraform version you want
tfswitch
# Configure your shell to find the newly
# installed terraform binaries for execution
export PATH="$PATH:${HOME}/bin"
bash # or invok your other shell (sh, zsh, etc)
# Verify
terraform version
Terraform init
# Initialize the working directory
$ terraform init
Initializing the backend...
Initializing modules...
Initializing provider plugins...
- Reusing previous version of hashicorp/azurerm from the dependency lock file
- Using previously-installed hashicorp/azurerm v4.46.0
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
Launch tasks required before being able to create or update the infrastructure:
- Initialize the backend: ensures the tfstate file is accessible. The tfstate file may be stored locally or remotely depending on your backend configuration.
- Initialize modules: ensures the Terraform modules you have declared are accessible and available locally
- Initialize provider plugins: ensures the binaries of the required providers plugins you have declared are accessible and available locally
The downloaded requirements (modules, providers, etc) will be available inside the '.terraform' directory. Ensure your source code version control system ignores it.
Terraform init reconfigure
# Reconfigure the backend
terraform init -reconfigure
Run this when you make changes to the backend configuration (new path for the tfstate or remote storage change).
Terraform init upgrade
# Upgrade providers versions and update the lock file
terraform init -upgrade
You can optionally upgrade the version of the providers plugins based on the version contraints you have declared by using the '-upgrade' flag. The '.terraform.lock.hcl' version lock file will be updated accordingly.
Terraform plan and apply
# Show the changes Terraform will make
terraform plan
# Apply the changes. Will again show you what
# will be performed and ask for a confirmation (yes or no)
terraform apply
You can optionally view or apply changes only for specific resources by using the '-target' flag. Here is an example:
# Target specific resources for plan or apply
terraform plan -target=google_compute_instance.myinstance
terraform apply -target=google_compute_instance.myinstance
Terraform state manipulation
# List resources inside Terraform state
terraform state list
# Show specific resources configurations from Terraform state
terraform state show <resource_id>
# Remove specific resources from Terraform state
terraform state rm <resource_id>
Terraform force-unlock
Sometimes, it may be useful to voluntarily unlock the Terraform state file, for instance after a previous run that didn't properly exit or for any other legitimate reason.
If Terraform state file is locked and you run 'terraform plan' or 'terraform apply', Terraform will tell you that the state file is locked and give you the lock ID.
To unlock the state file, simply run this:
terraform force-unlock -force <lock_id>
Terraform taint
If you want to make Terraform consider that a specific resource should be destroyed and recreated, you can use the taint command as follows:
# Taint a resource : tell Terraform that a
# resource should be destroyed and recreated
terraform taint <resource_id>
# Untaint the resource
terraform untaint <resource_id>
Want to report a mistake, ask questions or suggest improvement ? Feel free to email me at gmkziz@hackerstack.org.
If you like my articles, consider registering to my newsletter in order to receive the latest posts as soon as they are available.
Take care, keep learning and see you in the next post 🚀