By the end of this lab, you'll be able to:
- Understand why remote Terraform state is essential — for collaboration, consistency, and locking
- Create an Azure Resource Group — as the logical container for your Terraform backend resources
- Provision an Azure Storage Account and Blob Container — to store and lock the Terraform state file securely
⏱️ Estimated Time: ~10 minutes
Before starting, ensure you have:
- Azure CLI installed and authenticated (
az login) - Bash terminal (macOS/Linux or WSL on Windows)
- Completed Lab 1.1 — Azure DevOps Setup
- Active Azure subscription with Contributor or Owner permissions
-
📂 Open the storage creation script
Open
scripts/create-terraform-storage.shand update the following variables to match your environment:RESOURCE_GROUP_NAME="devops-journey-rg-apr2026" STORAGE_ACCOUNT_NAME="devopsjourneyapr2026" LOCATION="uksouth" CONTAINER_NAME="tfstate"
⚠️ Storage account names must be globally unique, 3–24 characters, lowercase alphanumeric only. Choose a name unique to you.
-
▶️ Execute the scriptcd labs/1-Initial-Setup chmod +x scripts/create-terraform-storage.sh ./scripts/create-terraform-storage.sh✅ Expected Output:
Creating resource group: devops-journey-rg-apr2026 { "id": "/subscriptions/.../resourceGroups/devops-journey-rg-apr2026", "location": "uksouth", "name": "devops-journey-rg-apr2026" } Creating storage account: devopsjourneyapr2026 Storage account created successfully. Creating blob container: tfstate Blob container created successfully. ✅ Terraform backend resources created: Resource Group : devops-journey-rg-apr2026 Storage Account: devopsjourneyapr2026 Container : tfstate
What the script does:
- Creates an Azure Resource Group for all DevOps journey resources
- Creates an Azure Storage Account with LRS redundancy and HTTPS-only enforcement
- Creates a Blob Container named
tfstateto store the Terraform state file - Enables blob versioning and soft-delete for state file protection
Make note of the following — you will need them in the pipeline YAML configuration in Lab 2:
# Retrieve storage account key (needed for Terraform backend config)
az storage account keys list \
--resource-group devops-journey-rg-apr2026 \
--account-name devopsjourneyapr2026 \
--query "[0].value" -o tsv💡 In this lab we use the WIF service connection for authentication, so the storage key is only needed if you choose key-based backend auth. The pipeline uses the service connection's identity to access the storage account.
Infrastructure checklist:
- Resource group
devops-journey-rg-apr2026exists in Azure Portal - Storage account
devopsjourneyapr2026is present within the resource group - Blob container
tfstateexists inside the storage account
# Validate resource group
az group show --name devops-journey-rg-apr2026 --query "{Name:name, Location:location, State:properties.provisioningState}" -o table
# Validate storage account
az storage account show \
--name devopsjourneyapr2026 \
--resource-group devops-journey-rg-apr2026 \
--query "{Name:name, SKU:sku.name, HTTPS:enableHttpsTrafficOnly}" -o table
# Validate blob container
az storage container show \
--name tfstate \
--account-name devopsjourneyapr2026 \
--auth-mode login \
--query "{Name:name, PublicAccess:properties.publicAccess}" -o table✅ Expected Output:
Name Location State
------------------------- ---------- ---------
devops-journey-rg-apr2026 uksouth Succeeded
Name SKU HTTPS
---------------------- ---------- -------
devopsjourneyapr2026 Standard_LRS True
Name PublicAccess
-------- ------------
tfstate None
You can also verify visually in the Azure Portal:
🔧 Troubleshooting (click to expand)
# Problem: Storage account name already taken
# Solution: Choose a different, more unique name
# The name must be 3-24 lowercase alphanumeric characters and globally unique
STORAGE_ACCOUNT_NAME="devopsjourneyapr2026v2"
# Problem: Insufficient permissions to create resource group
# Solution: Ensure your account has Contributor or Owner on the subscription
az role assignment list --assignee "$(az account show --query user.name -o tsv)" \
--query "[].roleDefinitionName" -o tsv
# Problem: "AuthorizationFailed" on container list
# Solution: Assign yourself Storage Blob Data Contributor on the storage account
az role assignment create \
--assignee "$(az account show --query user.name -o tsv)" \
--role "Storage Blob Data Contributor" \
--scope "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/devops-journey-rg-apr2026/providers/Microsoft.Storage/storageAccounts/devopsjourneyapr2026"- Remote state enables collaboration — multiple engineers can run
terraform plan/applyagainst the same state without conflicts. Local state is per-developer and leads to drift. - State locking prevents concurrent modifications — Azure Blob Storage supports lease-based locking so only one
terraform applyruns at a time. Without locking, parallel runs can corrupt the state. - Terraform loses track of resources without state — it would try to create all resources again, causing duplication or errors. Always back up state files using versioning and soft-delete.
- The blob container must not have public access — the state file may contain sensitive values including secrets, passwords, and keys in plaintext.
Your Terraform remote state backend is ready. The storage account and container details will be used to configure the backend in your pipeline YAML in Lab 2.
← Back to Lab 1.1 | Continue to Lab 1.3 →