Quick Answer
Install Terraform, authenticate with Azure (CLI or service principal), configure the azurerm provider, and deploy. Service principals are recommended for CI/CD; Azure CLI login works for local development.
Prerequisites
# Install Azure CLI
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
# Login to Azure
az login
# Set your subscription
az account set --subscription "My Subscription"
az account show
Authentication Methods
Method 1: Azure CLI (Local Development)
provider "azurerm" {
features {}
# Uses your az login session automatically
}
Method 2: Service Principal (CI/CD — Recommended)
# Create a service principal
az ad sp create-for-rbac --name "terraform-sp" \
--role Contributor \
--scopes "/subscriptions/YOUR_SUBSCRIPTION_ID"
provider "azurerm" {
features {}
subscription_id = var.subscription_id
client_id = var.client_id
client_secret = var.client_secret
tenant_id = var.tenant_id
}
# Set as environment variables (preferred over hardcoding)
export ARM_SUBSCRIPTION_ID="your-sub-id"
export ARM_CLIENT_ID="your-client-id"
export ARM_CLIENT_SECRET="your-client-secret"
export ARM_TENANT_ID="your-tenant-id"
Method 3: Managed Identity (Azure VMs/Pipelines)
provider "azurerm" {
features {}
use_msi = true
}
First Azure Resource
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.90"
}
}
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "main" {
name = "myapp-prod-rg"
location = "East US"
}
resource "azurerm_virtual_network" "main" {
name = "myapp-vnet"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
}
resource "azurerm_subnet" "app" {
name = "app-subnet"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.0.1.0/24"]
}
terraform init
terraform plan
terraform apply
Remote State with Azure Blob
# Create storage account for state
az storage account create --name myapptfstate --resource-group myapp-rg \
--sku Standard_LRS --encryption-services blob
az storage container create --name tfstate --account-name myapptfstate
terraform {
backend "azurerm" {
resource_group_name = "myapp-rg"
storage_account_name = "myapptfstate"
container_name = "tfstate"
key = "prod.terraform.tfstate"
}
}
Common Azure Resources
# Linux VM
resource "azurerm_linux_virtual_machine" "web" {
name = "web-vm"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
size = "Standard_B2s"
admin_username = "azureuser"
network_interface_ids = [azurerm_network_interface.web.id]
admin_ssh_key {
username = "azureuser"
public_key = file("~/.ssh/id_rsa.pub")
}
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts"
version = "latest"
}
}
Azure vs AWS Terraform Comparison
| Concept | AWS | Azure |
|---|---|---|
| Account isolation | AWS Account | Resource Group |
| Networking | VPC | Virtual Network |
| Identity | IAM | Azure AD + RBAC |
| Storage | S3 | Blob Storage |
| Compute | EC2 | Virtual Machines |
| Provider | hashicorp/aws | hashicorp/azurerm |
| Auth env vars | AWS_ACCESS_KEY_ID | ARM_CLIENT_ID |
Troubleshooting
| Error | Fix |
|---|---|
| AuthorizationFailed | Assign Contributor role to SP |
| SubscriptionNotFound | Check ARM_SUBSCRIPTION_ID |
| features {} required | Add empty features {} block to provider |
| MissingSubscription | Set subscription: az account set |
Related Articles
Conclusion
Terraform with Azure works best with service principals for CI/CD and Azure CLI for local development. Start with a resource group, add networking, then build up. Use Azure Blob Storage for remote state with locking.




