Skip to content

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

The default authentication method tries multiple credential sources in order:

  1. Environment variables (AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID)
  2. Managed Identity (if running on Azure infrastructure)
  3. Azure CLI (if authenticated via az login)
  4. Visual Studio Code (if authenticated)
  5. Azure PowerShell (if authenticated)
providers:
  azure:
    kind: azure
    auth:
      kind: default

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):

providers:
  azure:
    kind: azure
    auth:
      kind: cli

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:

  1. For Managed Identity:

    # Verify managed identity is enabled
    az webapp identity show --name myapp --resource-group myapp-rg
    
    # Check if identity has Key Vault access
    az role assignment list --assignee {identity-object-id} --scope {key-vault-id}
    

  2. For Azure CLI:

    # Verify CLI authentication
    az account show
    
    # Re-authenticate if needed
    az login
    

  3. For Service Principal:

    # Test service principal authentication
    az login --service-principal \
      --username {client-id} \
      --password {client-secret} \
      --tenant {tenant-id}
    

Permission Denied

Error: Forbidden: The user, group or application does not have secrets get permission

Solutions:

  1. Check RBAC role assignments:

    az role assignment list \
      --assignee {object-id} \
      --scope /subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.KeyVault/vaults/{vault}
    

  2. Grant appropriate role:

    az role assignment create \
      --role "Key Vault Secrets Officer" \
      --assignee {object-id} \
      --scope {key-vault-resource-id}
    

  3. For access policies (classic model):

    az keyvault set-policy \
      --name myapp-kv \
      --object-id {object-id} \
      --secret-permissions get list set
    

Key Vault Not Found

Error: VaultNotFound: Vault not found

Solutions:

  1. Verify vault URL is correct:

    az keyvault show --name myapp-kv
    

  2. Check DNS resolution:

    nslookup myapp-kv.vault.azure.net
    

  3. Ensure vault is not soft-deleted:

    az keyvault list-deleted
    
    # Recover if needed
    az keyvault recover --name myapp-kv
    

Network Access Issues

Error: Network access denied

Solutions:

  1. Check Key Vault firewall settings:

    az keyvault network-rule list --name myapp-kv
    

  2. Add IP or VNet rule:

    # Allow specific IP
    az keyvault network-rule add \
      --name myapp-kv \
      --ip-address 1.2.3.4
    
    # Allow VNet subnet
    az keyvault network-rule add \
      --name myapp-kv \
      --vnet-name myapp-vnet \
      --subnet myapp-subnet
    

  3. Allow trusted Azure services:

    az keyvault update \
      --name myapp-kv \
      --bypass AzureServices
    

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:

  1. Cache secrets in application memory
  2. Use managed identities (no additional cost)
  3. Consolidate secrets in fewer vaults
  4. Use standard tier unless HSM keys required
  5. 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")

See Also