Terraform Basics

Terraform is a powerful tool for managing infrastructure as code (IaC). It allows you to define and provision data center infrastructure using a high-level configuration language known as HashiCorp Configuration Language (HCL). In this blog post, we'll dive into the basics of Terraform, explore its workflow, and provide practical examples to get you started.

Understanding HCL Basics

HCL is the backbone of Terraform, enabling you to define your infrastructure with blocks and parameters. Here's a simple structure:

<block> <parameters> {
    key1 = value1
    key2 = value2
}

For example:

resource "local_file" "pet" {
    filename = "/root/pets.txt"
    content  = "We love pets!"
}

resource "aws_s3_bucket" "data" {
    bucket = "webserver-bucket-org-2207"
    acl    = "private"
}

Terraform Workflow

Terraform's workflow is straightforward:

  1. Write Configuration Files: Define your infrastructure in .tf files.
  2. Initialize the Working Directory: Run terraform init to set up the directory and install necessary plugins.
  3. Create an Execution Plan: Use terraform plan to see what changes will be made.
  4. Apply the Changes: Execute terraform apply to apply the plan and create the resources.
  5. Update Resources: Repeat steps 3 and 4 to update resources.
  6. Destroy Resources: Clean up with terraform destroy.

Using Terraform Providers

Terraform providers are plugins that allow interaction with cloud providers, SaaS providers, and other APIs. Providers are categorized into:

  • Official Providers: Maintained by HashiCorp.
  • Partner Providers: Maintained by third parties.
  • Community Providers: Maintained by the community.

For example, hashicorp/local denotes the provider namespace and type.

providers

Managing Variables

Avoid hardcoding values by using input variables. This improves reusability and readability.

main.tf:

resource "local_file" "pet" {
    filename = var.filename
    content  = var.content
}

variables.tf:

variable "filename" {
    default = "/root/pets.txt"
}
variable "content" {
    default = "We love pets!"
}

Variable blocks can specify types, descriptions, and default values.

types

  • Lists: list(string), list(number)

  • Maps: map(string), map(number)

  • Sets: set(string) (sets cannot have duplicate values, whereas lists can)

  • Objects:

    object({
        name          = string
        color         = string
        age           = number
        food          = list(string)
        favorite_pet  = bool
    })
    
  • Tuples: tuple([string, number, bool])

Setting Variable Values via CLI

You can set variable values directly through the CLI:

terraform apply -var "filename=/root/pets.txt" -var "content=We love Pets!" -var "prefix=Mrs" -var "separator=." -var "length=2"

Alternatively, use environment variables:

export TF_VAR_filename="/root/pets.txt"

Or configuration files:

  • terraform.tfvars
  • terraform.tfvars.json
  • *.auto.tfvars
  • *.auto.tfvars.json

Example content of terraform.tfvars:

filename  = "/root/pets.txt"
content   = "We love pets!"
prefix    = "Mrs"
separator = "."
length    = "2"

Other variables need to be explicitly passed:

terraform apply -var-file=variables.tfvars

Resource Attributes and Dependencies

Resources can reference attributes from other resources, creating implicit dependencies. For explicit dependencies, use

depends_on.

resource "local_file" "pet" {
    filename = var.filename
    content  = "My favorite pet is Mr.Cat"
    depends_on = [random_pet.my-pet]
}

resource "random_pet" "my-pet" {
    prefix    = var.prefix
    separator = var.separator
    length    = var.length
}

Output Variables

Output variables store and display values from your infrastructure:

output "pet-name" {
    value       = random_pet.my-pet.id
    description = "Record the value of pet ID generated by the random_pet resource"
}

Managing State

Terraform keeps track of your infrastructure with a state file (terraform.tfstate). This file is critical for understanding resource dependencies and efficiently managing updates. For team collaboration, store the state file in a remote backend like S3 or Terraform Cloud, but never edit it manually.

Essential Terraform Commands

  • terraform validate: Validates the configuration files.
  • terraform fmt: Formats the configuration files.
  • terraform show: Displays the state or a plan.
  • terraform providers: Lists the providers used.
  • terraform output: Displays output variables.
  • terraform refresh: Syncs the state with real-world infrastructure.
  • terraform plan: Creates an execution plan.
  • terraform apply: Applies the changes.
  • terraform graph: Generates a visual representation of the configuration.

Mutable vs. Immutable Infrastructure

Terraform promotes immutable infrastructure, where changes involve creating new resources and decommissioning old ones. This practice simplifies versioning and rollbacks.

Lifecycle Rules

Lifecycle rules control resource creation and destruction:

resource "local_file" "pet" {
    filename = "/root/pets.txt"
    content  = "We love pets!"
    lifecycle {
        create_before_destroy = true
    }
}

Using Data Sources

Data sources retrieve information from external sources for use in your configuration:

resource "local_file" "pet" {
    filename = "/root/pets.txt"
    content  = data.local_file.dog.content
}

data "local_file" "dog" {
    filename = "/root/dog.txt"
}

resource vs data source

Meta-Arguments: depends_on, lifecycle, count, and for_each

Count

resource "local_file" "pet" {
    filename = var.filename[count.index]
    count    = length(var.filename)
}

For-Each

resource "local_file" "pet" {
    filename = each.value
    for_each = toset(var.filename)
}

Versioning

Specify version constraints to manage resource versions:

resource "local_file" "pet" {
    filename = each.value
    for_each = var.filename
}

In conclusion, Terraform is a versatile and powerful tool for managing infrastructure. By understanding HCL, workflow, providers, variables, resource attributes, dependencies, state management, and essential commands.