To run Terraform safely across multiple people and environments, you need centralized remote state, locking to prevent concurrent runs, and robust authentication. This article distills practical operational tips for the azurerm backend on Azure Blob Storage, along with the points most commonly tested on the Associate / Pro exams.
Based on the official specification, we cover creation commands, authentication patterns, workspace separation, locking behavior, and CI/CD pitfalls with concrete fixes.
A Terraform backend defines where state is stored and how it is locked. On Azure, the standard choice is the azurerm backend with Blob Storage. Mutual exclusion via Blob leases prevents concurrent apply runs.
Storage has server-side encryption enabled by default, and Azure AD-based RBAC lets you grant fine-grained data-plane permissions. On the Terraform side, terraform init wires up the backend, and subsequent plan/apply runs read and update remote state.
| Backend | Locking | Default Encryption | Primary Authentication |
|---|---|---|---|
| local | None (risk of corruption with concurrent runs) | None | Local permissions only |
| azurerm (Azure Blob) | Mutual exclusion via Blob Lease | Server-side encryption (Microsoft-managed keys) | Azure AD (RBAC) / Managed Identity / Service Principal / (alternative) access key, SAS |
| s3 (reference) | Optional lock via DynamoDB table | Server-side encryption (SSE-S3 default) | AWS IAM (role/user) |
Azure Blob backend flow (including locking)
Minimal backend block (authentication supplied from the environment)
terraform {
backend "azurerm" {
resource_group_name = "rg-tfstate"
storage_account_name = "sttfstate1234"
container_name = "tfstate"
key = "network/terraform.tfstate"
# Credentials are sourced from the environment (Azure AD / CLI / Managed Identity / SP)
}
}
Provision a dedicated storage account for state. Names must be lowercase alphanumeric and 3 to 24 characters long. Split Blob containers per environment, or separate logically by key. Disable public access, and enable versioning and soft delete for easier recovery.
Before running terraform init, create the resource group, storage account, and container. When you authenticate with Azure AD, creating the container via CLI login authentication is the safest path.
Example creation with the Azure CLI and recommended property settings
# 1) Resource group
az group create -n rg-tfstate -l japaneast
# 2) Storage account (public access disabled)
az storage account create \
-n sttfstate1234 \
-g rg-tfstate \
-l japaneast \
--sku Standard_LRS \
--kind StorageV2 \
--allow-blob-public-access false
# 3) Blob versioning and soft delete (e.g. 30 days)
az storage account blob-service-properties update \
--account-name sttfstate1234 \
--enable-versioning true
az storage blob service-properties delete-policy update \
--account-name sttfstate1234 \
--enable true --days-retained 30
# 4) Create the container (using Azure AD login)
az storage container create \
--name tfstate \
--account-name sttfstate1234 \
--auth-mode login
Azure AD is the recommended way to authenticate the backend. You can use a service principal, Managed Identity, a local Azure CLI login, or Workload Identity (OIDC). For storage data-plane permissions, grant at least Storage Blob Data Contributor (scoping to the container level is preferable to minimize blast radius).
For environment-variable service principal authentication, set ARM_CLIENT_ID / ARM_CLIENT_SECRET / ARM_TENANT_ID / ARM_SUBSCRIPTION_ID. For Workload Identity federation (e.g. GitHub Actions), set client ID / tenant ID / subscription ID, and the OIDC token is sourced from the file or environment provided by the login action. Access keys and SAS tokens are also possible, but Azure AD is the safer default because key rotation and secret management get complex in production.
RBAC assignment and environment variable example (service principal authentication)
# Grant Storage Blob Data Contributor to the service principal (appId)
az role assignment create \
--assignee <APP_ID> \
--role "Storage Blob Data Contributor" \
--scope "/subscriptions/<SUB_ID>/resourceGroups/rg-tfstate/providers/Microsoft.Storage/storageAccounts/sttfstate1234"
# Environment variables for Terraform (SP secret authentication)
export ARM_CLIENT_ID=<APP_ID>
export ARM_CLIENT_SECRET=<CLIENT_SECRET>
export ARM_TENANT_ID=<TENANT_ID>
export ARM_SUBSCRIPTION_ID=<SUB_ID>
# (For Workload Identity / OIDC, the login action supplies the token; CLIENT_SECRET is not required)The backend block is static, so variable interpolation generally is not allowed. To separate state per environment, switch the key or split the container. For team operations, enforce a naming convention of project/environment/component.tfstate to prevent accidental cross-references.
A practical pattern is to fill in a partial configuration by passing -backend-config at terraform init time. To reflect the workspace name in the key, fetch the current workspace from a script and overwrite the key with init -reconfigure.
Example of -backend-config with workspace integration
# Example) switch the key based on the environment
RG=rg-tfstate
ST=sttfstate1234
CT=tfstate
WS=$(terraform workspace show)
KEY="projectA/${WS}/network.tfstate"
terraform init -reconfigure \
-backend-config="resource_group_name=${RG}" \
-backend-config="storage_account_name=${ST}" \
-backend-config="container_name=${CT}" \
-backend-config="key=${KEY}"
Locking is implemented via Blob leases, and concurrent applies are rejected. If a process aborts and leaves a stale lock, release it with terraform force-unlock (the lock ID is shown in the error message).
Encryption is enabled by default. Depending on requirements, you can switch to customer-managed keys (CMK) on the storage side. Enabling Blob versioning and soft delete makes rollback after a mistake easy by restoring a prior version.
Releasing locks and checking versioning
# Unlock (use the LOCK_ID shown in the error message)
terraform force-unlock -force 01234567-89ab-cdef-0123-456789abcdef
# Versioning enablement is covered in section 2. Restore prior Blob versions from the Portal or CLI.
In CI, the keys are non-interactive execution (-input=false) and reliable credential delivery. When the backend is reconfigured, specify -reconfigure explicitly. Because state can contain sensitive data, be careful about where you store plan files and what you log.
Common errors and fixes: 403 (AuthorizationPermissionMismatch) usually means insufficient RBAC; other typical culprits are a missing or misspelled container, mismatched tenant or subscription, and lock contention from concurrent jobs.
GitHub Actions example (OIDC + Azure AD)
name: tf-apply
on: [workflow_dispatch]
jobs:
apply:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.6.6
- name: Init backend
run: |
terraform init -reconfigure \
-backend-config="resource_group_name=rg-tfstate" \
-backend-config="storage_account_name=sttfstate1234" \
-backend-config="container_name=tfstate" \
-backend-config="key=projectA/prod/app.tfstate"
- name: Plan
run: terraform plan -input=false -out=plan.tfplan
- name: Apply
run: terraform apply -input=false -auto-approve plan.tfplan
Associate / Pro
問題 1
Which mechanism does the Terraform azurerm backend use to prevent concurrent state updates?
正解: A
The azurerm backend implements mutual exclusion via Azure Blob leases. Queue visibility timeouts and SMB locks do not apply here, and Terraform Cloud integration is not required.
Can I authenticate with Azure AD instead of a storage access key?
Yes. You can use a service principal, Managed Identity, Azure CLI login, or Workload Identity (OIDC). Grant at least the Storage Blob Data Contributor RBAC role.
What is the best practice for separating state per environment?
Use a dedicated container or storage account for production, and separate non-production environments by key naming such as project/env/component.tfstate. Since the backend block is static, switch keys at init time with -backend-config.
The lock will not release and I cannot apply. What should I do?
First make sure no other run is still in progress, then run terraform force-unlock -force <LOCK_ID> if needed. If this happens often, review CI serialization, timeout settings, and job-level mutual exclusion.
Practice with certification-focused question sets
無料で問題を解いてみるNicheeLab Editorial Team
NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.
HCL Syntax: Terraform's Configuration Language (2026)
HCL2 fundamentals for Terraform — blocks, attributes, expres...
Terraform Authoring & Operations Pro: Complete Guide (2026)
Tactics for the Terraform Pro exam — module authoring, works...
Terraform Providers: Plugin Management Fundamentals (2026)
Provider mechanics — required_providers, versions, mirrors, ...
Terraform Resource Blocks: Declarative Infra Units (2026)
Resource block fundamentals — addresses, references, common ...
Terraform Data Sources: Read-Only External Data (2026)
Data source basics — declaration, refresh behavior, dependen...