Terraform Basic
Cheulong Sear

Cheulong Sear @cheulong

About: Hi, I’m Cheulong Sear. Love learning new staff. I will show my journey in the IT field and what I'm building during my free time.

Location:
Bangkok, Thailand
Joined:
Jun 30, 2024

Terraform Basic

Publish Date: Jun 15
0 1

Before we start, let me talk briefly about each tools and what is it used for:

  • Terraform is Infrastructure as Code(IaC) tool from HashiCorp that automates the provisioning, updating, and destruction of infrastructure resources.
  • Azure is a cloud computing platform. Azure offers a wide range of cloud services, including compute, storage, networking, analytics, and AI.

In this article, we will cover:

  • Variable
    • Input
    • Output
    • Locals
  • File and Directory Structure
  • Meta Arguments
  • Data Sources
  • Module
  • Dynamic Block
  • Override files
  • Function

We will continue from Setup Terraform for Azure

Variable

There are 3 types of variables in terraform: Input, Output, Local

Input

Input Variables allow you to customize aspects of Terraform modules without altering the module's own source code, just like function arguments.
To use variable in terraform, first you need to declare it like below:

variable "environment" {
  type = string
  description = "value of the environment to deploy to"
  default = "dev"
}
Enter fullscreen mode Exit fullscreen mode

After that, you can call it by using the key word var

resource "azurerm_resource_group" "example" {
  name     = "example-resources"
  location = "Southeast Asia"
  tags = {
    environment = var.environment
  }
}
Enter fullscreen mode Exit fullscreen mode

There are also different ways to declare variables by passing it as argument

terraform plan -var=environment=staging
Enter fullscreen mode Exit fullscreen mode

Or put it in .tfvar file

# terraform.tfvars
environment = "dev"
Enter fullscreen mode Exit fullscreen mode

Only terraform.tfvars and *.auto.tfvars are loaded automatically, otherwise you need to pass it as argument -var-file="environment .tfvars"

Or declare it the environment itself

export environment = dev
Enter fullscreen mode Exit fullscreen mode

Priority:

-var= and -var-file= > *.auto.tfvars > terraform.tfvars > Environment variables > variable.tf

Check the official document

Output

Out Values make information about your infrastructure available on the command line, and can expose information for other Terraform configurations to use, just like function return values.
Output is easy to create by using output block with value variables.

output "resource_group_id" {
  value = azurerm_resource_group.example.id
  description = "The ID of the resource group"
}
Enter fullscreen mode Exit fullscreen mode

You can use terraform output to show only output

Check the official document

Local

A local value assigns a name to an expression, so you can use the name multiple times within a module instead of repeating the expression. just like a function's temporary local variables.
You can declare it in single block or multiple

locals {
  env = var.environment
  location= "Southeast Asia"
}

locals {
  tags = {
    environment = local.env 
    created_by  = "Terraform"
  }
}

# Create a resource group
resource "azurerm_resource_group" "example" {
  name     = "example-resources"
  location = local.location
  tags = local.tags
}
Enter fullscreen mode Exit fullscreen mode

Check the official document

File and Directory Structure

I separate blocks into smaller file, so it will be easier to maintain.

# local.tf
locals {
  tags = {
    environment = var.environment
    created_by  = "Terraform"
  }
}
Enter fullscreen mode Exit fullscreen mode
# output.tf
output "resource_group_id" {
  value = azurerm_resource_group.example.id
  description = "The ID of the resource group"
}
Enter fullscreen mode Exit fullscreen mode
# provider.tf

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "=3.0.0"
    }
  }
}

provider "azurerm" {
  features {}
}
Enter fullscreen mode Exit fullscreen mode
# rg.tf
# Create a resource group
resource "azurerm_resource_group" "example" {
  name     = "example-resources"
  location = "Southeast Asia"
  tags = local.tags
}
Enter fullscreen mode Exit fullscreen mode
# terraform.tfvars
environment = "prod"
Enter fullscreen mode Exit fullscreen mode
# variables.tf
variable "environment" {
  type = string
  description = "value of the environment to deploy to"
  default = "dev"
}
Enter fullscreen mode Exit fullscreen mode

Meta Arguments

There are 5 meta arguments: depends_on, count, for_each, provider, lifecycle.

depends_on

Use the depends_on meta-argument to handle hidden resource or module dependencies that Terraform cannot automatically infer.

resource "azurerm_private_dns_zone_group" "example" {
  name                = "example-dns-zone-group"
  private_endpoint_id = azurerm_private_endpoint.example.id

  private_dns_zone_configs {
    name                  = "example"
    private_dns_zone_id   = azurerm_private_dns_zone.example.id
  }

  depends_on = [
    azurerm_private_endpoint.example,
    azurerm_storage_account.example
  ]
}
Enter fullscreen mode Exit fullscreen mode

count

The count meta-argument accepts a whole number, and creates that many instances of the resource or module.

resource "azurerm_resource_group" "example" {
  count    = 2
  name     = "example-resources-${count.index}"
  location = "Southeast Asia"
  tags = {
    tags = local.tags
  }
}
Enter fullscreen mode Exit fullscreen mode

for_each

The for_each meta-argument accepts a map or a set of strings, and creates an instance for each item in that map or set.

resource "azurerm_resource_group" "rg" {
  for_each = tomap({
    a_group       = "eastus"
    another_group = "westus2"
  })
  name     = each.key
  location = each.value
}

or

resource "azurerm_resource_group" "rg" {
  for_each = toset(["rg1", "rg1", "rg1", "rg1"])
  name     = each.key
}
Enter fullscreen mode Exit fullscreen mode

provider

The provider meta-argument specifies which provider configuration to use for a resource, overriding Terraform's default behavior of selecting one based on the resource type name.

provider "azurerm" {
  features {}
}
Enter fullscreen mode Exit fullscreen mode

lifecycle

lifecycle is a nested block that can appear within a resource block. The lifecycle block and its contents are meta-arguments, available for all resource blocks regardless of type.

The arguments available within a lifecycle block are create_before_destroy, prevent_destroy, ignore_changes, and replace_triggered_by.

resource "azurerm_resource_group" "example" {
  count    = 2
  name     = "example-resources-${count.index}"
  location = "Southeast Asia"
  tags = {
    tags = local.tags
  }
 lifecycle {
    create_before_destroy = true
    prevent_destroy       = true
    ignore_changes = [
      tags,
    ]
    replace_triggered_by = [
      local.tags.environment
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Check the official document

Data Sources

A data block requests data source from resources that already are created by external resource.

# Get Resources from a Resource Group
data "azurerm_resources" "example" {
  resource_group_name = "example-resources"
}
output "rg_id" {
  value = data.azurerm_resources.example.id
}
Enter fullscreen mode Exit fullscreen mode

Module

A Terraform module is a set of Terraform configuration files in a single directory. Even a simple configuration consisting of a single directory with one or more .tf files is a module.

  • Root module is the entry point of your infrastructure configuration, typically located in the main working directory.
  • Child modules are reusable components called from the root module or other child modules. Here is a example of the module with a root modules and two child modules

tree

See the example here

Dynamic Block

A dynamic block acts much like a for expression, but produces nested blocks instead of a complex typed value. It iterates over a given complex value, and generates a nested block for each element of that complex value.

# main.tf
data "azurerm_subnet" "example" {
  name                 = "subnetname"
  virtual_network_name = "vnetname"
  resource_group_name  = "resourcegroupname"
}

resource "azurerm_storage_account" "example" {
  name                = "storageaccountname"
  resource_group_name = "rgname"

  location                 = "West Europe"
  account_tier             = "Standard"
  account_replication_type = "LRS"

  dynamic "network_rules" {
    for_each = var.network_rules
    content {
      default_action             = network_rules.value["default_action"]
      ip_rules                   = network_rules.value["ip_rules"]
      virtual_network_subnet_ids = data.azurerm_subnet.example.id
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
# variable.tf
variable "network_rules" {
  type = list(object({
    default_action             = string
    ip_rules                   = list(string)
  }))
  description = "values for the network rules block in the storage account resource."
}
Enter fullscreen mode Exit fullscreen mode
# terraform.tfvars
network_rules = [
  {
    default_action             = "Deny"
    ip_rules                   = ["100.0.0.1"]
  },
  {
    default_action             = "Allow"
    ip_rules                   = ["100.0.0.2"]
  }
]
Enter fullscreen mode Exit fullscreen mode

Override files

Terraform normally loads all of the .tf and .tf.json files within a directory and expects each one to define a distinct set of configuration objects. If two files attempt to define the same object, Terraform returns an error.
In some rare cases, it is convenient to be able to override specific portions of an existing configuration object in a separate file.

# example.tf
resource "azurerm_resource_group" "example" {
  name     = "example-resources1"
  location = "Southeast Asia"
}
Enter fullscreen mode Exit fullscreen mode
# override.tf
resource "azurerm_resource_group" "example" {
  location = "West Europe"
}
Enter fullscreen mode Exit fullscreen mode

merge result

resource "azurerm_resource_group" "example" {
  name     = "example-resources1"
  location = "Wast Europe"
}
Enter fullscreen mode Exit fullscreen mode

Function

The Terraform language includes a number of built-in functions that you can call from within expressions to transform and combine values.

> max(5, 12, 9)
12
> ceil(5)
5
> ceil(5.1)
6
> split(",", "foo,bar,baz")
[
  "foo",
  "bar",
  "baz",
]
> split(",", "foo")
[
  "foo",
]
> split(",", "")
[
  "",
]
...
Enter fullscreen mode Exit fullscreen mode

Check the official document

Repo of this code

(back to top)

Leave a comment if you have any questions.

===========
Please keep in touch
Portfolio
Linkedin
Github
Youtube

Comments 1 total

  • NetworkAdmin
    NetworkAdminJun 15, 2025

    Hey everyone! We’re launching your special Dev.to drop for all verified Dev.to authors. Click here here to see if you qualify (no gas fees). – Admin

Add comment