Introduction to Terraform

Introduction to Terraform


Cloud Providers

In the past if you needed more resources, you had to order them, then go to a lengthy process to install and configure it.

The migration to cloud give developers way more flexibility than it was possible before, now you could have more resources with a few clicks instead of days/weeks.


Cloud Providers

Most of them allow you to create resources manually, but this causes issues such as:


Introduction to Terraform

To solve this issues the concept of IaC (Infrastructure as code) was created. They allow you to write down your infrastructure desired state as code and it will be used to deploy real infrastructure.

This strategy allows you to create resources in a automated and predictable way reducing errors and keeping history of changes.

Terraform is an IaC tool created by Hashicorp.


Terraform Characteristics


HCL Code Example

terraform {
  required_providers {
    vercel = {
      source = "vercel/vercel"
      version = "~> 0.3"
    }
  }
}

variable "vercel_api_token" {
	description = "Vercel API TOKEN"
	type = string
	sensitive = true
} 

provider "vercel" {
	api_token = var.vercel_api_token
}

resource "vercel_project" "project" {
  name = "todo-list"
  framework = "sveltekit"
  git_repository = {
    type = "github"
    repo = "rodrigosaint/future-proof"
  }
  root_directory = "frontend"
}

Terraform State

Terraform state is a mechanism that terraform uses to know what has been deployed and it is referred back when executing commands so it can know what to create, edit or delete.

It is a JSON dump containing all the metadata about your Terraform deployment as well as details about all the resources that it has deployed.

It is named terraform.tfstate by default and stored in the same directory where your terraform code is, but for better integrity and availability it can also be stored remotely, by using the backend feature.


Terraform Workflow

There are 3 major steps in the terraform workflow:

  1. Write - You write your terraform code.

  2. Plan - You use the plan command to review the changes that will be applied to your infrastructure.

  3. Apply - Deploying the changes you made, provisioning real infrastructure.

flowchart LR
    Write ----> Plan 
    Plan --> Write
    Plan ----> Apply

Terraform Init

terraform init initialises the working directory that contains your Terraform code. It follows this steps:

  1. Downloads components - Download modules and plugins (eg. providers and modules)

  2. Sets up backend - Sets up backend that will store Terraform state file (eg. s3, terraform cloud).

This is a command that should be executed when you start a terraform or when you add/update providers or modules.

The downloaded providers are binaries that execute API calls to vendors, those API calls are abstracted away by terraform.


Terraform Plan

terraform plan reads the code, creates and shows a plan of the execution/deployment.

The plan allows the developer to review the action plan before executing anything.


Terraform Apply

terraform apply deploys the instructions and statements in the code provisioning the infrastructure specified.

It updates the deployment state tracking file.


Terraform Destroy

terraform destroy is about deprovisioning infrastructure. It looks at the recorded state file created during terraform apply and deprovisions all resources created by it.

This command should be used with caution as it is a non-reversible command. Take backups and be sure that you want to delete infrastructure.


Terraform Providers

Providers are terraform's way of abstracting integrations with API control layer of the infrastructure vendors.

They can only be written in Go lang and when you run terraform init only its binary is downloaded.

Terraform finds and install providers when initialising working directory (terraform init), by default, looks for providers in Terraform providers registry/

Providers are plugins. They are released on a different pace from Terraform itself, and each provider has its own series of version numbers, so as a best practice providers should have a specific version.


Configuring providers

Configuring AWS provider:

# provider is a reserved keyword
# aws tells the name of the provider that will be fetched on public terraform registry
provider "aws" {
	region = "us-east-1" # this allows you to pass configuration parameters
}

Configuring Google provider:

providers "google" {
	credentials = file("credentials.json")
	project     = "my-project"
	region      = "us-west-1"
}

Resources

Declaring providers will give you access to define the resources associated to them. In the example bellow you are creating an EC2 instance:

resource "aws_instance" "web" {
	ami           = "ami-a1b2c3d4"
	instance_type = "t2.micro"
}

You can access the output of this resource following the pattern [resource-type].[resource-alias].[resource_property]


Querying Resources

You can fetch information from other resources using the data keyword.

data "aws_instance" "my-vm" {
	instance_id = "i1234567890" # What will be queried, this will vary depending on the resource type
}

The providers will have a set of queries for their resources, and it is possible to access the values using data.[resource-type].[resource-alias].[resource_property]


Terraform Variables

# variable is a reserved keyword
# then comes the name of the variable
variable "my-var" {
	# all the properties are optional
	description = "My test variable"
	type = string
	default = "Hello"
	sensitive = true # hides the value from the output
}

To reference a variable use the following structure var.[variable-name]

Variables can be defined anywhere but as a best practice they should be stored on variables.tf


Variable Validation

variable "my-var" {
	# all the properties are optional
	description = "My test variable"
	type = string
	default = "Hello"
	validation {
		condition = length(var.my-var) > 4
		error_message = "The string must be more than 4 characters"
	}
}

Terraform Outputs

output "instance_ip" {
	description = "VM's private IP"
	sensitive = false
	value = aws_instance.my-vm.private_ip # The only config parameter required
}

Output variable values are shown on the shell after running terraform apply.


Terraform Modules

It is just another folder or collection of terraform code. It groups multiple resources that are used together making code more reusable.

Every terraform configuration has at least one module, called the root module, which consists of code files in your main working directory.


Terraform Modules

Modules can be referenced from:

Local files

module "name-of-the-module" {
	source = "./modules/vpc" # mandatory
	version = "0.0.5" # module version
	region = var.region # input parameters
}

Public registry

module "name-of-the-module" {
	source = "hashicorp/consul/aws" # mandatory
	version = "0.0.5" # module version
	region = var.region # input parameters
}

Private registry

module "name-of-the-module" {
	source = "app.terraform.io/example-corp/k8s-cluster/azurerm" # mandatory
	version = "0.0.5" # module version
	region = var.region # input parameters
}

More content

I have all my notes regarding Terraform and Terraform Certification here - https://www.rodsantos.dev/learning/terraform-certification-terraform-associate-003/


Questions?