Skip to main content

Azure Authentication

This guide shows you how to configure Azure authentication using the native atmos auth system.

Overview​

Atmos Auth provides native Azure authentication that works identically to az login, with support for:

  • Device Code Flow (browser-based authentication for users)
  • OIDC (workload identity for CI/CD pipelines)
  • Service Principals (client credentials for automation)

All authentication methods write credentials to the Azure CLI MSAL cache (~/.azure/msal_token_cache.json), ensuring full compatibility with Terraform's Azure providers (azurerm, azuread, azapi).

Quick Start​

Basic Azure Device Code Authentication​

The simplest Azure authentication uses device code flow for interactive user login:

auth:
providers:
azure-dev:
kind: azure/device-code
tenant_id: "12345678-1234-1234-1234-123456789012"
subscription_id: "87654321-4321-4321-4321-210987654321"
location: "eastus"

identities:
azure-dev-subscription:
default: true
kind: azure/subscription
via:
provider: azure-dev
principal:
subscription_id: "87654321-4321-4321-4321-210987654321"
location: "eastus"

Authenticate:

atmos auth login

This will:

  1. Display a device code and verification URL
  2. Open your browser to https://microsoft.com/devicelogin
  3. Prompt you to enter the code and sign in
  4. Cache credentials for all Azure providers (azurerm, azuread, azapi)

Authentication Methods​

Device Code Flow (Interactive)​

Best for: Developer workstations, interactive sessions

Device code flow provides browser-based authentication with full MFA support:

auth:
providers:
azure-interactive:
kind: azure/device-code
tenant_id: "YOUR_TENANT_ID"
subscription_id: "YOUR_SUBSCRIPTION_ID"
location: "eastus"

identities:
azure-dev:
kind: azure/subscription
via:
provider: azure-interactive
principal:
subscription_id: "YOUR_SUBSCRIPTION_ID"
location: "eastus"

How it works:

  1. Atmos displays a device code (e.g., "WDDD-HRQV")
  2. Browser opens to Microsoft's device login page
  3. You enter the code and sign in (with MFA if configured)
  4. Atmos receives 3 token scopes:
    • https://management.azure.com/.default (Azure Resource Manager)
    • https://graph.microsoft.com/.default (Azure AD operations)
    • https://vault.azure.net/.default (Azure KeyVault operations)

Usage:

# Authenticate interactively
atmos auth login --identity azure-dev

# Use with Terraform
atmos terraform plan my-component -s my-stack

OIDC (Workload Identity for CI/CD)​

Best for: GitHub Actions, GitLab CI, Azure DevOps pipelines

OIDC enables passwordless authentication using federated credentials:

auth:
providers:
azure-oidc:
kind: azure/oidc
tenant_id: "YOUR_TENANT_ID"
client_id: "YOUR_APP_CLIENT_ID"
subscription_id: "YOUR_SUBSCRIPTION_ID"
location: "eastus"

identities:
azure-ci:
kind: azure/subscription
via:
provider: azure-oidc
principal:
subscription_id: "YOUR_SUBSCRIPTION_ID"

Setup Requirements:

  1. Create an Azure AD App Registration
  2. Configure federated credentials for your CI/CD platform
  3. Grant app appropriate Azure RBAC roles
  4. Set environment variables in your pipeline:
    AZURE_CLIENT_ID="YOUR_APP_CLIENT_ID"
    AZURE_TENANT_ID="YOUR_TENANT_ID"
    AZURE_SUBSCRIPTION_ID="YOUR_SUBSCRIPTION_ID"

Example GitHub Actions workflow:

name: Deploy Infrastructure
on:
push:
branches: [main]

permissions:
id-token: write # Required for OIDC token
contents: read

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Atmos
uses: cloudposse/github-action-setup-atmos@v2

- name: Authenticate to Azure
run: atmos auth login --identity azure-ci
env:
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- name: Deploy
run: atmos terraform apply my-component -s prod

Service Principal (Client Credentials)​

Best for: Automation, service accounts, long-running processes

Service principals use client ID and secret for non-interactive authentication:

auth:
providers:
azure-service-principal:
kind: azure/service-principal
tenant_id: "YOUR_TENANT_ID"
client_id: "YOUR_SERVICE_PRINCIPAL_CLIENT_ID"
client_secret: "YOUR_CLIENT_SECRET" # Store securely!
subscription_id: "YOUR_SUBSCRIPTION_ID"
location: "eastus"

identities:
azure-automation:
kind: azure/subscription
via:
provider: azure-service-principal
principal:
subscription_id: "YOUR_SUBSCRIPTION_ID"

Security Best Practices:

  • Never commit secrets to version control
  • Use environment variables: export AZURE_CLIENT_SECRET="..."
  • Or use Atmos's secrets integration:
    client_secret: "{{ (store.get \"secret/azure/client-secret\").data.value }}"

Usage:

# Set secret via environment variable
export AZURE_CLIENT_SECRET="your-secret-here"

# Authenticate
atmos auth login --identity azure-automation

# Run Terraform
atmos terraform apply my-component -s prod

Multi-Subscription Workflows​

Atmos makes it easy to work with multiple Azure subscriptions:

auth:
providers:
azure-main:
kind: azure/device-code
tenant_id: "YOUR_TENANT_ID"
subscription_id: "DEV_SUBSCRIPTION_ID"
location: "eastus"

identities:
azure-dev:
kind: azure/subscription
via:
provider: azure-main
principal:
subscription_id: "DEV_SUBSCRIPTION_ID"
location: "eastus"

azure-staging:
kind: azure/subscription
via:
provider: azure-main
principal:
subscription_id: "STAGING_SUBSCRIPTION_ID"
location: "westus"

azure-prod:
kind: azure/subscription
via:
provider: azure-main
principal:
subscription_id: "PROD_SUBSCRIPTION_ID"
location: "eastus2"

Switch between subscriptions:

# Deploy to dev
atmos terraform apply my-component -s dev --identity azure-dev

# Deploy to staging
atmos terraform apply my-component -s staging --identity azure-staging

# Deploy to prod
atmos terraform apply my-component -s prod --identity azure-prod

Terraform Provider Compatibility​

Atmos Azure authentication works seamlessly with all Terraform Azure providers:

azurerm Provider​

Atmos sets ARM_USE_CLI=true, enabling the azurerm provider to use Azure CLI credentials:

provider "azurerm" {
features {}
# Atmos automatically sets:
# - ARM_USE_CLI=true
# - ARM_SUBSCRIPTION_ID
# - ARM_TENANT_ID
# - ARM_LOCATION
}

resource "azurerm_key_vault" "example" {
name = "my-keyvault"
location = "eastus"
tenant_id = data.azurerm_client_config.current.tenant_id
# KeyVault operations work because Atmos provides KeyVault token scope
}

azuread Provider​

The azuread provider automatically uses Graph API tokens from Atmos:

provider "azuread" {
# Atmos provides Graph API token via ARM_USE_CLI
}

resource "azuread_group" "example" {
display_name = "My Group"
security_enabled = true
}

azapi Provider​

The azapi provider also works with Azure CLI authentication:

provider "azapi" {
# Atmos authentication "just works"
}

resource "azapi_resource" "example" {
type = "Microsoft.Network/virtualNetworks@2021-02-01"
name = "my-vnet"
parent_id = azurerm_resource_group.example.id
# ...
}

Token Scopes​

Atmos Azure authentication provides all three token scopes required for full Azure functionality:

Token ScopeUsed ByPurpose
https://management.azure.com/.defaultazurerm, azapiAzure Resource Manager operations
https://graph.microsoft.com/.defaultazureadAzure Active Directory operations
https://vault.azure.net/.defaultazurermAzure KeyVault operations

This matches exactly what az login provides, ensuring 100% compatibility.

Authentication Flow​

When you run atmos auth login:

  1. Provider Authentication: Atmos authenticates using configured method (device code/OIDC/service principal)
  2. Token Acquisition: Requests all 3 token scopes from Azure AD
  3. MSAL Cache Update: Writes tokens to ~/.azure/msal_token_cache.json
  4. Profile Update: Updates ~/.azure/azureProfile.json with subscription info
  5. Environment Setup: Sets ARM_* environment variables for Terraform
  6. Credential Storage: Caches credentials in system keyring for reuse

Common Patterns​

Pattern 1: Developer + CI/CD​

Use device code for developers, OIDC for pipelines:

auth:
providers:
azure-dev-interactive:
kind: azure/device-code
tenant_id: "YOUR_TENANT_ID"
subscription_id: "DEV_SUBSCRIPTION_ID"

azure-ci:
kind: azure/oidc
tenant_id: "YOUR_TENANT_ID"
client_id: "YOUR_APP_CLIENT_ID"
subscription_id: "PROD_SUBSCRIPTION_ID"

identities:
azure-dev:
default: true # Default for developers
kind: azure/subscription
via:
provider: azure-dev-interactive
principal:
subscription_id: "DEV_SUBSCRIPTION_ID"

azure-prod-ci:
kind: azure/subscription
via:
provider: azure-ci
principal:
subscription_id: "PROD_SUBSCRIPTION_ID"

Pattern 2: Multi-Region Deployment​

Deploy to multiple Azure regions:

auth:
providers:
azure-main:
kind: azure/device-code
tenant_id: "YOUR_TENANT_ID"
subscription_id: "YOUR_SUBSCRIPTION_ID"

identities:
azure-eastus:
kind: azure/subscription
via:
provider: azure-main
principal:
subscription_id: "YOUR_SUBSCRIPTION_ID"
location: "eastus"

azure-westus:
kind: azure/subscription
via:
provider: azure-main
principal:
subscription_id: "YOUR_SUBSCRIPTION_ID"
location: "westus"

azure-westeurope:
kind: azure/subscription
via:
provider: azure-main
principal:
subscription_id: "YOUR_SUBSCRIPTION_ID"
location: "westeurope"

Troubleshooting​

Token Expiration​

Azure tokens typically expire after 1 hour. Atmos automatically caches and reuses valid tokens:

# Check current authentication status
atmos auth whoami

# Re-authenticate if expired
atmos auth login

Missing Token Scopes​

If you see errors about missing permissions, ensure all 3 token scopes are configured:

# Check MSAL cache contents
cat ~/.azure/msal_token_cache.json | jq '.AccessToken | keys[]'

# Should see entries for:
# - management.azure.com
# - graph.microsoft.com
# - vault.azure.net

Provider Not Found​

Ensure your Terraform providers are configured to use ARM_USE_CLI:

# Check environment
atmos auth env --identity azure-dev

# Should show:
# ARM_USE_CLI=true
# ARM_SUBSCRIPTION_ID=...
# ARM_TENANT_ID=...

Security Considerations​

Credential Storage​

  • Credentials are stored in your system keyring (Keychain on macOS, Secret Service on Linux, Credential Manager on Windows)
  • Tokens are also cached in ~/.azure/msal_token_cache.json for Terraform provider compatibility
  • Both storages are secured by your operating system

Least Privilege​

Grant identities only the permissions they need:

# Example: Grant Contributor role to a subscription
az role assignment create \
--assignee YOUR_APP_CLIENT_ID \
--role Contributor \
--scope /subscriptions/YOUR_SUBSCRIPTION_ID

Audit Logging​

Enable Azure AD sign-in logs to audit authentication:

  • Go to Azure Portal → Azure Active Directory → Monitoring → Sign-in logs
  • Filter by "Application" to see Atmos authentications

Migration from az login​

Already using az login? Atmos authentication is a drop-in replacement:

Before (using az login):

az login
az account set --subscription "YOUR_SUBSCRIPTION_ID"
terraform apply

After (using Atmos):

atmos auth login --identity azure-dev
atmos terraform apply my-component -s my-stack

Both write to the same Azure CLI credential files, so your Terraform code doesn't need any changes.

Next Steps​