Version: 1.0 Last Updated: 2025-10-23 Status: Production Ready
This guide provides comprehensive setup instructions for configuring service principal authentication in azlin. Service principals enable automated, non-interactive Azure authentication for CI/CD pipelines, automation scripts, and team environments without requiring user credentials.
What is Service Principal Authentication?
A service principal is an identity created for use with applications, hosted services, and automated tools to access Azure resources. Unlike user authentication (Azure CLI), service principals:
- Work without interactive login
- Support certificate-based authentication for enhanced security
- Can be restricted to specific permissions and subscriptions
- Are ideal for automation and CI/CD pipelines
azlin Authentication Methods:
azlin supports 4 authentication methods with automatic fallback:
- Service Principal with Certificate (highest security)
- Service Principal with Client Secret (secure with proper secret management)
- Managed Identity (for Azure-hosted workloads)
- Azure CLI (default, backward compatible)
Before setting up service principal authentication, ensure you have:
- Azure subscription with permissions to create service principals
- Azure CLI installed (
azcommand) - azlin installed (version 2.1+)
- Python 3.11+ (for azlin)
- OpenSSL for generating certificates
- Azure Key Vault (optional, for certificate storage)
# Verify installations
az --version
azlin --version
python --version
# Login to Azure (if not already authenticated)
az loginHow it works:
Uses your existing Azure CLI credentials (az login). No additional configuration required.
When to use:
- Local development
- Quick prototyping
- Personal projects
- When you're already logged in with
az login
Setup steps:
# Login with Azure CLI
az login
# Use azlin normally (no profile needed)
azlin list
azlin newBenefits:
- Zero configuration
- Seamless user experience
- Works out of the box
Limitations:
- Requires interactive login
- Not suitable for CI/CD
- Cannot be automated
How it works: Uses an application identity (service principal) with a password (client secret) to authenticate. The secret is stored in environment variables, never in config files.
When to use:
- CI/CD pipelines (GitHub Actions, Azure DevOps)
- Automation scripts
- Shared development environments
- Non-interactive workflows
Security considerations:
- Client secrets can be compromised if leaked
- Secrets should be rotated regularly
- Use certificate-based auth when possible for production
Step 1: Create Service Principal in Azure
Using Azure CLI (recommended):
# Create service principal with Contributor role
az ad sp create-for-rbac \
--name "azlin-automation" \
--role Contributor \
--scopes /subscriptions/{YOUR-SUBSCRIPTION-ID}
# Output includes:
# {
# "appId": "12345678-1234-1234-1234-123456789abc", # Use as CLIENT_ID
# "displayName": "azlin-automation",
# "password": "your-secret-value-here", # Use as CLIENT_SECRET
# "tenant": "87654321-4321-4321-4321-cba987654321" # Use as TENANT_ID
# }Using Azure Portal:
- Navigate to Azure Active Directory → App registrations
- Click "New registration"
- Name:
azlin-automation - Click "Register"
- Note the Application (client) ID and Directory (tenant) ID
- Go to "Certificates & secrets" → "New client secret"
- Description:
azlin-secret, Expires: 12 months - Click "Add" and copy the secret value (shown only once!)
Step 2: Grant Required Permissions
Assign the service principal access to your subscription or resource groups:
# Grant Contributor role to subscription
az role assignment create \
--assignee {CLIENT-ID} \
--role Contributor \
--scope /subscriptions/{SUBSCRIPTION-ID}
# Or grant access to specific resource group
az role assignment create \
--assignee {CLIENT-ID} \
--role Contributor \
--scope /subscriptions/{SUBSCRIPTION-ID}/resourceGroups/{RESOURCE-GROUP}Required permissions:
- Subscription level: Reader (minimum), Contributor (recommended for VM operations)
- Resource Group level: Contributor (for creating/managing VMs)
- Key Vault (if used): Key Vault Secrets User
Step 3: Configure azlin Profile
Use the interactive setup wizard:
# Interactive setup
azlin auth setup
# You'll be prompted for:
# - Azure Tenant ID: 87654321-4321-4321-4321-cba987654321
# - Azure Client ID: 12345678-1234-1234-1234-123456789abc
# - Azure Subscription ID: abcdef12-3456-7890-abcd-ef1234567890
# - Use certificate? [y/N]: NOr non-interactive setup:
azlin auth setup --profile prod \
--tenant-id "87654321-4321-4321-4321-cba987654321" \
--client-id "12345678-1234-1234-1234-123456789abc" \
--subscription-id "abcdef12-3456-7890-abcd-ef1234567890"Step 4: Set Client Secret Environment Variable
Set the client secret in your environment (NEVER store in config files):
# Set client secret
export AZURE_CLIENT_SECRET="your-secret-value-here"
# Or use azlin-specific variable
export AZLIN_SP_CLIENT_SECRET="your-secret-value-here"
# Make it permanent (add to ~/.bashrc or ~/.zshrc)
echo 'export AZURE_CLIENT_SECRET="your-secret-value-here"' >> ~/.bashrc
source ~/.bashrcFor CI/CD environments (GitHub Actions example):
# .github/workflows/azure.yml
name: Azure Deployment
env:
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy with azlin
run: |
azlin --auth-profile prod listStep 5: Test Authentication
# Test default profile
azlin auth test
# Test specific profile
azlin auth test --profile prod
# Test with subscription validation
azlin auth test --profile prod --subscription-id "abcdef12-3456-7890-abcd-ef1234567890"Expected output:
Testing authentication profile: prod
Profile details:
Client ID: 12345678-1234-1234-1234-123456789abc
Tenant ID: 87654321-4321-4321-4321-cba987654321
Subscription ID: abcdef12-3456-7890-abcd-ef1234567890
Auth method: client_secret
Validating credentials...
✓ Credentials valid
Testing Azure authentication...
✓ Authentication successful!
Authenticated as:
Tenant: 87654321-4321-4321-4321-cba987654321
Subscription: Production Subscription
ID: abcdef12-3456-7890-abcd-ef1234567890
How it works: Uses a certificate file (.pem) instead of a password for authentication. This provides enhanced security through public-key cryptography.
When to use:
- Production environments
- High-security requirements
- Long-term automation
- When enhanced security is required
Security benefits:
- Cannot be compromised through password leaks
- Supports hardware security modules (HSM)
- Automatic expiration enforcement
- Stronger audit trail
Step 1: Generate Certificate
Using OpenSSL (self-signed certificate):
# Generate private key and certificate
openssl req -x509 -newkey rsa:4096 -keyout sp-key.pem -out sp-cert.pem -days 365 -nodes \
-subj "/CN=azlin-service-principal"
# Combine key and cert into single PEM file
cat sp-key.pem sp-cert.pem > azlin-sp.pem
# Clean up separate files
rm sp-key.pem sp-cert.pem
# Set secure permissions (REQUIRED)
chmod 600 azlin-sp.pem
# Move to secure location
mkdir -p ~/.azlin/certs
mv azlin-sp.pem ~/.azlin/certs/Using Azure Key Vault (recommended for production):
# Create certificate in Key Vault
az keyvault certificate create \
--vault-name "your-keyvault" \
--name "azlin-sp-cert" \
--policy "$(az keyvault certificate get-default-policy)"
# Download certificate
az keyvault secret download \
--vault-name "your-keyvault" \
--name "azlin-sp-cert" \
--file ~/.azlin/certs/azlin-sp.pem
# Set secure permissions
chmod 600 ~/.azlin/certs/azlin-sp.pemStep 2: Create Service Principal with Certificate
# Create service principal with certificate
az ad sp create-for-rbac \
--name "azlin-automation-cert" \
--role Contributor \
--scopes /subscriptions/{SUBSCRIPTION-ID} \
--cert @~/.azlin/certs/azlin-sp.pem
# Output includes:
# {
# "appId": "12345678-1234-1234-1234-123456789abc", # Use as CLIENT_ID
# "displayName": "azlin-automation-cert",
# "tenant": "87654321-4321-4321-4321-cba987654321" # Use as TENANT_ID
# }Or upload certificate to existing service principal:
# Upload certificate to existing service principal
az ad sp credential reset \
--id {CLIENT-ID} \
--cert @~/.azlin/certs/azlin-sp.pemStep 3: Configure azlin Profile
# Interactive setup
azlin auth setup --profile prod
# You'll be prompted for:
# - Azure Tenant ID: 87654321-4321-4321-4321-cba987654321
# - Azure Client ID: 12345678-1234-1234-1234-123456789abc
# - Azure Subscription ID: abcdef12-3456-7890-abcd-ef1234567890
# - Use certificate? [y/N]: y
# - Certificate path: /Users/username/.azlin/certs/azlin-sp.pemOr non-interactive:
azlin auth setup --profile prod \
--tenant-id "87654321-4321-4321-4321-cba987654321" \
--client-id "12345678-1234-1234-1234-123456789abc" \
--subscription-id "abcdef12-3456-7890-abcd-ef1234567890" \
--use-certificate \
--certificate-path ~/.azlin/certs/azlin-sp.pemStep 4: Set Certificate Permissions
Certificate files MUST have secure permissions (0600 or 0400):
# Set read/write for owner only
chmod 600 ~/.azlin/certs/azlin-sp.pem
# Or read-only for owner
chmod 400 ~/.azlin/certs/azlin-sp.pem
# Verify permissions
ls -la ~/.azlin/certs/azlin-sp.pem
# Should show: -rw------- (600) or -r-------- (400)Security validation:
azlin automatically validates certificate permissions during setup. If permissions are too permissive, you'll see:
✗ Certificate validation failed: Certificate has insecure permissions: 0644. Expected: 0600 or 0400
Step 5: Test Authentication
# Test certificate authentication
azlin auth test --profile prodHow it works: Uses the managed identity assigned to an Azure resource (VM, Container Instance, Function App). No credentials needed - authentication is automatic.
When to use:
- Azure-hosted workloads
- Azure VMs running azlin
- Azure Container Instances
- Azure Functions
- Any Azure service with managed identity support
Benefits:
- No credential management
- Automatic credential rotation
- Azure-native security
- Zero configuration on the workload
Step 1: Enable Managed Identity on Azure Resource
For Azure VMs:
# Enable system-assigned managed identity
az vm identity assign \
--name "my-vm" \
--resource-group "my-rg"
# Output includes:
# {
# "systemAssignedIdentity": "12345678-1234-1234-1234-123456789abc"
# }For Azure Container Instances:
# Create container with managed identity
az container create \
--name "azlin-worker" \
--resource-group "my-rg" \
--image "myregistry.azurecr.io/azlin-worker:latest" \
--assign-identityStep 2: Grant Permissions to Managed Identity
# Get managed identity principal ID
IDENTITY_ID=$(az vm identity show --name "my-vm" --resource-group "my-rg" --query principalId -o tsv)
# Grant Contributor role
az role assignment create \
--assignee $IDENTITY_ID \
--role Contributor \
--scope /subscriptions/{SUBSCRIPTION-ID}Step 3: Configure azlin (Auto-Detection)
When azlin runs on an Azure resource with managed identity, it automatically detects and uses it. No configuration needed!
# On the Azure VM/Container, just use azlin normally
azlin list
azlin newFor explicit configuration:
azlin auth setup --profile managed \
--tenant-id "87654321-4321-4321-4321-cba987654321" \
--subscription-id "abcdef12-3456-7890-abcd-ef1234567890" \
--use-managed-identityStep 4: Test on Azure Resource
# SSH into your Azure VM
ssh azureuser@your-vm-ip
# Test managed identity authentication
azlin auth test --profile managedThe easiest way to get started:
# Run interactive setup wizard
azlin auth setup
# Follow prompts to enter:
# - Profile name (e.g., "dev", "prod", "staging")
# - Tenant ID
# - Client ID
# - Subscription ID
# - Auth method (certificate or client secret)For non-interactive or scripted setup, edit ~/.azlin/config.toml directly:
[auth.profiles.production]
client_id = "12345678-1234-1234-1234-123456789abc"
tenant_id = "87654321-4321-4321-4321-cba987654321"
subscription_id = "abcdef12-3456-7890-abcd-ef1234567890"
auth_method = "client_secret"
[auth.profiles.prod-cert]
client_id = "12345678-1234-1234-1234-123456789abc"
tenant_id = "87654321-4321-4321-4321-cba987654321"
subscription_id = "abcdef12-3456-7890-abcd-ef1234567890"
auth_method = "certificate"
certificate_path = "/Users/username/.azlin/certs/azlin-sp.pem"
[auth.profiles.dev]
client_id = "abcdefgh-5678-5678-5678-abcdefghijkl"
tenant_id = "87654321-4321-4321-4321-cba987654321"
subscription_id = "fedcba09-8765-4321-fedc-ba0987654321"
auth_method = "client_secret"-
Never store secrets in config files
⚠️ - Use environment variables for client secrets
- azlin config files should NEVER contain
client_secretfield - Review config before committing to version control
-
Use environment variables for secrets
# Good practices export AZURE_CLIENT_SECRET="secret" export AZLIN_SP_CLIENT_SECRET="secret" # Bad practices (DO NOT DO THIS) # - Hardcoding in scripts # - Storing in config files # - Committing to Git
-
Set certificate permissions to 0600 or 0400
# Correct permissions chmod 600 certificate.pem # Read/write for owner only chmod 400 certificate.pem # Read-only for owner only # Incorrect permissions (azlin will reject) chmod 644 certificate.pem # Too permissive! chmod 755 certificate.pem # Extremely insecure!
-
Rotate secrets regularly
- Client secrets: Rotate every 90 days
- Certificates: Set 1-year expiration, renew at 30 days
- Document rotation procedures
- Use Azure Key Vault for automatic rotation
-
Use certificate-based auth when possible
- Higher security than client secrets
- Better for production environments
- Harder to compromise
- Supports hardware security modules
-
Grant minimum required permissions
- Start with Reader role, escalate as needed
- Use resource group-level permissions when possible
- Avoid subscription-wide Contributor unless necessary
- Regular permission audits
Before deploying to production:
- No secrets in config files (
~/.azlin/config.toml) - Client secrets stored in environment variables only
- Certificate permissions are 0600 or 0400
- Service principal has minimum required permissions
- Secret rotation schedule documented
- Certificates expire in > 30 days
- Config file permissions are 0600
- No credentials in version control
- Azure Key Vault configured (for production)
- Audit logging enabled
Scenario: Local development with client secret authentication
# Setup profile
azlin auth setup --profile dev \
--tenant-id "87654321-4321-4321-4321-cba987654321" \
--client-id "12345678-1234-1234-1234-123456789abc" \
--subscription-id "dev-sub-id"
# Set secret
export AZURE_CLIENT_SECRET="dev-secret-here"
# Use profile
azlin --auth-profile dev list
azlin --auth-profile dev new --name dev-vmConfig file (~/.azlin/config.toml):
[auth.profiles.dev]
client_id = "12345678-1234-1234-1234-123456789abc"
tenant_id = "87654321-4321-4321-4321-cba987654321"
subscription_id = "dev-sub-id"
auth_method = "client_secret"Scenario: Production environment with certificate authentication and Key Vault
# Generate certificate in Key Vault
az keyvault certificate create \
--vault-name "prod-keyvault" \
--name "azlin-prod-cert" \
--policy "$(az keyvault certificate get-default-policy)"
# Download certificate
az keyvault secret download \
--vault-name "prod-keyvault" \
--name "azlin-prod-cert" \
--file ~/.azlin/certs/prod-cert.pem
# Set permissions
chmod 400 ~/.azlin/certs/prod-cert.pem
# Setup profile
azlin auth setup --profile prod \
--tenant-id "87654321-4321-4321-4321-cba987654321" \
--client-id "prod-client-id" \
--subscription-id "prod-sub-id" \
--use-certificate \
--certificate-path ~/.azlin/certs/prod-cert.pem
# Test
azlin auth test --profile prod
# Use profile
azlin --auth-profile prod listConfig file (~/.azlin/config.toml):
[auth.profiles.prod]
client_id = "prod-client-id"
tenant_id = "87654321-4321-4321-4321-cba987654321"
subscription_id = "prod-sub-id"
auth_method = "certificate"
certificate_path = "/Users/username/.azlin/certs/prod-cert.pem"Scenario: Automated deployment in GitHub Actions
GitHub Secrets Configuration:
- Go to repository Settings → Secrets and variables → Actions
- Add secrets:
AZURE_TENANT_IDAZURE_CLIENT_IDAZURE_CLIENT_SECRETAZURE_SUBSCRIPTION_ID
Workflow file (.github/workflows/deploy.yml):
name: Azure Deployment with azlin
on:
push:
branches: [ main ]
workflow_dispatch:
env:
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install azlin
run: |
pip install azlin
- name: Configure azlin profile
run: |
azlin auth setup --profile ci \
--tenant-id "$AZURE_TENANT_ID" \
--client-id "$AZURE_CLIENT_ID" \
--subscription-id "${{ secrets.AZURE_SUBSCRIPTION_ID }}"
- name: Test authentication
run: |
azlin auth test --profile ci
- name: List VMs
run: |
azlin --auth-profile ci list
- name: Deploy VM
run: |
azlin --auth-profile ci new --name "ci-vm-${{ github.run_number }}"Scenario: Dev, staging, and production profiles
# Setup development profile
azlin auth setup --profile dev \
--tenant-id "$DEV_TENANT_ID" \
--client-id "$DEV_CLIENT_ID" \
--subscription-id "$DEV_SUBSCRIPTION_ID"
# Setup staging profile (certificate-based)
azlin auth setup --profile staging \
--tenant-id "$STAGING_TENANT_ID" \
--client-id "$STAGING_CLIENT_ID" \
--subscription-id "$STAGING_SUBSCRIPTION_ID" \
--use-certificate \
--certificate-path ~/.azlin/certs/staging-cert.pem
# Setup production profile (certificate-based)
azlin auth setup --profile prod \
--tenant-id "$PROD_TENANT_ID" \
--client-id "$PROD_CLIENT_ID" \
--subscription-id "$PROD_SUBSCRIPTION_ID" \
--use-certificate \
--certificate-path ~/.azlin/certs/prod-cert.pem
# Use profiles
export AZURE_CLIENT_SECRET="dev-secret"
azlin --auth-profile dev list
azlin --auth-profile staging list
azlin --auth-profile prod listConfig file (~/.azlin/config.toml):
[auth.profiles.dev]
client_id = "dev-client-id"
tenant_id = "dev-tenant-id"
subscription_id = "dev-subscription-id"
auth_method = "client_secret"
[auth.profiles.staging]
client_id = "staging-client-id"
tenant_id = "staging-tenant-id"
subscription_id = "staging-subscription-id"
auth_method = "certificate"
certificate_path = "/Users/username/.azlin/certs/staging-cert.pem"
[auth.profiles.prod]
client_id = "prod-client-id"
tenant_id = "prod-tenant-id"
subscription_id = "prod-subscription-id"
auth_method = "certificate"
certificate_path = "/Users/username/.azlin/certs/prod-cert.pem"# Test default profile
azlin auth test
# Test specific profile
azlin auth test --profile production
# Test with subscription validation
azlin auth test --profile prod --subscription-id "YOUR-SUB-ID"Successful authentication:
Testing authentication profile: production
Profile details:
Client ID: 12345678-1234-1234-1234-123456789abc
Tenant ID: 87654321-4321-4321-4321-cba987654321
Subscription ID: abcdef12-3456-7890-abcd-ef1234567890
Auth method: certificate
Certificate: /Users/username/.azlin/certs/prod-cert.pem
Validating credentials...
✓ Credentials valid
Testing Azure authentication...
✓ Authentication successful!
Authenticated as:
Tenant: 87654321-4321-4321-4321-cba987654321
Subscription: Production Subscription
ID: abcdef12-3456-7890-abcd-ef1234567890
# Use profile with any azlin command
azlin --auth-profile prod list
azlin --auth-profile prod new --name test-vm
azlin --auth-profile prod status
# Set default profile via environment variable
export AZLIN_AUTH_PROFILE=prod
azlin list # Uses prod profile automaticallyazlin auth listOutput:
Authentication Profiles (3):
dev
Client ID: 12345678...
Tenant ID: 87654321...
Auth method: client_secret
staging
Client ID: abcdefgh...
Tenant ID: 87654321...
Auth method: certificate
Certificate: /Users/username/.azlin/certs/staging-cert.pem
prod
Client ID: fedcba09...
Tenant ID: 87654321...
Auth method: certificate
Certificate: /Users/username/.azlin/certs/prod-cert.pem
Use 'azlin auth show <profile>' for full details
azlin auth show productionOutput:
Profile: production
Client ID: 12345678-1234-1234-1234-123456789abc
Tenant ID: 87654321-4321-4321-4321-cba987654321
Subscription ID: abcdef12-3456-7890-abcd-ef1234567890
Auth method: certificate
Certificate path: /Users/username/.azlin/certs/prod-cert.pem
Certificate status: Valid ✓
Config location: /Users/username/.azlin/config.toml
# Remove with confirmation prompt
azlin auth remove old-profile
# Remove without confirmation
azlin auth remove staging --yesOutput:
Profile 'staging' will be removed.
This will delete the profile configuration (not the Azure service principal).
Are you sure? [y/N]: y
✓ Profile 'staging' removed successfully!
Remaining profiles: dev, prod
Problem: Certificate file has permissions 0644 or looser
Error message:
✗ Certificate validation failed: Certificate has insecure permissions: 0644. Expected: 0600 or 0400
Solution:
# Fix permissions
chmod 600 /path/to/certificate.pem
# Verify
ls -la /path/to/certificate.pem
# Should show: -rw------- (600)
# Or set read-only
chmod 400 /path/to/certificate.pem
# Should show: -r-------- (400)
# Re-test
azlin auth test --profile prodProblem: AZURE_CLIENT_SECRET or AZLIN_SP_CLIENT_SECRET environment variable not set
Error message:
✗ Credential validation failed: Client secret not found in environment variables
Solution:
# Set client secret
export AZURE_CLIENT_SECRET="your-secret-here"
# Or use azlin-specific variable
export AZLIN_SP_CLIENT_SECRET="your-secret-here"
# Verify it's set
echo $AZURE_CLIENT_SECRET
# Make it permanent
echo 'export AZURE_CLIENT_SECRET="your-secret-here"' >> ~/.bashrc
source ~/.bashrc
# Re-test
azlin auth test --profile devProblem: Certificate expires within 30 days or has already expired
Warning message:
Warning: Certificate expires in 15 days on 2025-11-15
Error message:
✗ Certificate validation failed: Certificate has expired on 2025-10-15
Solution:
# Generate new certificate
openssl req -x509 -newkey rsa:4096 -keyout new-key.pem -out new-cert.pem -days 365 -nodes \
-subj "/CN=azlin-service-principal"
# Combine
cat new-key.pem new-cert.pem > azlin-sp-new.pem
chmod 600 azlin-sp-new.pem
mv azlin-sp-new.pem ~/.azlin/certs/
# Update service principal
az ad sp credential reset \
--id {CLIENT-ID} \
--cert @~/.azlin/certs/azlin-sp-new.pem
# Update profile
azlin auth setup --profile prod \
--tenant-id "$TENANT_ID" \
--client-id "$CLIENT_ID" \
--subscription-id "$SUBSCRIPTION_ID" \
--use-certificate \
--certificate-path ~/.azlin/certs/azlin-sp-new.pem
# Test
azlin auth test --profile prodProblem: Tenant ID or Client ID is not in valid UUID format
Error message:
Error: Invalid tenant_id format: abc123
Solution:
# Get correct values from Azure Portal or CLI
# Tenant ID and Client ID must be in UUID format:
# 12345678-1234-1234-1234-123456789abc
# Get tenant ID
az account show --query tenantId -o tsv
# Get client ID for service principal
az ad sp list --display-name "azlin-automation" --query "[0].appId" -o tsv
# Use correct UUIDs in setup
azlin auth setup --profile prod \
--tenant-id "$(az account show --query tenantId -o tsv)" \
--client-id "$(az ad sp list --display-name 'azlin-automation' --query '[0].appId' -o tsv)" \
--subscription-id "$(az account show --query id -o tsv)"Problem: Incorrect credentials or insufficient permissions
Error message:
✗ Authentication failed
Error: AADSTS7000215: Invalid client secret provided
Solution:
Check credentials are correct:
# Verify tenant ID
az account show --query tenantId -o tsv
# Verify client ID
az ad sp show --id {CLIENT-ID} --query appId -o tsv
# Verify client secret (create new if unsure)
az ad sp credential reset --id {CLIENT-ID}
# Update environment variable with new secret
export AZURE_CLIENT_SECRET="new-secret-value"
# Re-test
azlin auth test --profile prodCheck permissions:
# List role assignments for service principal
az role assignment list --assignee {CLIENT-ID} --output table
# Grant required permissions if missing
az role assignment create \
--assignee {CLIENT-ID} \
--role Contributor \
--scope /subscriptions/{SUBSCRIPTION-ID}
# Re-test
azlin auth test --profile prodProblem: Specified profile doesn't exist in configuration
Error message:
Error: Profile 'production' not found.
Available profiles: dev, staging
Solution:
# List available profiles
azlin auth list
# Create missing profile
azlin auth setup --profile production
# Or check for typo in profile name
azlin auth show prod # Instead of 'production'Problem: Azure CLI (az command) is not installed
Error message:
✗ Azure CLI not found
Please install Azure CLI: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli
Solution:
macOS:
brew install azure-cliLinux (Debian/Ubuntu):
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bashLinux (RPM-based):
sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
sudo dnf install azure-cliWindows:
# Using winget
winget install Microsoft.AzureCLI
# Or download installer from:
# https://aka.ms/installazurecliwindowsVerify installation:
az --versionEnable verbose logging for detailed troubleshooting:
# Enable verbose output
azlin --verbose auth test --profile prod
# Check azlin logs
tail -f ~/.azlin/logs/azlin.log
# Test with Azure CLI debug mode
export AZURE_CLI_LOGGING_ENABLED=1
azlin auth test --profile prodIf you're currently using Azure CLI authentication and want to add service principal support:
Azure CLI authentication remains the default. Your existing workflows continue to work:
# Still works - uses Azure CLI by default
azlin list
azlin new
azlin statusService principals are completely opt-in. Only use them when you specify a profile:
# Uses Azure CLI (default)
azlin list
# Uses service principal (explicit)
azlin --auth-profile prod list-
Continue using Azure CLI for development
az login azlin new # Uses Azure CLI -
Add service principal for CI/CD
azlin auth setup --profile ci # Use in GitHub Actions -
Optionally migrate production to service principals
azlin auth setup --profile prod --use-certificate
azlin automatically falls back to Azure CLI if:
- No profile is specified
- Profile authentication fails
- Service principal credentials are invalid
This ensures seamless operation during migration.
# Create service principal with Contributor role on subscription
az ad sp create-for-rbac \
--name "azlin-automation" \
--role Contributor \
--scopes /subscriptions/{SUBSCRIPTION-ID}
# Output:
# {
# "appId": "12345678-1234-1234-1234-123456789abc", # CLIENT_ID
# "displayName": "azlin-automation",
# "password": "your-secret-value-here", # CLIENT_SECRET
# "tenant": "87654321-4321-4321-4321-cba987654321" # TENANT_ID
# }# Create with access only to specific resource group
az ad sp create-for-rbac \
--name "azlin-dev" \
--role Contributor \
--scopes /subscriptions/{SUBSCRIPTION-ID}/resourceGroups/{RESOURCE-GROUP}# Generate certificate first
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes \
-subj "/CN=azlin-service-principal"
cat key.pem cert.pem > azlin-sp.pem
chmod 600 azlin-sp.pem
# Create service principal with certificate
az ad sp create-for-rbac \
--name "azlin-automation-cert" \
--role Contributor \
--scopes /subscriptions/{SUBSCRIPTION-ID} \
--cert @azlin-sp.pem-
Navigate to Azure Active Directory
- Go to Azure Portal
- Search for "Azure Active Directory" or select it from left menu
-
Create App Registration
- Click "App registrations" in left menu
- Click "New registration"
- Name:
azlin-automation - Supported account types: "Accounts in this organizational directory only"
- Redirect URI: Leave blank
- Click "Register"
-
Note Application Details
- On the Overview page, copy:
- Application (client) ID → Use as
CLIENT_ID - Directory (tenant) ID → Use as
TENANT_ID
- Application (client) ID → Use as
- On the Overview page, copy:
-
Create Client Secret (for password-based auth)
- Click "Certificates & secrets" in left menu
- Click "New client secret"
- Description:
azlin-secret - Expires: 12 months (recommended)
- Click "Add"
- Copy the secret value immediately (shown only once!) → Use as
CLIENT_SECRET
-
Upload Certificate (for certificate-based auth)
- Click "Certificates & secrets" in left menu
- Click "Upload certificate"
- Select your
.pemor.cerfile - Click "Add"
-
Grant Permissions
- Go to your Subscription (search for "Subscriptions")
- Select your subscription
- Click "Access control (IAM)" in left menu
- Click "Add" → "Add role assignment"
- Role: Contributor
- Members: Search for
azlin-automation - Click "Review + assign"
Service principals need appropriate Azure permissions to manage resources.
| Operation | Minimum Role | Scope |
|---|---|---|
| List VMs | Reader | Subscription or Resource Group |
| Create VMs | Contributor | Resource Group |
| Start/Stop VMs | Virtual Machine Contributor | Resource Group |
| Manage Storage | Storage Account Contributor | Resource Group |
| View Costs | Cost Management Reader | Subscription |
# Grant Contributor role to entire subscription
az role assignment create \
--assignee {CLIENT-ID} \
--role Contributor \
--scope /subscriptions/{SUBSCRIPTION-ID}Use when:
- Managing resources across multiple resource groups
- Creating new resource groups dynamically
- Full subscription management
# Grant Contributor role to specific resource group
az role assignment create \
--assignee {CLIENT-ID} \
--role Contributor \
--scope /subscriptions/{SUBSCRIPTION-ID}/resourceGroups/{RESOURCE-GROUP}Use when:
- Limiting access to specific workloads
- Team-based resource isolation
- Following least-privilege principle
# Grant access to multiple resource groups
for RG in azlin-dev azlin-staging azlin-prod; do
az role assignment create \
--assignee {CLIENT-ID} \
--role Contributor \
--scope /subscriptions/{SUBSCRIPTION-ID}/resourceGroups/$RG
done# Reader - Read-only access
az role assignment create \
--assignee {CLIENT-ID} \
--role Reader \
--scope /subscriptions/{SUBSCRIPTION-ID}
# Virtual Machine Contributor - VM management only
az role assignment create \
--assignee {CLIENT-ID} \
--role "Virtual Machine Contributor" \
--scope /subscriptions/{SUBSCRIPTION-ID}/resourceGroups/{RESOURCE-GROUP}
# Contributor - Full management (no permission changes)
az role assignment create \
--assignee {CLIENT-ID} \
--role Contributor \
--scope /subscriptions/{SUBSCRIPTION-ID}# Grant access to Key Vault secrets
az role assignment create \
--assignee {CLIENT-ID} \
--role "Key Vault Secrets User" \
--scope /subscriptions/{SUBSCRIPTION-ID}/resourceGroups/{RESOURCE-GROUP}/providers/Microsoft.KeyVault/vaults/{KEYVAULT-NAME}# List role assignments for service principal
az role assignment list \
--assignee {CLIENT-ID} \
--output table
# Output:
# Principal Role Scope
# ------------------------------------ ------------ ----------------------------------------------------------------
# 12345678-1234-1234-1234-123456789abc Contributor /subscriptions/abcdef12-3456-7890-abcd-ef1234567890/resourceGroups/azlin-rg| Variable | Required | Auth Method | Description |
|---|---|---|---|
AZURE_TENANT_ID |
Yes (SP/MI) | Service Principal, Managed Identity | Azure Active Directory tenant ID (UUID format) |
AZURE_CLIENT_ID |
Yes (SP/MI) | Service Principal, Managed Identity | Application (client) ID (UUID format) |
AZURE_CLIENT_SECRET |
Yes (secret) | Service Principal (Secret) | Client secret value for password-based auth |
AZLIN_SP_CLIENT_SECRET |
Alternative | Service Principal (Secret) | Alternative to AZURE_CLIENT_SECRET (azlin-specific) |
AZURE_CLIENT_CERTIFICATE_PATH |
Yes (cert) | Service Principal (Certificate) | Absolute path to certificate file (.pem) |
AZLIN_AUTH_PROFILE |
Optional | All | Default authentication profile to use |
AZURE_SUBSCRIPTION_ID |
Optional | All | Default subscription ID (if not in profile) |
When multiple variables are set, azlin uses this priority:
- Command-line flags (
--auth-profile,--subscription-id) AZLIN_AUTH_PROFILEenvironment variable- Profile configuration in
~/.azlin/config.toml - Azure CLI default subscription
# Set for current terminal session only
export AZURE_TENANT_ID="87654321-4321-4321-4321-cba987654321"
export AZURE_CLIENT_ID="12345678-1234-1234-1234-123456789abc"
export AZURE_CLIENT_SECRET="your-secret-here"Bash (~/.bashrc or ~/.bash_profile):
# Add to ~/.bashrc
cat >> ~/.bashrc <<'EOF'
export AZURE_TENANT_ID="87654321-4321-4321-4321-cba987654321"
export AZURE_CLIENT_ID="12345678-1234-1234-1234-123456789abc"
export AZURE_CLIENT_SECRET="your-secret-here"
EOF
# Reload
source ~/.bashrcZsh (~/.zshrc):
# Add to ~/.zshrc
cat >> ~/.zshrc <<'EOF'
export AZURE_TENANT_ID="87654321-4321-4321-4321-cba987654321"
export AZURE_CLIENT_ID="12345678-1234-1234-1234-123456789abc"
export AZURE_CLIENT_SECRET="your-secret-here"
EOF
# Reload
source ~/.zshrcenv:
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}variables:
- group: azure-credentials # Variable group with secrets
pool:
vmImage: ubuntu-latest
steps:
- script: |
azlin auth setup --profile prod \
--tenant-id "$(AZURE_TENANT_ID)" \
--client-id "$(AZURE_CLIENT_ID)" \
--subscription-id "$(AZURE_SUBSCRIPTION_ID)"
env:
AZURE_CLIENT_SECRET: $(AZURE_CLIENT_SECRET)# Check if variables are set
echo $AZURE_TENANT_ID
echo $AZURE_CLIENT_ID
echo $AZURE_CLIENT_SECRET # Shows secret (be careful!)
# Check without revealing secret
[ -n "$AZURE_CLIENT_SECRET" ] && echo "Secret is set" || echo "Secret not set"
# Test authentication
azlin auth test --profile prodAfter completing setup:
-
Review Security Best Practices
-
Understand Architecture
- Check Authentication Architecture Design
- Review authentication chain and fallback behavior
-
Start Using azlin
- Read main azlin README for general usage
- Try Quick Start examples
- Explore Command Reference
-
Join Community
- GitHub Issues: https://github.com/rysweet/azlin/issues
- Contribute: See Contributing Guide
Documentation:
- Main README - General azlin usage
- Architecture Design - Technical details
- Security Guide - Security best practices
- API Reference - Complete API documentation
Troubleshooting:
- Review Troubleshooting section above
- Check logs with
--verboseflag - Enable Azure CLI debug mode
- Search GitHub Issues
Community:
- Report bugs: GitHub Issues
- Request features: GitHub Discussions
- Ask questions: Open an issue with
questionlabel
# Setup
azlin auth setup --profile prod
azlin auth test --profile prod
# List profiles
azlin auth list
azlin auth show prod
# Use profile
azlin --auth-profile prod list
azlin --auth-profile prod new --name test-vm
# Remove profile
azlin auth remove old-profile
# Debug
azlin --verbose auth test --profile prodNew in v2.1: Service principal authentication now works seamlessly with Azure VM tags for session management and cross-resource-group discovery.
When you use --auth-profile, azlin:
- Authenticates with the specified service principal
- Sets Azure environment variables (AZURE_CLIENT_ID, etc.)
- All Azure CLI commands inherit these credentials
- TagManager operations automatically use the service principal
- Your personal Azure CLI login remains unchanged
Scenario: Discover all azlin-managed VMs across your entire subscription using a service principal
# Setup service principal profile (one-time)
azlin auth setup --profile team-vms
export AZURE_CLIENT_SECRET="your-sp-secret"
# List ALL azlin VMs across ALL resource groups
azlin --auth-profile team-vms list
# Output shows VMs from multiple resource groups:
# SESSION NAME VM NAME STATUS IP REGION SIZE
# amplihack-dev azlin-vm-1761056536 Running 20.9.184.101 westus2 Standard_D2s_v3
# simserv azlin-vm-1760983759 Running 4.246.117.21 westus2 Standard_D2s_v3
# atg azlin-vm-1760928027 Running 4.155.31.150 eastus Standard_D2s_v3Session names are stored in Azure VM tags, which means:
- The service principal needs
Microsoft.Compute/virtualMachines/writepermission - Session names are shared across all users of the service principal
- Perfect for team environments
# Set session name using service principal
azlin --auth-profile team-vms session azlin-vm-123 production
# This writes to Azure VM tags:
# - managed-by: azlin
# - azlin-session: production
# - azlin-created: 2025-10-24T...
# - azlin-owner: team-vms
# Anyone using the same service principal sees the session
azlin --auth-profile team-vms list
# Shows "production" in SESSION NAME columnManage VMs across different subscriptions with different service principals:
# Development subscription
azlin --auth-profile dev-sub list
azlin --auth-profile dev-sub new --name dev-vm --session testing
# Production subscription
azlin --auth-profile prod-sub list
azlin --auth-profile prod-sub session prod-vm-1 api-server
# Your personal Azure work (different auth context)
az vm list --subscription my-personal-subSetup (Team Lead):
# Create service principal for team
az ad sp create-for-rbac --name "team-azlin-sp" --role Contributor
# Share credentials with team (secure channel)
# Store in team password manager or Azure Key VaultUsage (Team Members):
# Each team member configures same profile
azlin auth setup --profile team
export AZURE_CLIENT_SECRET="team-shared-secret"
# Everyone sees the same VMs and session names
azlin --auth-profile team list
# Session names are shared (stored in Azure tags)
azlin --auth-profile team session worker-1 alice-dev
azlin --auth-profile team session worker-2 bob-devGitHub Actions with Azure Tags:
name: Manage Azure VMs
on:
push:
branches: [main]
jobs:
vm-management:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup azlin
run: pip install azlin
- name: Configure service principal
env:
AZURE_CLIENT_SECRET: ${{ secrets.AZLIN_SP_SECRET }}
run: |
azlin auth setup --profile ci \
--client-id ${{ secrets.AZLIN_CLIENT_ID }} \
--tenant-id ${{ secrets.AZLIN_TENANT_ID }} \
--subscription-id ${{ secrets.AZLIN_SUBSCRIPTION_ID }}
- name: List all VMs (cross-RG discovery)
env:
AZURE_CLIENT_SECRET: ${{ secrets.AZLIN_SP_SECRET }}
run: azlin --auth-profile ci list
- name: Create test VM with session tag
env:
AZURE_CLIENT_SECRET: ${{ secrets.AZLIN_SP_SECRET }}
run: |
azlin --auth-profile ci new --name "ci-vm-${{ github.run_number }}" \
--session "ci-test-${{ github.run_number }}"
- name: Clean up
env:
AZURE_CLIENT_SECRET: ${{ secrets.AZLIN_SP_SECRET }}
run: azlin --auth-profile ci kill "ci-test-${{ github.run_number }}"For full Azure tags functionality, your service principal needs:
az role assignment create \
--assignee "YOUR_SP_CLIENT_ID" \
--role "Virtual Machine Contributor" \
--scope "/subscriptions/YOUR_SUBSCRIPTION_ID"This role includes:
Microsoft.Compute/virtualMachines/read- List VMs and read tagsMicrosoft.Compute/virtualMachines/write- Create VMs and write tagsMicrosoft.Compute/virtualMachines/delete- Delete VMs- Plus network and resource group operations
✅ Separate authentication contexts - azlin uses SP, you use personal login ✅ Cross-resource-group discovery - Find all team VMs with one command ✅ Shared session names - Everyone on team sees same labels ✅ Multi-machine sync - Session names in Azure, not local config ✅ Audit trail - SP operations tracked separately in Azure logs ✅ Team collaboration - Multiple users share same VM pool ✅ CI/CD automation - Fully automated VM lifecycle management
Problem: "Authentication failed" when using --auth-profile
# Verify profile exists
azlin auth list
# Test authentication
azlin auth test --profile your-profile
# Check environment variable is set
echo $AZURE_CLIENT_SECRETProblem: "Failed to set session name in VM tags"
# Verify SP has write permissions
az role assignment list --assignee YOUR_SP_CLIENT_ID --output table
# Should show "Virtual Machine Contributor" or "Contributor" roleProblem: "No VMs found" with cross-RG list
# Verify VMs have managed-by=azlin tag
az vm show --name VM_NAME --resource-group RG --query tags
# Tag new VMs manually if needed
azlin --auth-profile team session old-vm my-sessionDocument Version: 2.0 Last Updated: 2025-10-24 Feedback: Open an issue at https://github.com/rysweet/azlin/issues