☁️ Introduction
Before Terraform can create resources in Azure, it needs to authenticate — to prove its identity.
Azure then checks authorization to determine what actions are allowed.
While authenticating via Azure Service Principal is ideal for automation outside Azure, Managed Identity (MI) is the most secure and simplest method when Terraform runs inside Azure — for example, from an Azure Virtual Machine (VM), Virtual Machine Scale Set (VMSS), Azure Kubernetes Service (AKS) node, Azure App Service, Azure Function, Azure Logic App (Standard), Azure Automation Account, Azure Container Instance (ACI), Azure DevOps Self-hosted Agent, Azure Cloud Shell, or Azure Arc-enabled Server — essentially, any environment that supports a Managed Identity.
In this post, we’ll:
✅ Authenticate Terraform using a Managed Identity (MI)
✅ Deploy a Virtual Network and Subnet using Terraform
🧩 Prerequisites
✅ Azure subscription
You’ll need an active Azure subscription with permissions to create and manage resources.
✅ A Linux (Ubuntu) VM
Terraform will run from inside this Azure VM.
You can create one by following the post How to Create an Ubuntu VM in Azure
✅ Managed Identity assigned
The compute resource running Terraform (e.g., VM) must have a Managed Identity with Contributor or sufficient permissions on the target subscription or resource group.
You can follow the post How to Assign a Managed Identity to an Azure Virtual Machine (VM)
✅ Terraform installed
You can follow Installing Terraform on Linux (Ubuntu) and Windows (WSL2 with Ubuntu).
✅ Azure CLI installed (optional)
Useful for verifying Managed Identity configuration or troubleshooting authentication issues. You can follow Getting Started with Azure CLI on Linux (Ubuntu) and Windows (WSL2 with Ubuntu).
✅ Verify Managed Identity Setup (optional)
As you’re using a System-Assigned Managed Identity, ensure it’s enabled:
az vm identity show --name myVM --resource-group myRG
If you see "principalId" in the output, the identity is active.
If not, enable it:
az vm identity assign --name myVM --resource-group myRG
For a User-Assigned Managed Identity, attach it to your VM:
az vm identity assign --name myVM --resource-group myRG --identities myUserAssignedIdentity
🧩 Step 1: Set Environment Variables
When Terraform runs inside Azure, it can automatically authenticate using the Managed Identity assigned to the resource (for example, an Azure VM, App Service, or Container Instance).
Terraform requests an access token from the Azure Instance Metadata Service (IMDS) — this means no secrets or credentials are required.
However, Terraform still needs to know which Azure Subscription to use.
The environment variables you set depend on whether you’re using a System-Assigned or User-Assigned Managed Identity.
🪪 Option 1: System-Assigned Managed Identity
For Linux/macOS/WSL:
export ARM_USE_MSI=true
export ARM_SUBSCRIPTION_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
For Windows PowerShell:
$env:ARM_USE_MSI="true"
$env:ARM_SUBSCRIPTION_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
💡 No client ID or tenant ID is required — Terraform automatically discovers them through IMDS.
🧾 Alternative Option: User-Assigned Managed Identity
If your Azure VM or compute resource has a User-Assigned Managed Identity, Terraform must know which one to use.
For Linux/macOS/WSL:
export ARM_USE_MSI=true
export ARM_CLIENT_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
export ARM_SUBSCRIPTION_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
For Windows PowerShell:
$env:ARM_USE_MSI="true"
$env:ARM_CLIENT_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$env:ARM_SUBSCRIPTION_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
💡 The ARM_CLIENT_ID identifies your specific User-Assigned MI, while ARM_SUBSCRIPTION_ID tells Terraform which subscription to use.
✅ Summary
- No secrets or credentials are stored.
- Always set
ARM_USE_MSI=true. - Always provide
ARM_SUBSCRIPTION_ID. - Set
ARM_CLIENT_IDonly for User-Assigned Managed Identity.
⚙️ Step 2: Terraform Script (main.tf)
Create a new folder (e.g., terraform-azure-msi) and inside it, create a file named main.tf.
mkdir terraform-azure-msi
cd terraform-azure-msi
nano main.tf
Note: You may use vi instead of nano if you are familiar and I use that mostly.
Paste the following Terraform configuration:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
}
}
provider "azurerm" {
# use_msi = true # if not using env variable
features {}
}
# Option 1: Create a new Resource Group
resource "azurerm_resource_group" "rg" {
name = "tfmsi-rg"
location = "eastus"
}
# Create a Virtual Network
resource "azurerm_virtual_network" "vnet" {
name = "tfmsi-vnet"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
}
# Create a Subnet inside the Virtual Network
resource "azurerm_subnet" "subnet" {
name = "tfmsi-subnet"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = ["10.0.1.0/24"]
}
💡 Note:
The key difference is the line use_msi = true in the provider block — this tells Terraform to authenticate using the Managed Identity available on the host.
⚙️ Step 3: Initialize and Apply Terraform
1️⃣ Initialize Terraform
terraform init
Downloads the Azure provider plugin and prepares Terraform for this project.
💡 You’ll see “Terraform has been successfully initialized.”
2️⃣ Review the Plan
terraform plan
Previews what Terraform will create — nothing is applied yet.
💡 You should see: Plan: 3 to add, 0 to change, 0 to destroy.
3️⃣ Apply the Configuration
terraform apply
Terraform will now use the Managed Identity for authentication and begin creating Azure resources.
When prompted, type yes.
💡 After a few moments, you’ll see:
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
✅ Step 4: Verify in Azure Portal
Go to Azure Portal → Resource Groups → tfmsi-rg
You’ll find your Virtual Network (tfmsi-vnet) and Subnet (tfmsi-subnet) created successfully.
🧹 Step 5: (Optional) Clean Up
When finished, clean up to avoid charges:
terraform destroy
When prompted, type yes.
💡 You’ll see:
Destroy complete! Resources: 2 destroyed.
🔍 Bonus: Verify Authentication
You can confirm that Terraform used the Managed Identity by checking Azure Activity Logs:
- Go to Azure Portal → Monitor → Activity Log
- Look under Caller — it should show the Managed Identity name instead of your personal account or Service Principal.
Summary
✅ Use Managed Identity for the most secure and seamless Terraform authentication when running within Azure.
✅ No secrets, no credentials — just native Azure identity integration.
✅ Ideal for VMs, DevOps agents, and containerized automation inside Azure.
✅ Run terraform init → plan → apply → destroy confidently with secure identity-based access.
By following this process, you ensure Terraform connects to Azure securely and automatically through a Managed Identity — a best practice for cloud-native, secret-free automation.