Chapter 2: Fundamental Terraform Building Blocks

Terraform uses the HashiCorp Configuration Language (HCL) to define infrastructure as code (IAC) for various platforms, including Azure. HCL is a domain-specific language (DSL) designed to be easy to read and use. It allows you to define and configure infrastructure resources in a simple, predictable way, making it a great choice for defining your Azure infrastructure. HCL has a straightforward syntax that is easy to learn, even for those new to programming. It uses blocks, denoted by curly braces, to define resources and other elements in your configuration.

Here are the fundamental building blocks of Terraform:

In Chapter 5, we will use all of the building blocks of Terraform. Below, we briefly describe each of them

Resources

A resource is a component of your infrastructure that you can manage with Terraform. Examples of resources include virtual machines, network switches, and DNS records.

To create an Azure resource group with Terraform, you can use the azurerm_resource_group resource. Here is an example of how to use this resource to create a resource group:

resource "azurerm_resource_group" "example" {
  name     = "example-rg"
  location = "North Europe"
}

This resource block creates a resource group called "example-rg" in the "North Europe" location.

Variables

Variables allow you to define values that can be reused throughout your configuration. You can also use variables to parameterize your configuration, making it more flexible and easier to maintain. You can set a default value and perform validation in a variable block. For example, you can define a variable for the location and use it in the resource block like this:

variable "location" {
  description = "The region where the resource will be deployed"
  type        = string
  default     = "North Europe"
  validation {
    condition     = can(regex("^West Europe$|^North Europe$", var.location))
    error_message = "Location can be either `West Europe` or `North Europe`."
  }
}

resource "azurerm_resource_group" "example" {
  name     = "example-rg"
  location = var.location
}

Outputs

Outputs allow you to display values after a Terraform run. They can be used to display important information about your infrastructure, such as IP addresses or resource IDs.

variable "location" {
  description = "The region where the resource will be deployed"
  type        = string
  default     = "North Europe"
  validation {
    condition     = can(regex("^West Europe$|^North Europe$", var.location))
    error_message = "Location can be either `West Europe` or `North Europe`."
  }
}

resource "azurerm_resource_group" "example" {
  name     = "example-rg"
  location = var.location
}

output "resource_group_name" {
  description = "Name of the resource group"
  value       = azurerm_resource_group.example.name
}

Modules

Modules are self-contained packages of Terraform configurations that can be shared and reused across multiple projects. Modules allow you to define reusable blocks of infrastructure that you can use in your configurations, making it easier to manage your infrastructure as code.

Modules can include resources, variables, outputs, and other elements that are required to create and manage your infrastructure. They can be used to define complex infrastructure architectures or to simplify the process of creating and managing resources. In Chapter 6, we will cover how to use modules in your code. However, here is an example of how to use a module in your Terraform configuration:

module "resource_group" {
  # Source it locally
  source = "path/to/module"
  # Source it from registry
  # source  = "yasarlaro/resource-group/azurerm"
  # version = "1.0.0" 

  resource_group_name = "example-rg"
  location            = "North Europe"
}

Providers

A provider is a plugin that Terraform uses to interact with a specific cloud or on-premises infrastructure. Providers must be configured in your configuration before you can use their resources. Providers are responsible for creating, updating, and deleting resources on the target platform.

The Azure Resource Manager (ARM) provider is used to interact with Azure resources. To use the ARM provider, you will need to specify it in your Terraform configuration. Here is an example of how to do this:

provider "azurerm" {
}

The Azure Resource Manager (ARM) provider can accept subscription authentication information. However, if your code will be pushed to a Git repository, it is not a good idea to include sensitive information such as authentication details in your code. It is a better idea to provide authentication details using environment variables, as mentioned in Chapter 1.

Data sources

Data sources allow you to fetch information about existing resources or external systems and use that information in your configurations. Data sources are read-only, which means that you cannot use them to create or modify resources.

Data sources are useful for retrieving information about existing resources or external systems that you want to use in your configuration. For example, you might use a data source to retrieve the ID of an existing Azure resource group, or to retrieve a list of available VM sizes in Azure.

Here is an example of how to use the azurerm_resource_group data source to retrieve information about an existing Azure resource group:

data "azurerm_resource_group" "example" {
  name = "example-rg"
}

output "resource_group_location" {
  value = data.azurerm_resource_group.example.location
}

Provisioners

Provisioners are used to execute scripts or other actions after a resource is created, updated, or destroyed. Provisioners are typically used to configure or customize resources, or to perform other tasks that are required for the resource to be fully functional.

Provisioners are useful for performing tasks that cannot be accomplished using Terraform's built-in resources and data sources. For example, you might use a provisioner to install software on a virtual machine, or to configure a database with specific users and permissions.

There are several types of provisioners available in Terraform, including local-exec, remote-exec, and file. Here is an example of how to use the local-exec provisioner to execute a script on a local machine after creating an Azure virtual machine:

resource "azurerm_virtual_machine" "example" {
  # ... other resource properties omitted for brevity

  provisioner "local-exec" {
    command = "./configure-vm.sh"
  }
}

Terraform state

Terraform state is a record of your infrastructure and the resources that have been created as part of that infrastructure. Terraform stores the state in a file or in a remote storage location, such as Azure Storage or Terraform Cloud.

The state is an important part of the Terraform workflow, as it allows Terraform to know what resources exist and what their current properties are. When you make changes to your infrastructure using Terraform, the state is updated to reflect those changes.

Here is an example of how to use an existing Azure Storage account to store the state file:

terraform {
  #... other properties omitted for brevity
  backend "azurerm" {
    resource_group_name = "rg-common-tf-state-neu"
    storage_account_name = "onursa001"
    container_name = "tfstate"
    key = "dev.basic-infra.tfstate"
  }
}

In this example, the terraform block is used to specify that the state should be stored in the Azure Storage account using the Azure Resource Manager (ARM) backend. The resource_group_name property specifies the resource group name of the storage account, storage_account_name property specifies the name of the storage account, the container_name property specifies the name of the container in the storage account where the state should be stored, and the key property specifies the name of the state file.

The state is an important part of the Terraform workflow and is used to track the resources that have been created as part of your infrastructure. By storing the state in a remote location, such as Azure Storage, you can ensure that the state is accessible.

When using an Azure Storage Account, Terraform will automatically lock your state file during any state-writing operations to prevent others from corrupting it. Details about this operation can be reviewed here.

You can also import existing resources into your state file by following the examples provided in the Terraform resource documentation. For example, the azurerm_resource_group resource definition includes instructions for importing an existing resource to a state file.

terraform import azurerm_resource_group.example <Azure Resource ID>

Last updated