ADR-036: Azure Authentication Research and Strategy
Status: Accepted
Date: 2024-10-15
Deciders: Architecture Team, Security Team
Related: ADR-008 (Azure Authentication for Local Docker Testing)
Context
Implementing secure Azure authentication across multiple deployment scenarios proved complex:
- Local Development: Developers need quick, secure access
- Docker Testing: Containers can't use interactive login
- DevContainers: Remote development environments
- CI/CD Pipelines: Automated deployments
- Azure Production: Managed services without secrets
Each scenario has different constraints: - Security requirements - Available authentication mechanisms - Developer experience priorities - Operational complexity
Research Findings
Authentication Mechanisms Evaluated
1. Interactive Login (az login)
How it works:
Pros:
- ✅ Most secure (no secrets in files)
- ✅ Uses your Azure AD identity
- ✅ Multi-factor authentication support
- ✅ Automatic credential refresh
- ✅ Works with DefaultAzureCredential
Cons: - ❌ Requires browser access - ❌ Doesn't work in containers - ❌ Not suitable for CI/CD - ❌ Credentials expire (need re-login)
Decision: Use for local development ✅
2. Service Principal (Client ID + Secret)
How it works:
# Create Service Principal
az ad sp create-for-rbac --name "app-name" --role "Cognitive Services OpenAI User"
# Outputs:
{
"appId": "client-id",
"password": "client-secret",
"tenant": "tenant-id"
}
# Use in environment
export AZURE_TENANT_ID="tenant-id"
export AZURE_CLIENT_ID="client-id"
export AZURE_CLIENT_SECRET="client-secret"
Pros:
- ✅ Works in containers
- ✅ Works in CI/CD
- ✅ Programmatic authentication
- ✅ Works with DefaultAzureCredential
- ✅ Can be rotated
Cons: - ⚠️ Secrets must be stored securely - ⚠️ Need to rotate every 90 days - ⚠️ Risk of secret exposure - ⚠️ Manual management required
Decision: Use for Docker testing ✅
3. Managed Identity (Azure Production)
How it works:
# No configuration needed!
from azure.identity import DefaultAzureCredential
credential = DefaultAzureCredential()
# Azure automatically provides identity
# No secrets, no configuration
Pros:
- ✅ No secrets to manage
- ✅ Automatic credential rotation
- ✅ Azure-native security
- ✅ RBAC-based permissions
- ✅ Best security practice
- ✅ Works with DefaultAzureCredential
Cons: - ❌ Only works in Azure - ❌ Can't test locally - ❌ Requires Azure resource deployment
Decision: Use for Azure production ✅
4. Access Tokens (Short-lived)
How it works:
# Generate token (expires in 1 hour)
az account get-access-token --resource https://cognitiveservices.azure.com
# Use token
export AZURE_ACCESS_TOKEN="eyJ0eXAi..."
Pros: - ✅ Works in containers - ✅ No long-lived secrets - ✅ Inherits your permissions
Cons: - ❌ Expires in 1 hour - ❌ Not suitable for long-running services - ❌ Manual refresh required - ❌ Complex to manage
Decision: Not recommended ❌
5. Connection Strings / API Keys
How it works:
# Get API key from Azure Portal
export AZURE_OPENAI_KEY="abc123..."
export AZURE_OPENAI_ENDPOINT="https://..."
Pros: - ✅ Simple to use - ✅ Works everywhere
Cons: - ❌ Least secure - ❌ Keys in plaintext - ❌ No automatic rotation - ❌ Not recommended by Microsoft - ❌ No RBAC integration
Decision: Not recommended ❌
DefaultAzureCredential Chain
Microsoft's DefaultAzureCredential tries authentication methods in order:
1. Environment variables (AZURE_CLIENT_ID, etc.)
2. Managed Identity (in Azure)
3. Azure CLI (az login)
4. Visual Studio Code
5. Azure PowerShell
6. Interactive browser
Key insight: One authentication code works across all environments! 🎯
from azure.identity import DefaultAzureCredential
# Works everywhere with proper setup
credential = DefaultAzureCredential()
client = AzureOpenAIClient(endpoint=endpoint, credential=credential)
Decision Matrix
| Scenario | Method | Setup | Security | Experience |
|---|---|---|---|---|
| Local Dev | az login | Easy | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Docker Test | Service Principal | Medium | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| DevContainers | az login* | Easy | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| CI/CD | Service Principal | Medium | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| Azure Prod | Managed Identity | Auto | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
*DevContainers can forward az login credentials
Adopted Strategy
Stage 1: Local Development (Recommended)
# One-time setup
az login
# Run application - authentication automatic!
uv run python -m loan_defenders.api.app
No credentials in files! Most secure and easiest.
Stage 2: Docker Testing
# One-time setup (per 90 days)
az ad sp create-for-rbac --name "loan-defenders-docker"
# Add to .env (never commit!)
AZURE_TENANT_ID=xxx
AZURE_CLIENT_ID=xxx
AZURE_CLIENT_SECRET=xxx
# Run containers
docker-compose up
Credentials in .env (git-ignored), rotated regularly.
Stage 3: Azure Production
# Container App configuration
identity:
type: SystemAssigned
# RBAC assignment (via Bicep)
roleAssignments:
- principalId: containerApp.identity.principalId
roleDefinitionId: "Cognitive Services OpenAI User"
No configuration needed! Managed Identity automatic.
Implementation Guide
Setup for Each Environment
Local Development:
# 1. Login to Azure
az login
# 2. Verify
az account show
# 3. Run app (no .env credentials needed)
cd apps/api
uv run python -m loan_defenders.api.app
Docker Testing:
# 1. Create Service Principal (if not exists)
./scripts/setup-azure-auth.sh
# 2. Verify .env has credentials
grep AZURE_ .env
# 3. Start containers
docker-compose up
Azure Production:
# 1. Deploy infrastructure with Managed Identity
cd infrastructure/bicep
../deploy.sh dev loan-defenders-dev-rg --stage all
# 2. Identity and RBAC configured automatically
# 3. Deploy containers (no credentials needed)
Security Best Practices
✅ DO
- Use
az loginfor local development - Store Service Principal secrets in
.env(git-ignored) - Rotate Service Principal secrets every 90 days
- Use Managed Identity in Azure production
- Apply least-privilege RBAC roles
- Use
DefaultAzureCredentialin code
❌ DON'T
- Commit credentials to git
- Use API keys (use RBAC instead)
- Share Service Principal secrets
- Use same credentials for dev/prod
- Store secrets in plaintext files
- Hard-code credentials in code
Consequences
Positive
✅ Security: Best practices for each environment
✅ Developer Experience: az login is easiest for devs
✅ Production Security: Managed Identity is most secure
✅ Flexibility: Different auth per scenario
✅ Consistent Code: DefaultAzureCredential works everywhere
Negative
⚠️ Complexity: Different setup per environment
⚠️ Documentation: Need clear guides for each scenario
⚠️ Learning Curve: Developers must understand auth chain
⚠️ Troubleshooting: Different failure modes per method
Mitigations
- Comprehensive authentication guide created
- Clear error messages in application
- Automated setup scripts provided
- Documentation per deployment stage
References
- Azure Authentication Guide
- Local Development Guide
- Docker Development Guide
- Azure Deployment Guide
- Microsoft DefaultAzureCredential Docs
Status
Researched: October 2024
Implemented: Across all deployment stages
Current State: Working in local, Docker, and Azure environments
Documentation: Comprehensive guides completed
Lessons Learned
DefaultAzureCredentialis powerful - One code path for all environmentsaz loginbest for developers - Security + ease of use- Managed Identity is ideal - But only available in Azure
- Service Principals needed - For Docker and CI/CD
- Documentation critical - Authentication is confusing without clear guides