Azure Provider¶
The Azure provider enables SecretZero to store and manage secrets in Azure Key Vault. It supports multiple authentication methods including Managed Identity, Default Azure Credential chain, and Azure CLI authentication.
Overview¶
The Azure provider is ideal for:
- Azure-native applications running on Azure App Service, Azure Functions, AKS, or Azure VMs
- Managed Identity authentication for secure, credential-free access
- Enterprise applications requiring centralized secret management with RBAC
- Multi-cloud deployments needing Azure integration
- Compliance requirements that mandate Azure-native security services
Supported Target Types¶
| Target Type | Description | Use Case |
|---|---|---|
key_vault |
Azure Key Vault Secrets | Application secrets, certificates, connection strings, API keys |
Authentication Methods¶
Default Credential Chain (Recommended)¶
The default authentication method tries multiple credential sources in order:
- Environment variables (
AZURE_CLIENT_ID,AZURE_CLIENT_SECRET,AZURE_TENANT_ID) - Managed Identity (if running on Azure infrastructure)
- Azure CLI (if authenticated via
az login) - Visual Studio Code (if authenticated)
- Azure PowerShell (if authenticated)
When to use: Most scenarios, especially production environments with Managed Identity or development with Azure CLI.
Managed Identity Authentication¶
Use the system-assigned or user-assigned managed identity of Azure resources:
# System-assigned managed identity
providers:
azure:
kind: azure
auth:
kind: managed_identity
# User-assigned managed identity
providers:
azure:
kind: azure
auth:
kind: managed_identity
client_id: 12345678-1234-1234-1234-123456789012
When to use: Production deployments on Azure App Service, Azure Functions, AKS, Azure VMs, or Azure Container Instances.
Azure CLI Authentication¶
Use credentials from the Azure CLI (az login):
When to use: Local development, testing, CI/CD pipelines with Azure CLI pre-authenticated.
Configuration¶
Basic Configuration¶
version: '1.0'
providers:
azure:
kind: azure
auth:
kind: default
secrets:
- name: database-password
kind: random_password
config:
length: 32
special: true
targets:
- provider: azure
kind: key_vault
config:
vault_url: https://myapp-secrets.vault.azure.net
secret_name: database-password
Multi-Vault Configuration¶
Store secrets in multiple Key Vaults:
providers:
azure_prod:
kind: azure
auth:
kind: default
azure_dr:
kind: azure
auth:
kind: default
secrets:
- name: api-key
kind: random_string
config:
length: 32
targets:
# Primary vault
- provider: azure_prod
kind: key_vault
config:
vault_url: https://prod-secrets.vault.azure.net
secret_name: api-key
# DR vault
- provider: azure_dr
kind: key_vault
config:
vault_url: https://dr-secrets.vault.azure.net
secret_name: api-key
Service Principal Authentication¶
For CI/CD or cross-tenant scenarios:
providers:
azure:
kind: azure
auth:
kind: default # Uses environment variables
# Set these environment variables:
# export AZURE_CLIENT_ID=<service-principal-app-id>
# export AZURE_CLIENT_SECRET=<service-principal-password>
# export AZURE_TENANT_ID=<azure-tenant-id>
Target Type: Key Vault¶
Azure Key Vault provides secure storage for secrets, keys, and certificates with comprehensive access control and audit logging.
Configuration Options¶
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
vault_url |
string | Yes | - | Key Vault URL (e.g., https://myvault.vault.azure.net) |
secret_name |
string | Yes | - | Name of the secret in Key Vault |
content_type |
string | No | - | Content type metadata (e.g., "application/json") |
tags |
object | No | - | Key-value tags for the secret |
enabled |
boolean | No | true |
Whether the secret is enabled |
Example: Basic Secret¶
secrets:
- name: api-key
kind: random_string
config:
length: 32
charset: alphanumeric
targets:
- provider: azure
kind: key_vault
config:
vault_url: https://myapp-kv.vault.azure.net
secret_name: api-key
content_type: text/plain
tags:
Environment: production
Application: myapp
ManagedBy: secretzero
Example: JSON Secret¶
secrets:
- name: database-connection
kind: templates.db_config
templates:
db_config:
description: Database connection configuration
fields:
host:
generator:
kind: static
config:
default: myserver.database.azure.com
username:
generator:
kind: static
config:
default: adminuser
password:
generator:
kind: random_password
config:
length: 32
special: true
database:
generator:
kind: static
config:
default: production
targets:
- provider: azure
kind: key_vault
config:
vault_url: https://myapp-kv.vault.azure.net
secret_name: database-connection
content_type: application/json
tags:
Type: database-config
Rotation: 90days
The secret will be stored as JSON in Key Vault:
{
"host": "myserver.database.azure.com",
"username": "adminuser",
"password": "xyz789...",
"database": "production"
}
Example: Connection String¶
secrets:
- name: storage-connection-string
kind: static
config:
default: ${AZURE_STORAGE_CONNECTION_STRING}
targets:
- provider: azure
kind: key_vault
config:
vault_url: https://myapp-kv.vault.azure.net
secret_name: storage-connection-string
content_type: text/plain
tags:
Service: azure-storage
Type: connection-string
Example: Certificate¶
secrets:
- name: ssl-certificate
kind: static
config:
default: |
-----BEGIN CERTIFICATE-----
MIICljCCAX4CCQCKz...
-----END CERTIFICATE-----
targets:
- provider: azure
kind: key_vault
config:
vault_url: https://myapp-kv.vault.azure.net
secret_name: ssl-certificate
content_type: application/x-pem-file
tags:
Type: certificate
Expiry: "2025-12-31"
Complete Examples¶
Example 1: Application Secrets¶
version: '1.0'
variables:
environment: production
vault_url: https://myapp-prod.vault.azure.net
app_name: myapp
providers:
azure:
kind: azure
auth:
kind: managed_identity
secrets:
# Database password
- name: db-password
kind: random_password
rotation_period: 90d
config:
length: 32
special: true
exclude_characters: '"@\'
targets:
- provider: azure
kind: key_vault
config:
vault_url: "{{var.vault_url}}"
secret_name: "{{var.app_name}}-db-password"
tags:
Environment: "{{var.environment}}"
Type: database
Rotation: enabled
# API Key
- name: external-api-key
kind: random_string
config:
length: 64
charset: alphanumeric
targets:
- provider: azure
kind: key_vault
config:
vault_url: "{{var.vault_url}}"
secret_name: external-api-key
content_type: text/plain
tags:
Service: external-api
Environment: "{{var.environment}}"
# JWT Signing Key
- name: jwt-secret
kind: random_string
config:
length: 128
charset: alphanumeric
targets:
- provider: azure
kind: key_vault
config:
vault_url: "{{var.vault_url}}"
secret_name: jwt-signing-key
content_type: text/plain
tags:
Type: jwt
Algorithm: HS256
metadata:
project: "{{var.app_name}}"
owner: backend-team
environment: "{{var.environment}}"
Example 2: Multi-Environment Setup¶
version: '1.0'
variables:
app_name: myapp
providers:
azure_dev:
kind: azure
auth:
kind: cli # Local development
azure_staging:
kind: azure
auth:
kind: managed_identity
client_id: dev-managed-identity-id
azure_prod:
kind: azure
auth:
kind: managed_identity
client_id: prod-managed-identity-id
secrets:
- name: database-password
kind: random_password
config:
length: 32
special: true
targets:
# Development
- provider: azure_dev
kind: key_vault
config:
vault_url: https://myapp-dev.vault.azure.net
secret_name: database-password
tags:
Environment: development
# Staging
- provider: azure_staging
kind: key_vault
config:
vault_url: https://myapp-staging.vault.azure.net
secret_name: database-password
tags:
Environment: staging
# Production
- provider: azure_prod
kind: key_vault
config:
vault_url: https://myapp-prod.vault.azure.net
secret_name: database-password
tags:
Environment: production
Critical: "true"
Example 3: Azure SQL Database Credentials¶
version: '1.0'
providers:
azure:
kind: azure
auth:
kind: default
secrets:
- name: azure-sql-credentials
kind: templates.sql_credentials
templates:
sql_credentials:
description: Azure SQL Database credentials
fields:
server:
generator:
kind: static
config:
default: myserver.database.windows.net
database:
generator:
kind: static
config:
default: production
username:
generator:
kind: static
config:
default: sqladmin
password:
generator:
kind: random_password
config:
length: 32
special: true
exclude_characters: '"@\'
connection_string:
generator:
kind: static
config:
default: "Server=tcp:myserver.database.windows.net,1433;Database=production;User ID=sqladmin;Password={password};Encrypt=true;Connection Timeout=30;"
targets:
- provider: azure
kind: key_vault
config:
vault_url: https://myapp-kv.vault.azure.net
secret_name: azure-sql-credentials
content_type: application/json
tags:
Service: azure-sql
Database: production
Azure RBAC Permissions¶
Required Permissions¶
Grant the following Azure RBAC roles or permissions:
Option 1: Use Built-in Roles¶
Assign the Key Vault Secrets Officer role:
# For a user
az role assignment create \
--role "Key Vault Secrets Officer" \
--assignee user@example.com \
--scope /subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.KeyVault/vaults/{vault-name}
# For a managed identity
az role assignment create \
--role "Key Vault Secrets Officer" \
--assignee {managed-identity-client-id} \
--scope /subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.KeyVault/vaults/{vault-name}
# For a service principal
az role assignment create \
--role "Key Vault Secrets Officer" \
--assignee {service-principal-app-id} \
--scope /subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.KeyVault/vaults/{vault-name}
Option 2: Use Key Vault Access Policies (Classic)¶
If using access policies instead of RBAC:
az keyvault set-policy \
--name {vault-name} \
--object-id {object-id} \
--secret-permissions get list set delete
Minimum Required Permissions¶
For fine-grained RBAC control:
{
"permissions": [
{
"actions": [
"Microsoft.KeyVault/vaults/secrets/read",
"Microsoft.KeyVault/vaults/secrets/write",
"Microsoft.KeyVault/vaults/secrets/delete"
],
"notActions": [],
"dataActions": [
"Microsoft.KeyVault/vaults/secrets/getSecret/action",
"Microsoft.KeyVault/vaults/secrets/setSecret/action",
"Microsoft.KeyVault/vaults/secrets/readMetadata/action"
],
"notDataActions": []
}
]
}
Best Practices¶
1. Use Managed Identity in Production¶
# Good: Managed Identity (no credentials to manage)
providers:
azure:
kind: azure
auth:
kind: managed_identity
# Avoid: Service principal with secrets in code
providers:
azure:
kind: azure
auth:
kind: default
# Don't put credentials in the config file
2. Implement Naming Conventions¶
# Good: Clear, hierarchical naming
database-password
api-external-key
jwt-signing-key-prod
storage-connection-string
# Avoid: Ambiguous names
secret1
mykey
password
3. Use Tags Consistently¶
targets:
- provider: azure
kind: key_vault
config:
vault_url: https://myapp-kv.vault.azure.net
secret_name: api-key
tags:
Environment: production
Application: myapp
ManagedBy: secretzero
Owner: backend-team
CostCenter: engineering
Compliance: pci-dss
4. Separate Key Vaults by Environment¶
# Development
vault_url: https://myapp-dev.vault.azure.net
# Staging
vault_url: https://myapp-staging.vault.azure.net
# Production
vault_url: https://myapp-prod.vault.azure.net
Benefits: - Isolation between environments - Separate RBAC policies - Different compliance requirements - Independent disaster recovery
5. Enable Soft Delete and Purge Protection¶
# Enable soft delete (retention period: 90 days)
az keyvault update \
--name myapp-kv \
--enable-soft-delete true \
--retention-days 90
# Enable purge protection (prevents permanent deletion)
az keyvault update \
--name myapp-kv \
--enable-purge-protection true
Integration with Azure Services¶
Azure App Service¶
Access Key Vault secrets in App Service:
# Using App Service configuration
az webapp config appsettings set \
--resource-group myapp-rg \
--name myapp \
--settings \
"DB_PASSWORD=@Microsoft.KeyVault(SecretUri=https://myapp-kv.vault.azure.net/secrets/database-password/)" \
"API_KEY=@Microsoft.KeyVault(SecretUri=https://myapp-kv.vault.azure.net/secrets/api-key/)"
In application code:
import os
# Secrets automatically injected as environment variables
db_password = os.environ.get('DB_PASSWORD')
api_key = os.environ.get('API_KEY')
Azure Functions¶
Access secrets in Azure Functions:
import os
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
def main(req):
# Using environment variables (App Settings reference Key Vault)
db_password = os.environ.get('DB_PASSWORD')
# Or direct SDK access
credential = DefaultAzureCredential()
client = SecretClient(
vault_url="https://myapp-kv.vault.azure.net",
credential=credential
)
secret = client.get_secret("database-password")
db_password = secret.value
Azure Kubernetes Service (AKS)¶
Use Azure Key Vault Provider for Secrets Store CSI Driver:
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: azure-keyvault
spec:
provider: azure
parameters:
usePodIdentity: "false"
useVMManagedIdentity: "true"
userAssignedIdentityID: "client-id-of-managed-identity"
keyvaultName: "myapp-kv"
objects: |
array:
- |
objectName: database-password
objectType: secret
objectVersion: ""
tenantId: "your-tenant-id"
---
apiVersion: v1
kind: Pod
metadata:
name: myapp
spec:
serviceAccountName: myapp-sa
containers:
- name: myapp
image: myapp:latest
volumeMounts:
- name: secrets-store
mountPath: "/mnt/secrets"
readOnly: true
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: database-password
key: password
volumes:
- name: secrets-store
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "azure-keyvault"
Azure DevOps Pipelines¶
Access Key Vault secrets in Azure Pipelines:
trigger:
- main
pool:
vmImage: 'ubuntu-latest'
variables:
- group: myapp-keyvault # Variable group linked to Key Vault
steps:
- task: AzureKeyVault@2
inputs:
azureSubscription: 'my-service-connection'
KeyVaultName: 'myapp-kv'
SecretsFilter: '*'
RunAsPreJob: true
- script: |
echo "Using secrets from Key Vault"
echo "API Key: $(api-key)"
displayName: 'Use secrets'
Troubleshooting¶
Authentication Errors¶
Error: Azure authentication failed. Check credentials and configuration.
Solutions:
-
For Managed Identity:
-
For Azure CLI:
-
For Service Principal:
Permission Denied¶
Error: Forbidden: The user, group or application does not have secrets get permission
Solutions:
-
Check RBAC role assignments:
-
Grant appropriate role:
-
For access policies (classic model):
Key Vault Not Found¶
Error: VaultNotFound: Vault not found
Solutions:
-
Verify vault URL is correct:
-
Check DNS resolution:
-
Ensure vault is not soft-deleted:
Network Access Issues¶
Error: Network access denied
Solutions:
-
Check Key Vault firewall settings:
-
Add IP or VNet rule:
-
Allow trusted Azure services:
Cost Optimization¶
Azure Key Vault Pricing¶
- Standard tier: $0.03 per 10,000 transactions
- Premium tier: \(0.03 per 10,000 transactions + HSM-backed keys (\)1.00 per key per month)
- Certificate operations: $0.30 per renewal request
Cost-saving tips:
- Cache secrets in application memory
- Use managed identities (no additional cost)
- Consolidate secrets in fewer vaults
- Use standard tier unless HSM keys required
- Monitor transactions with Azure Monitor
Transaction Optimization¶
from azure.keyvault.secrets import SecretClient
from azure.identity import DefaultAzureCredential
import time
# Bad: Fetch secret on every request
def get_config_bad():
client = SecretClient(vault_url="https://myapp-kv.vault.azure.net",
credential=DefaultAzureCredential())
return client.get_secret("api-key").value
# Good: Cache secret with refresh
class SecretCache:
def __init__(self, vault_url):
self.client = SecretClient(vault_url=vault_url,
credential=DefaultAzureCredential())
self.cache = {}
self.ttl = 3600 # 1 hour
def get_secret(self, name):
if name in self.cache:
value, timestamp = self.cache[name]
if time.time() - timestamp < self.ttl:
return value
# Fetch from Key Vault
secret = self.client.get_secret(name)
self.cache[name] = (secret.value, time.time())
return secret.value
cache = SecretCache("https://myapp-kv.vault.azure.net")
api_key = cache.get_secret("api-key")