Skip to main content

Terraform Workflow with Azure: Write, Plan and Apply

Introduction #

This is a continuation of getting Terraform ready to use with Azure.

The basic Terraform workflow is:

  1. Write the configuration.
  2. Plan the changes.
  3. Apply the configuration to Azure.

In this example, the project deploys a simple Azure resource group so the workflow is easy to follow.

Write #

Terraform uses a directory as a workspace, not an individual file. When using the Terraform CLI, the current working directory is treated as the root module.

Terraform files normally use the .tf extension. There is also a JSON-based variant that uses .tf.json, but this example uses .tf.

A common file name is main.tf. This file usually contains the main configuration for a Terraform project.

For larger projects, the configuration can be split into multiple .tf files. For example, an App Service could be defined in one file and an Azure SQL instance in another. Terraform treats all .tf files in the same directory as one configuration, so splitting files is mainly for readability.

For this project, create this folder structure:

1
2
3
Terraform Projects
+-- resource group
    +-- main.tf

The first deployment will run from the resource group directory.

Terraform and Provider Blocks #

A Terraform file starts with a provider setup. The provider tells Terraform which platform it is working with, such as Azure, AWS, or GCP.

For Azure, use the AzureRM provider from the Terraform Registry.

The configuration includes a terraform block and a provider block:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.0"
    }
  }
}

provider "azurerm" {
  features {}
}

Terraform is case sensitive, so capitalization matters.

The required_providers block specifies the provider source and version. Without a version, Terraform may use the latest provider version. Specifying a version gives more control and helps avoid unexpected breaking changes when the same code is reused in another environment.

The provider block sets the local provider configuration used by the Terraform code.

Resource Group Resource #

Next, add a resource group resource from the AzureRM provider documentation.

The AzureRM resource group resource requires:

  • name
  • location

Tags are optional.

Use a resource block like this:

1
2
3
4
resource "azurerm_resource_group" "resource_group" {
  name     = "terraform-rg"
  location = "West Europe"
}

The block starts with resource.

After that comes the resource type:

1
azurerm_resource_group

Then comes the local name:

1
resource_group

The local name is used if this resource group needs to be referenced elsewhere in the Terraform configuration.

The name value is the name the resource group will have in Azure. In this example, the name is:

1
terraform-rg

Save the file before running Terraform commands, because Terraform reads the saved files in the working directory.

Initialize #

Before planning, initialize the directory from the same folder as main.tf:

1
terraform init

Initialization creates a hidden .terraform directory. This directory is used for provider plugins and modules.

Initialization also creates a dependency lock file:

1
terraform.lock.hcl

This lock file tracks provider dependencies and the provider version used by the configuration. Terraform manages this file.

Azure Sign-In #

Sign in to Azure with an account that has permission to deploy a resource group:

1
az login

If needed, update the Azure CLI account extension:

1
az extension add --upgrade -n account

Check the current subscription:

1
az account show

If there is more than one subscription, list the subscriptions:

1
az account list

Set the correct subscription:

1
az account set --subscription "<subscription-name>"

Then verify again:

1
az account show

This is important before running terraform plan and terraform apply.

Plan #

Run the plan command:

1
terraform plan

Terraform plan reads the current state of remote objects, compares the current configuration with the prior state, and creates a plan for the changes that will take place.

For this example, the plan should show that one item will be added: the Azure resource group.

Apply #

If the plan looks correct, apply the configuration:

1
terraform apply

Terraform will show a confirmation prompt. Enter:

1
yes

If the apply succeeds, Terraform should show that one item was added.

Terraform apply also performs a plan before applying. You can skip a separate terraform plan when you are confident with the configuration, but reviewing the plan first is still useful.

Verify the Resource Group #

Use Azure CLI to check that the resource group exists:

1
az group list --query "[?name=='terraform-rg']"

If the resource group name is different, update the query with the name used in the Terraform configuration.

After apply, Terraform also creates a state file:

1
terraform.tfstate

This state file tracks the status of resources Terraform manages and maps real Azure resources to the Terraform configuration.

Destroy #

To remove the resources created by Terraform, run:

1
terraform destroy

Terraform will ask for confirmation. Enter:

1
yes

This removes the resources managed by the configuration, so use it carefully in production.

After destroy finishes, run the resource group query again:

1
az group list --query "[?name=='terraform-rg']"

If nothing returns, the resource group has been removed.

Summary #

That is the basic Terraform workflow with Azure:

  1. Write the Terraform configuration in a working directory.
  2. Define the AzureRM provider.
  3. Add an Azure resource group resource.
  4. Run terraform init.
  5. Sign in to Azure and confirm the subscription.
  6. Run terraform plan.
  7. Run terraform apply.
  8. Verify the resource group.
  9. Run terraform destroy when the resource is no longer needed.

The AzureRM reference documentation is important because it shows the supported resources, required arguments, optional settings, and examples for each resource.