Terraform Azure Service Principal: Authentication Guide
Terraform Azure Service Principal: Authentication Guide
Create and configure Azure Service Principals for Terraform authentication with step-by-step instructions.
Table of Contents
- What is a Service Principal
- Prerequisites
- Create Service Principal
- Configure Terraform
- Environment Variables
- Security Best Practices
- Troubleshooting
What is a Service Principal
An Azure Service Principal is an identity created for use with applications, hosted services, and automated tools to access Azure resources. It's like a service account that can be assigned permissions to access resources.
Why Use Service Principals
- Security: Granular access control
- Automation: No interactive login required
- Auditing: Track service activity
- Multi-tenancy: Work across subscriptions
- CI/CD Integration: Perfect for automation pipelines
Prerequisites
Required Tools
- Azure CLI installed
- Terraform installed (0.12+)
- Azure subscription with appropriate permissions
Install Azure CLI
# macOS
brew install azure-cli
# Linux
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
# Windows
winget install Microsoft.AzureCLI
Login to Azure
az login
Set Default Subscription
# List subscriptions
az account list --output table
# Set default subscription
az account set --subscription "<subscription-id>"
Create Service Principal
Method 1: Using Azure CLI (Recommended)
# Create service principal with Contributor role
az ad sp create-for-rbac --name "terraform-sp" --role "Contributor" --scopes "/subscriptions/<subscription-id>"
# Output will contain:
# {
# "appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
# "displayName": "terraform-sp",
# "password": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
# "tenant": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# }
Save these values securely!
appId= Client IDpassword= Client Secrettenant= Tenant ID
Method 2: With Custom Scope
# Create with scope for specific resource group
az ad sp create-for-rbac \
--name "terraform-sp" \
--role "Contributor" \
--scopes "/subscriptions/<subscription-id>/resourceGroups/<resource-group-name>"
Method 3: With Specific Expiration
# Create service principal with 1-year expiration
az ad sp create-for-rbac \
--name "terraform-sp" \
--role "Contributor" \
--years 1
Method 4: Using Azure Portal
- Go to Azure Portal → Azure Active Directory
- Click App registrations → New registration
- Enter name and click Register
- Copy Application (client) ID and Directory (tenant) ID
- Go to Certificates & secrets → New client secret
- Add description and expiration
- Copy the Value (client secret)
- Go to Subscriptions → Access control (IAM)
- Add Role assignment for the service principal
Configure Terraform
Option 1: Environment Variables (Recommended)
export ARM_SUBSCRIPTION_ID="<subscription-id>"
export ARM_CLIENT_ID="<app-id>"
export ARM_CLIENT_SECRET="<client-secret>"
export ARM_TENANT_ID="<tenant-id>"
Add to ~/.bashrc or ~/.zshrc for persistence:
# Azure Terraform Credentials
export ARM_SUBSCRIPTION_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
export ARM_CLIENT_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
export ARM_CLIENT_SECRET="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export ARM_TENANT_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
Option 2: Terraform Provider Block
provider "azurerm" {
features {}
subscription_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
client_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
client_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
tenant_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
⚠️ Security Warning: Never commit secrets to Git!
Option 3: Using Azure CLI Authentication
# Login with Azure CLI
az login
# Terraform will automatically use CLI credentials
terraform init
terraform apply
Option 4: Managed Identity (Recommended for Production)
provider "azurerm" {
features {}
use_msi = true
subscription_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
tenant_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
Environment Variables
Complete List of Azure Provider Variables
| Variable | Description | Required |
|---|---|---|
ARM_SUBSCRIPTION_ID | Azure Subscription ID | Yes |
ARM_CLIENT_ID | Service Principal App ID | Yes* |
ARM_CLIENT_SECRET | Service Principal Password | Yes* |
ARM_TENANT_ID | Azure Tenant ID | Yes* |
ARM_USE_MSI | Use Managed Identity | No |
ARM_CLIENT_CERTIFICATE_PATH | Path to client certificate | No |
ARM_CLIENT_CERTIFICATE_PASSWORD | Certificate password | No |
*Not required if using Azure CLI or Managed Identity
Example .env File
# .env (never commit this!)
ARM_SUBSCRIPTION_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ARM_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ARM_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ARM_TENANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Load in your shell:
source .env
Security Best Practices
1. Use Least Privilege
# Assign specific role instead of Contributor
az ad sp create-for-rbac \
--name "terraform-sp" \
--role "Contributor" \
--scopes "/subscriptions/<subscription-id>/resourceGroups/<resource-group-name>"
2. Rotate Secrets Regularly
# Reset credentials
az ad sp credential reset \
--name "terraform-sp" \
--password "new-strong-password"
3. Use Key Vault for Secrets
data "azurerm_key_vault_secret" "client_secret" {
name = "terraform-client-secret"
key_vault_id = azurerm_key_vault.example.id
}
provider "azurerm" {
features {}
client_id = var.client_id
client_secret = data.azurerm_key_vault_secret.client_secret.value
tenant_id = var.tenant_id
}
4. Enable Multi-Factor Authentication
For critical service principals, enable conditional access policies.
5. Monitor Service Principal Activity
# Get sign-in logs
az login
az monitor activity-log list \
--caller "terraform-sp" \
--max-events 10
6. Use Separate Service Principals
Create different service principals for:
- Development environment
- Staging environment
- Production environment
Troubleshooting
Error: "authentication failed"
Cause: Invalid credentials or expired secret
Solution:
# Verify credentials
az login --service-principal \
--username "<app-id>" \
--password "<client-secret>" \
--tenant "<tenant-id>"
# Reset credentials if needed
az ad sp credential reset --name "terraform-sp"
Error: "insufficient privileges"
Cause: Service principal lacks required permissions
Solution:
# Check assigned roles
az role assignment list \
--assignee "<app-id>" \
--output table
# Assign required role
az role assignment create \
--assignee "<app-id>" \
--role "Contributor" \
--scope "/subscriptions/<subscription-id>"
Error: "subscription not found"
Cause: Wrong subscription ID or not accessible
Solution:
# List accessible subscriptions
az account list --output table
# Set correct subscription
az account set --subscription "<subscription-id>"
Error: "tenant ID mismatch"
Cause: Service principal from different tenant
Solution:
# Verify tenant ID
az account show --query tenantId -o tsv
# Update environment variable
export ARM_TENANT_ID="<correct-tenant-id>"
Testing Configuration
Test Authentication
# Test with Azure CLI
az login --service-principal \
--username "$ARM_CLIENT_ID" \
--password "$ARM_CLIENT_SECRET" \
--tenant "$ARM_TENANT_ID"
# Test with Terraform
terraform init
terraform plan
Verify Permissions
# List resource groups accessible
az group list --output table
# Try to create a test resource
az group create --name test-rg --location "UK South"
az group delete --name test-rg --yes --no-wait
Terraform Example
Complete Configuration
# providers.tf
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
}
provider "azurerm" {
features {}
}
# main.tf
resource "azurerm_resource_group" "example" {
name = "terraform-example-rg"
location = "UK South"
}
resource "azurerm_storage_account" "example" {
name = "terraformstorageexample"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
}
# outputs.tf
output "resource_group_name" {
value = azurerm_resource_group.example.name
}
output "storage_account_name" {
value = azurerm_storage_account.example.name
}
Run Terraform
# Initialize
terraform init
# Plan
terraform plan
# Apply
terraform apply
# Destroy
terraform destroy
Quick Reference
| Task | Command |
|---|---|
| Create SP | az ad sp create-for-rbac |
| List SPs | az ad sp list --display-name |
| Get SP details | az ad sp show --id |
| Reset credentials | az ad sp credential reset |
| Assign role | az role assignment create |
| List roles | az role assignment list |
Related Articles
Last Updated: October 2026
Author: CloudOpsGuide Team
Difficulty: Beginner
Estimated Reading Time: 12 minutes