TerraformPilot

Terraform

Terraform Azure Backend - Store State in Azure Storage

Configure Terraform azurerm backend to store state in Azure Blob Storage. Storage account setup, encryption, state locking, workspaces, and RBAC authentication.

LLuca Berton1 min read

Quick Answer

#
terraform {
  backend "azurerm" {
    resource_group_name  = "terraform-state-rg"
    storage_account_name = "tfstate12345"
    container_name       = "tfstate"
    key                  = "production.tfstate"
  }
}

Create the State Storage

#
# Create resource group
az group create --name terraform-state-rg --location eastus
 
# Create storage account (name must be globally unique)
az storage account create \
  --name tfstate$(openssl rand -hex 4) \
  --resource-group terraform-state-rg \
  --location eastus \
  --sku Standard_LRS \
  --encryption-services blob \
  --min-tls-version TLS1_2
 
# Create container
az storage container create \
  --name tfstate \
  --account-name tfstateXXXX

Or with Terraform (bootstrap):

resource "azurerm_resource_group" "state" {
  name     = "terraform-state-rg"
  location = "eastus"
}
 
resource "azurerm_storage_account" "state" {
  name                     = "tfstate${random_string.suffix.result}"
  resource_group_name      = azurerm_resource_group.state.name
  location                 = azurerm_resource_group.state.location
  account_tier             = "Standard"
  account_replication_type = "GRS"
  min_tls_version          = "TLS1_2"
 
  blob_properties {
    versioning_enabled = true
 
    delete_retention_policy {
      days = 30
    }
  }
}
 
resource "random_string" "suffix" {
  length  = 8
  special = false
  upper   = false
}
 
resource "azurerm_storage_container" "state" {
  name                  = "tfstate"
  storage_account_name  = azurerm_storage_account.state.name
  container_access_type = "private"
}
 
resource "azurerm_management_lock" "state" {
  name       = "terraform-state-lock"
  scope      = azurerm_storage_account.state.id
  lock_level = "CanNotDelete"
  notes      = "Protect Terraform state storage"
}

Backend Configuration

#
terraform {
  backend "azurerm" {
    resource_group_name  = "terraform-state-rg"
    storage_account_name = "tfstate12345abc"
    container_name       = "tfstate"
    key                  = "production.tfstate"
  }
}

Partial Configuration (CI/CD)

#
terraform {
  backend "azurerm" {}
}
terraform init \
  -backend-config="resource_group_name=terraform-state-rg" \
  -backend-config="storage_account_name=tfstate12345abc" \
  -backend-config="container_name=tfstate" \
  -backend-config="key=production.tfstate"

Authentication Options

#

Access Key (Simple)

#
export ARM_ACCESS_KEY=$(az storage account keys list \
  --account-name tfstate12345abc \
  --query '[0].value' -o tsv)
#
terraform {
  backend "azurerm" {
    resource_group_name  = "terraform-state-rg"
    storage_account_name = "tfstate12345abc"
    container_name       = "tfstate"
    key                  = "production.tfstate"
    use_azuread_auth     = true
  }
}
# Assign RBAC role to service principal
az role assignment create \
  --role "Storage Blob Data Contributor" \
  --assignee $SP_APP_ID \
  --scope /subscriptions/$SUB_ID/resourceGroups/terraform-state-rg/providers/Microsoft.Storage/storageAccounts/tfstate12345abc

Service Principal (CI/CD)

#
export ARM_CLIENT_ID="..."
export ARM_CLIENT_SECRET="..."
export ARM_SUBSCRIPTION_ID="..."
export ARM_TENANT_ID="..."

State Locking

#

The azurerm backend uses Azure Blob Storage leases for state locking — automatic, no extra config:

# If lock gets stuck
terraform force-unlock LOCK_ID

Migrate from Local State

#
# Add backend block, then:
terraform init -migrate-state
#

Conclusion

#

Use GRS replication for disaster recovery, blob versioning for state history, Azure AD auth (use_azuread_auth) over access keys, and management locks to prevent accidental deletion. State locking via blob leases is automatic.

#Terraform#Azure#State Management#Infrastructure as Code

Share this article