DevOps Rich DevOps content for Infrastructure Engineers

Terraform Basics

Workflow

Blocks

3 Fundamental Block Types:

Note: Only constant values are allowed in this block no variable references.

Additional Block Types:

Providers

Provider plugins are downloaded by terraform cli and used to interact with Cloud APIs e.g. the azure provider communicates with the Microsoft Azure API. They have their own release cycles and version numbers.

Required Provider Block

This is contained within the terraform block and includes the source and version as shown below:

terraform {
  required_version = ">= 1.0.0"
  required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
      version = ">= 2.0"
    }    
  }
}

The source is prefixed with registry.terraform.io by default.

Provider block

This block allows you to change the default behaviour of a provider such as:

Note: You can call a provider alias and thus feature settings in a resource block.

provider "azurerm" {
  features {}
}

Note: Local names should match between the required_providers and provider block. It is recommended to use the vendor preferred name e.g. azurerm but you could use anything in theory.

Dependency Lock File

It prevents you from updating a provider plugin version. It is best practice to check in the lock file into your code repository.

terraform init -upgrade - this will upgrade the lock file if you decide to change the allowed provider versions.

Note: It only includes provider version tracking not Terraform cli nor modules.

Provider Badges

When you navigate to the terraform registry list of providers you will notice they have 1 of 3 badges:

Resource Block Syntax

It contains:

resource "azurerm_resource_group" "resource_group" {
  location = "uksouth"
  name = "rg-01"  
}

Internal Block

A block within a resource block e.g. Ip_configuration {} in azurerm_network_interface resource.

Note: Whereas, tag = {} is an argument using a map value.

Resource Behaviour

Note: It first destroys and then recreates. You can change this behaviour using meta-arguments.

State

Note: It is not recommended to manually edit the state file nor store it locally.

Meta-arguments

Changes behaviour of resource blocks:

Note: Provisioners & Connections are not meta-arguments, they can invoke extra actions after resource creation or destruction e.g. install app on VM.

depends_on

depends_on = [
    azurerm_virtual_network.vnet1,
    azurerm_subnet.subnet1
  ]

Count

resource "azurerm_public_ip" "pip" {
  count = 2
  name                = "pip-${count.index}"
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  allocation_method   = "Static" 
}

for_each

For_each = {
Dev = "myapp1"
}

Each.key = dev

Each.value = myapp1
 for_each = azurerm_network_interface.nic

lifecycle

lifecycle {
    create_before_destroy = true
  }
  1. Prevent_destroy - database/ disk that you do not want to be removed.
lifecycle {
    prevent_destroy = true
  }
  1. Ignore_changes - ignore change made within Azure portal e.g. tags.
lifecycle {
    ignore_changes = [ tags, ]
  }

Functions

file function

This can be used to read the contents of a file e.g. obtain ssh public key within azurerm_linux_virtual_machine resource block.

admin_ssh_key {
    username = "localadmin"
    public_key = file("${path.module}/ssh-keys/key.pub")
  }

filebase64

This can be used to read the contents of a file Base64-encoded e.g. used by the Custom_data argument within azurerm_linux_virtual_machine resource block to reference cloud init file that can configure a VM.

custom_data = filebase64("${path.module}/scripts/cloud_init.txt")

Element

element(["blue","red","green"], 1)
red

Length

length(["blue","red","green"])
3

Toset

resource "azurerm_resource_group" "rg" {
  for_each = toset([ "uksouth", "ukwest" ])
  name = "rg-${each.key}"
  location = each.key 
}

Splat expression

The following example illustrates how to reference an instance of a NIC when both resource block azurerm_windows_virtual_machine and azurerm_network_interface are using the count meta-argument:

network_interface_ids = [
    element(azurerm_network_interface.winvm_nic[*].id, count.index)
  ]