Terraform for Azure: State and Backends

This is part of a blog series about using Terraform for provisioning Azure resources. In the first part I showed you how to get started with Terraform; how to install, initialize and create a simple resource. Now let's talk about State!

Terraform needs to store state about the resources you have created or imported. The state is used by Terraform to determine what resources need to be created, updated or deleted.

If you don't specify where to store state, like in the first part, Terraform will create a "terraform.tfstate" file in the working directory and describe the resources you have provisioned in a JSON-format. If you've completed the first part, you can inspect the state file:

{
  "version": 4,
  "terraform_version": "0.13.2",
  "serial": 1,
  "lineage": "81323f06-ab5a-bae6-032f-2cc69149e906",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "azurerm_resource_group",
      "name": "mywebpage",
      "provider": "provider[\"registry.terraform.io/hashicorp/azurerm\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "id": "/subscriptions/95fdc259-900e-462b-876f-b1baad798b0b/resourceGroups/mywebpage-rg",
            "location": "westeurope",
            "name": "mywebpage-rg",
            "tags": null,
            "timeouts": null
          },
          "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo1NDAwMDAwMDAwMDAwLCJkZWxldGUiOjU0MDAwMDAwMDAwMDAsInJlYWQiOjMwMDAwMDAwMDAwMCwidXBkYXRlIjo1NDAwMDAwMDAwMDAwfX0="
        }
      ]
    }
  ]
}

It's not necessary to know how the content of tfstate files is structured, but you can see how Terraform describes the Resource Group we created in the previous part. It's also discouraged to edit the tfstate file yourself.

If you're in a team and several people are going to modify Azure resources, storing state in a local tfstate file is not ideal. You could have several people changing the state simultaneously and corrupting the state. To solve this, Terraform enables you to use Backends. Backends let's you store your state remotely and uses locks to prevent corruption.

For Azure, you can store the state in a Azure Storage. This will also enable you to run Terraform commands (like 'terraform apply') remotely, including execution from Azure Devops or Github Actions. This will be covered in an other blog post.

Let's go back to the Terraform project we started in the first part, and setup a Backend using Azure Storage. First we'll need to set up a Azure Storage Account and a Storage Container. This will have to be done manually in the Azure Portal or with Azure CLI. Here's how I set it up in the Azure Portal:

Set up a Storage Account in Azure

And here is the same with Azure CLI commands:

//Login
az login

//Create a resource group
az group create --name terraform-rg --location norwayeast

//Create the Storage Account
az storage account create
    --name terraformsamplestorage
    --resource-group terraform-rg
    --location norwayeast
    --sku Standard_RAGRS
    --kind StorageV2

Now go to the start of your 'main.tf' file and configure the Backend like this:

terraform {
  backend "azurerm" {
    resource_group_name  = "terraform-rg"
    storage_account_name = "terraformsamplestorage"
    container_name       = "terraform"
    key                  = "samples.terraform.tfstate"
  }
}

This will make Terraform store the state file ('samples.terraform.tfstate') in a Storage Container (called 'terraform') in the Storage Account (called 'terraformsamplestorage').

To create the new state file, you will have to initialize Terraform (like we saw in part 1):

az login
az account set --subscription "<GUID of your subscription>"
terraform init
terraform plan
terraform apply

If you check the Storage Container, it should contain the 'samples.terraform.tfstate' file. That's it! You're now ready to share Terraform state with other people and systems.

In the next blog post, we'll continue to cover the basics and look at Modules. A module is a container for multiple resources that are used together, and let's you configure resources in seperate files (instead of having one large 'main.tf' file).