🔑 Azure Security

Azure Key Vault
Complete Guide

From beginner to architecture level — secrets, keys, certificates, RBAC, Managed Identity, private endpoints, soft delete, rotation, Function Apps, AKS, DevOps, monitoring, Managed HSM, and every secrets management pattern you need.

Beginner → Architecture22 SectionsZero-Secret ArchitectureFIPS 140-2 Level 2/3SDK + REST Examples

01What Is Azure Key Vault?

Azure Key Vault is a cloud-based, managed hardware security module (HSM) service for storing and accessing secrets, cryptographic keys, and certificates. It centralizes application secrets, tightly controls access with Entra ID, logs all access activity, and helps achieve a zero-secret architecture — where no credentials ever appear in code, config files, or environment variables.

🔐
Centralized Secrets
Store connection strings, API keys, passwords, and tokens in one secured, audited location
🗝️
Cryptographic Keys
Create RSA/EC keys for encryption, signing, wrapping — keys never leave the vault
📜
Certificate Management
Provision, renew, and deploy SSL/TLS certificates with auto-renewal from DigiCert, GlobalSign
🛡️
HSM-Backed
Premium tier keys backed by FIPS 140-2 Level 2 HSMs; Managed HSM offers Level 3
📋
Full Audit Trail
Every secret read, key operation, and access attempt logged to Azure Monitor
♻️
Auto-Rotation
Event-driven secret rotation via Event Grid — rotate DB passwords without downtime

02Core Concepts & Terminology

Before diving into Key Vault operations, it helps to understand the foundational terminology that underpins the service. These concepts define how secrets are organized, accessed, versioned, and protected throughout their lifecycle. Mastering this vocabulary ensures you can design secure vault architectures, configure access correctly, and communicate clearly with your team about secrets management strategies.

TermDefinition
VaultA logical container for secrets, keys, and certificates. Unique DNS: <vault-name>.vault.azure.net
SecretAny text value — connection strings, API keys, passwords, tokens, JSON blobs
KeyA cryptographic key (RSA 2048/3072/4096, EC P-256/P-384/P-521) — used for encrypt/decrypt/sign/verify
CertificateAn X.509 certificate with its private key managed together — for TLS, code signing, etc.
VersionEvery secret/key/certificate update creates a new immutable version. Old versions still accessible.
Secret IdentifierUnique URI: https://<vault>.vault.azure.net/secrets/<name>/<version>
Access PolicyLegacy model — grants a principal Get/List/Set/Delete on vault objects
RBACRecommended model — fine-grained role assignments using Entra ID
Soft DeleteDeleted objects retained for 7–90 days before permanent removal
Purge ProtectionPrevents permanent deletion of a vault or its objects — compliance requirement
Managed HSMSingle-tenant, FIPS 140-2 Level 3 HSM pool — highest security tier

03Secrets, Keys & Certificates

Key Vault manages three distinct object types, each designed for a specific security use case. Secrets hold arbitrary text values like connection strings and API keys, Keys provide server-side cryptographic operations without ever exposing the key material, and Certificates manage the full lifecycle of X.509 certificates including auto-renewal. Choosing the right object type ensures you get the appropriate security guarantees and operational capabilities for each credential in your system.

🔐 Secrets

Opaque string values. Any text up to 25 KB.

  • Database connection strings
  • API keys & tokens
  • Passwords & credentials
  • Storage account keys
  • OAuth client secrets
  • Arbitrary JSON config blobs
🗝️ Keys

Cryptographic keys. Operations performed server-side.

  • RSA 2048 / 3072 / 4096
  • EC P-256 / P-384 / P-521
  • AES (Managed HSM only)
  • Encrypt / Decrypt
  • Sign / Verify
  • Wrap Key / Unwrap Key
📜 Certificates

X.509 certs with lifecycle management.

  • TLS / SSL certificates
  • Code signing certificates
  • Auto-renew with DigiCert / GlobalSign
  • Self-signed certificates
  • Import existing PFX/PEM
  • Push to App Service / APIM

04Tiers — Standard vs Premium

Azure Key Vault offers two pricing tiers that differ primarily in how cryptographic keys are protected. Standard tier uses software-backed keys suitable for most workloads, while Premium tier provides HSM-backed keys validated to FIPS 140-2 Level 2 — a requirement for regulated industries handling payment card data or health records. Choose your tier based on compliance requirements early, as migrating keys between tiers requires re-creation. Both tiers support secrets and certificates identically.

FeatureStandardPremium
Secrets✓ Yes✓ Yes
Software-backed Keys✓ Yes✓ Yes
HSM-backed Keys✗ No✓ Yes
FIPS 140-2 LevelLevel 1 (software)Level 2 (HSM)
Key typesRSA, EC (software)RSA, EC (HSM-backed)
AES keys✗ NoManaged HSM only
PricingPer operationPer operation (higher for HSM keys)
ComplianceGeneral workloadsRegulated industries (PCI-DSS, HIPAA)
💡
When to Use Premium (HSM) KeysUse Premium for keys that protect sensitive data at rest, payment card data, health records, or any scenario where your compliance framework mandates hardware-protected keys. Software keys (Standard) are appropriate for most other applications.

05Access Model — RBAC vs Access Policies

Key Vault supports two authorization models for controlling who can read, write, or manage vault objects. Azure RBAC (the recommended model) provides fine-grained, per-secret role assignments that integrate with Entra ID Conditional Access and Privileged Identity Management. Legacy Access Policies are vault-scoped and limited to 1,024 entries, making them harder to audit and manage at scale. New vaults should always use RBAC mode — it offers better security posture, inheritance from management groups, and supports just-in-time access elevation.

DimensionAzure RBAC (Recommended)Vault Access Policies (Legacy)
Control plane✓ Yes✓ Yes
Data plane✓ Yes✓ Yes
GranularityPer-secret, per-key, per-certVault-wide only
AuditAzure Activity Log + RBAC logsVault diagnostic logs
InheritanceManagement group → subscription → RGVault-scoped only
Max policies per vaultN/A1,024
Conditional Access support✓ Yes✗ No
PIM (Just-in-time access)✓ Yes✗ No
MigrationSwitch vault to RBAC mode (one-way)Default on older vaults

Key Vault RBAC Roles

RoleScopePermissions
Key Vault AdministratorVaultFull data plane + manage access policies
Key Vault Secrets OfficerVault / SecretCRUD secrets, manage secret properties
Key Vault Secrets UserVault / SecretRead secret value only — most applications use this
Key Vault Crypto OfficerVault / KeyCRUD keys, perform all crypto operations
Key Vault Crypto UserVault / KeyUse keys for crypto operations — no management
Key Vault Certificate OfficerVault / CertCRUD certificates
Key Vault ReaderVaultRead metadata only — no secret values
Key Vault Purge OperatorVaultCan purge soft-deleted objects

Assign RBAC Role via CLI

bash
# Grant a Managed Identity read access to secrets only (recommended)
az role assignment create \
  --assignee <principal-id-or-object-id> \
  --role "Key Vault Secrets User" \
  --scope /subscriptions/<sub>/resourceGroups/<rg>/providers/\
Microsoft.KeyVault/vaults/<vault-name>

# Grant access to a specific secret only (fine-grained)
az role assignment create \
  --assignee <principal-id> \
  --role "Key Vault Secrets User" \
  --scope /subscriptions/<sub>/resourceGroups/<rg>/providers/\
Microsoft.KeyVault/vaults/<vault-name>/secrets/<secret-name>

06Working with Secrets

Secrets are the most commonly used Key Vault object type — they store any text value up to 25 KB, including database connection strings, API keys, passwords, and OAuth tokens. The SDK provides a simple get/set interface that works identically across .NET, Python, Java, and JavaScript, with DefaultAzureCredential handling authentication transparently. In production, always set expiration dates and content types on secrets to enable automated rotation workflows and make audit logs more meaningful.

Set & Get Secrets — .NET SDK

csharp
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;

var vaultUri = new Uri("https://<vault-name>.vault.azure.net/");
var credential = new DefaultAzureCredential();
var client = new SecretClient(vaultUri, credential);

// Set a secret
await client.SetSecretAsync("db-connection-string",
    "Server=myserver.database.windows.net;Database=mydb;...");

// Get latest version
KeyVaultSecret secret = await client.GetSecretAsync("db-connection-string");
Console.WriteLine(secret.Value);

// Get specific version
KeyVaultSecret versionedSecret = await client.GetSecretAsync(
    "db-connection-string",
    "a1b2c3d4e5f6...");

// Set with properties (expiry, content type, tags)
var newSecret = new KeyVaultSecret("api-key", "sk-abcdef123456");
newSecret.Properties.ExpiresOn = DateTimeOffset.UtcNow.AddDays(90);
newSecret.Properties.ContentType = "text/plain";
newSecret.Properties.Tags["Environment"] = "Production";
newSecret.Properties.Tags["ManagedBy"] = "Platform-Team";
await client.SetSecretAsync(newSecret);

// List all secret names
await foreach (SecretProperties props in client.GetPropertiesOfSecretsAsync())
{
    Console.WriteLine($"{props.Name} — Enabled: {props.Enabled}, Expires: {props.ExpiresOn}");
}

Set & Get Secrets — Python SDK

python
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
from datetime import datetime, timezone, timedelta

credential = DefaultAzureCredential()
client = SecretClient(vault_url="https://<vault>.vault.azure.net/", credential=credential)

# Set a secret
client.set_secret("db-password", "super-secret-password",
    expires_on=datetime.now(timezone.utc) + timedelta(days=90),
    content_type="text/plain",
    tags={"Environment": "Prod"})

# Get a secret
secret = client.get_secret("db-password")
print(secret.value)

# List all secrets
for secret_props in client.list_properties_of_secrets():
    print(f"{secret_props.name}: expires {secret_props.expires_on}")

CLI Operations

bash
# Create a secret
az keyvault secret set \
  --vault-name myKeyVault \
  --name "db-password" \
  --value "P@ssw0rd!Secure" \
  --expires "2026-12-31T00:00:00Z"

# Show a secret value
az keyvault secret show \
  --vault-name myKeyVault \
  --name "db-password" \
  --query value -o tsv

# List all secrets
az keyvault secret list --vault-name myKeyVault --query "[].name" -o table

# Delete (soft delete — restorable)
az keyvault secret delete --vault-name myKeyVault --name "db-password"

# Restore soft-deleted secret
az keyvault secret recover --vault-name myKeyVault --name "db-password"

# Permanently purge (requires purge protection to be off)
az keyvault secret purge --vault-name myKeyVault --name "db-password"

07Working with Keys (Cryptography)

Key Vault keys enable server-side cryptographic operations — encryption, decryption, signing, and verification — without ever exposing the raw key material to your application. This is critical for compliance because the key never leaves the vault boundary (or HSM boundary on Premium tier), eliminating the risk of key exfiltration. Use RSA keys for encryption and general-purpose signing, and EC keys for high-performance digital signatures. For large data encryption, always use the envelope encryption pattern rather than sending data directly to Key Vault.

Create & Use Keys — .NET SDK

csharp
using Azure.Security.KeyVault.Keys;
using Azure.Security.KeyVault.Keys.Cryptography;

var keyClient = new KeyVaultClient(vaultUri, credential);

// Create RSA 4096-bit key (HSM-protected on Premium)
var rsaKey = new CreateRsaKeyOptions("my-rsa-key")
{
    KeySize = 4096,
    KeyOperations = { KeyOperation.Encrypt, KeyOperation.Decrypt, KeyOperation.Sign, KeyOperation.Verify },
    ExpiresOn = DateTimeOffset.UtcNow.AddYears(1),
    HardwareProtected = true  // HSM-backed (Premium tier required)
};
KeyVaultKey key = await keyClient.CreateRsaKeyAsync(rsaKey);

// Create EC key for signing
var ecKey = new CreateEcKeyOptions("my-ec-key")
{
    CurveName = KeyCurveName.P256,
    KeyOperations = { KeyOperation.Sign, KeyOperation.Verify }
};
await keyClient.CreateEcKeyAsync(ecKey);

// Encrypt data
var cryptoClient = new CryptographyClient(key.Id, credential);
byte[] plaintext = Encoding.UTF8.GetBytes("sensitive data");
EncryptResult encryptResult = await cryptoClient.EncryptAsync(
    EncryptionAlgorithm.RsaOaep256, plaintext);

// Decrypt data
DecryptResult decryptResult = await cryptoClient.DecryptAsync(
    EncryptionAlgorithm.RsaOaep256, encryptResult.Ciphertext);
string decrypted = Encoding.UTF8.GetString(decryptResult.Plaintext);

// Sign data
SignResult signResult = await cryptoClient.SignDataAsync(
    SignatureAlgorithm.RS256, Encoding.UTF8.GetBytes("data to sign"));

// Verify signature
VerifyResult verifyResult = await cryptoClient.VerifyDataAsync(
    SignatureAlgorithm.RS256,
    Encoding.UTF8.GetBytes("data to sign"),
    signResult.Signature);
Console.WriteLine($"Valid: {verifyResult.IsValid}");

Key Operations Reference

OperationDescriptionCommon Use
Encrypt / DecryptAsymmetric encryption with RSAProtect small payloads, key exchange
Sign / VerifyDigital signatures with RSA or ECDocument signing, JWT signing, code signing
Wrap Key / Unwrap KeyEncrypt another key (envelope encryption)Protect symmetric keys (e.g. AES data encryption keys)
ImportBring your own key (BYOK) into Key VaultRegulatory key custody requirements
RotateCreate new key version; old version still availableCryptographic agility
🔑
Envelope Encryption PatternFor encrypting large data, never send it to Key Vault for encryption. Instead: generate a local AES data encryption key (DEK), encrypt your data with the DEK, then use Key Vault's Wrap Key operation to encrypt the DEK. Store the wrapped DEK alongside the encrypted data.

08Working with Certificates

Key Vault certificate management handles the entire lifecycle of X.509 certificates — from creation or import through renewal and deployment. You can generate self-signed certificates for development, or integrate with DigiCert and GlobalSign for production CA-issued certificates that auto-renew before expiry. This eliminates the operational burden of tracking certificate expirations manually, which is one of the most common causes of production outages. Always configure lifetime actions to auto-renew at least 30 days before expiry to allow time for propagation.

OperationDescription
Create (self-signed)Generate a new self-signed X.509 certificate
Create (CA-issued)Generate CSR and auto-fulfill via DigiCert or GlobalSign
Import (PFX/PEM)Import an existing certificate with private key
RenewTrigger renewal before expiry — automated with integrated CAs
ExportDownload certificate (PFX, PEM) for use in other services
Set issuersConfigure DigiCert / GlobalSign as trusted CAs for auto-issuance
Contact notificationsAlert on cert expiry (30, 60, 90 days before)

Create Certificate Policy (Auto-Renew)

csharp
using Azure.Security.KeyVault.Certificates;

var certClient = new CertificateClient(vaultUri, credential);

var certPolicy = new CertificatePolicy("Self", "CN=myapp.contoso.com")
{
    ValidityInMonths = 12,
    ReuseKey = false,
    CertificateTransparency = false,
    ContentType = CertificateContentType.Pkcs12,
    KeyType = CertificateKeyType.Rsa,
    KeySize = 2048,
    KeyUsage = { CertificateKeyUsage.DigitalSignature, CertificateKeyUsage.KeyEncipherment },
    LifetimeActions =
    {
        // Auto-renew 30 days before expiry
        new LifetimeAction(CertificatePolicyAction.AutoRenew)
        {
            DaysBeforeExpiry = 30
        }
    }
};

// Begin certificate creation (async operation)
CertificateOperation operation = await certClient.StartCreateCertificateAsync(
    "my-tls-cert", certPolicy);

// Wait for completion
KeyVaultCertificateWithPolicy cert = await operation.WaitForCompletionAsync();
Console.WriteLine($"Cert thumbprint: {Convert.ToHexString(cert.Properties.X509Thumbprint)}");

09Managed Identity Integration

Managed Identity is the gold standard for authenticating to Key Vault. No connection strings, no certificates, no secrets in config — the Azure platform handles credential issuance and rotation entirely.

  1. 1
    Enable Managed Identity on your resource
    System-assigned: auto-created and deleted with the resource. User-assigned: independently managed identity shared across resources.
  2. 2
    Assign Key Vault RBAC role
    Grant Key Vault Secrets User (or narrower scope) to the managed identity via IAM on the vault.
  3. 3
    Use DefaultAzureCredential in code
    SDK automatically uses Managed Identity when running in Azure. Falls back to Azure CLI / VS credentials in local dev.
  4. 4
    No credential rotation needed
    Microsoft handles token issuance. No secrets stored anywhere. Access can be revoked instantly by removing the role assignment.

Enable System-Assigned Identity via CLI

bash
# Enable on App Service
az webapp identity assign \
  --resource-group myRG \
  --name myAppService

# Enable on Function App
az functionapp identity assign \
  --resource-group myRG \
  --name myFunctionApp

# Enable on AKS node pool (for pod identity)
az aks update \
  --resource-group myRG \
  --name myAKS \
  --enable-managed-identity

# Assign Key Vault Secrets User to the identity
az role assignment create \
  --assignee $(az webapp identity show --resource-group myRG --name myApp --query principalId -o tsv) \
  --role "Key Vault Secrets User" \
  --scope /subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/<vault>

DefaultAzureCredential Chain

EnvironmentCredential Used
Azure VM / App Service / FunctionsManaged Identity (IMDS endpoint)
Azure Kubernetes ServiceWorkload Identity / Pod Identity
Local development (VS Code)Azure CLI credential or Visual Studio credential
Local development (VS)Visual Studio credential
CI/CD pipelineService Principal via env vars AZURE_CLIENT_ID, AZURE_CLIENT_SECRET
GitHub ActionsOIDC federated identity — no secrets stored

10Network Security & Private Endpoints

By default, Key Vault is accessible over the public internet — which is fine for development but unacceptable for production secrets stores. Network security features let you restrict vault access to specific IP ranges, VNet subnets, or eliminate public exposure entirely with Private Endpoints. In production, the recommended configuration is to disable public network access completely and use Private Endpoints combined with the Trusted Microsoft Services bypass, ensuring only your VNet resources and Azure platform services can reach the vault.

FeatureDescription
IP Firewall RulesAllow access from specific IP ranges only — blocks all other public access
VNet Service EndpointsAllow access from specific VNet subnets — traffic stays on Azure backbone
Private EndpointsKey Vault accessible only via private IP within your VNet — no public exposure at all
Trusted Services BypassAllow Azure services (App Service, Functions) to bypass firewall when using Managed Identity
Disable Public Network AccessBlock all public internet access — only private endpoints work

Create Private Endpoint via CLI

bash
# Create private endpoint for Key Vault
az network private-endpoint create \
  --name myKVPrivateEndpoint \
  --resource-group myRG \
  --vnet-name myVNet \
  --subnet mySubnet \
  --private-connection-resource-id \
    /subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/<vault> \
  --group-ids vault \
  --connection-name myKVConnection

# Create Private DNS Zone for Key Vault
az network private-dns zone create \
  --resource-group myRG \
  --name "privatelink.vaultcore.azure.net"

# Link DNS zone to VNet
az network private-dns link vnet create \
  --resource-group myRG \
  --zone-name "privatelink.vaultcore.azure.net" \
  --name myDNSLink \
  --virtual-network myVNet \
  --registration-enabled false
🔒
Recommended Network ConfigurationFor production: disable public access + private endpoints + Trusted Microsoft Services bypass. This ensures only resources within your VNet can reach Key Vault, while Azure platform services using Managed Identity (App Service, Functions) still work without being in the VNet.

11Soft Delete & Purge Protection

Soft delete and purge protection are Key Vault's safety net against accidental or malicious deletion of critical secrets and keys. When soft delete is enabled (mandatory on all new vaults), deleted objects are retained in a recoverable state for 7–90 days before permanent removal. Purge protection goes further by preventing anyone — including administrators — from permanently destroying objects during the retention window. Enable purge protection on all production vaults, especially those holding encryption keys, as losing a key means losing access to all data encrypted with it.

FeatureDescriptionDefault
Soft DeleteDeleted secrets/keys/certs/vaults retained for 7–90 daysEnabled (cannot disable on new vaults)
Soft Delete RetentionHow long deleted items are kept before permanent deletion90 days (configurable 7–90)
Purge ProtectionPrevents permanent deletion during soft-delete window — even by adminsOff (strongly recommended on)
RecoverRestore a soft-deleted object back to active stateAvailable during retention window
PurgePermanently destroy an object (only after soft-delete window, or if purge protection off)Requires explicit permission
🚨
Enable Purge Protection for ComplianceIf using Key Vault for cryptographic key custody (BYOK, HSM keys), enabling purge protection is often required by compliance frameworks (PCI-DSS, HIPAA). Once enabled, it cannot be disabled. Plan before creating production vaults.
bash
# Create vault with purge protection (recommended for production)
az keyvault create \
  --resource-group myRG \
  --name myKeyVault \
  --location eastus \
  --enable-purge-protection true \
  --retention-days 90 \
  --enable-rbac-authorization true

# Enable purge protection on existing vault
az keyvault update \
  --resource-group myRG \
  --name myKeyVault \
  --enable-purge-protection true

12Secret Versioning & Rotation

Every time you update a secret, Key Vault creates a new immutable version. The previous version remains accessible via its version-specific URI — enabling rollback. Automated rotation uses Event Grid to notify Function Apps or Logic Apps when rotation is needed.

Secret Version URIs

text
# Latest version (always resolves to current active version)
https://<vault>.vault.azure.net/secrets/<name>

# Specific version (immutable — always returns that version)
https://<vault>.vault.azure.net/secrets/<name>/<version-id>

# Example
https://myvault.vault.azure.net/secrets/db-password
https://myvault.vault.azure.net/secrets/db-password/a1b2c3d4e5f6789012345678901234ab

Automated Rotation with Event Grid

json
// Event Grid fires this event when a secret is near expiry
{
  "id": "...",
  "topic": "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/<vault>",
  "subject": "db-password",
  "eventType": "Microsoft.KeyVault.SecretNearExpiry",
  "data": {
    "Id": "https://<vault>.vault.azure.net/secrets/db-password/<version>",
    "VaultName": "<vault>",
    "ObjectType": "Secret",
    "ObjectName": "db-password",
    "Version": "<version>",
    "NBF": null,
    "EXP": 1893456000
  }
}

Rotation Function (Event Grid Handler)

csharp
[FunctionName("RotateSecret")]
public static async Task Run(
    [EventGridTrigger] EventGridEvent eventGridEvent,
    ILogger log)
{
    var secretName = eventGridEvent.Subject;
    log.LogInformation($"Rotating secret: {secretName}");

    // 1. Generate new credential at the source system (DB, API, etc.)
    var newPassword = await GenerateNewDbPasswordAsync();

    // 2. Update the secret in Key Vault with new version
    var secretClient = new SecretClient(
        new Uri("https://<vault>.vault.azure.net/"),
        new DefaultAzureCredential());

    var newSecret = new KeyVaultSecret(secretName, newPassword);
    newSecret.Properties.ExpiresOn = DateTimeOffset.UtcNow.AddDays(90);
    await secretClient.SetSecretAsync(newSecret);

    log.LogInformation($"Secret {secretName} rotated successfully");
    // Apps using the latest-version URI will pick up new value on next read
}

13Logic Apps Integration

Logic Apps can retrieve secrets from Key Vault as workflow actions — injecting credentials at runtime without ever storing them in workflow definitions or connection parameters.

ActionDescription
Get secretRetrieve a secret value by name — inject into downstream actions
Set secretCreate or update a secret value
List secretsEnumerate secret names in a vault
Delete secretSoft-delete a secret

Get Secret Action (JSON)

json
{
  "Get_DB_password": {
    "type": "ApiConnection",
    "inputs": {
      "host": {
        "connection": {
          "name": "@parameters('$connections')['keyvault']['connectionId']"
        }
      },
      "method": "get",
      "path": "/secrets/@{encodeURIComponent('db-password')}/value"
    }
  },
  "Call_Database": {
    "type": "Http",
    "inputs": {
      "method": "POST",
      "uri": "https://myapi.contoso.com/data",
      "headers": {
        "X-DB-Password": "@body('Get_DB_password')?['value']"
      }
    },
    "runAfter": { "Get_DB_password": ["Succeeded"] }
  }
}
Use Managed Identity for Logic Apps → Key VaultConfigure the Key Vault connector to use Managed Identity authentication (Standard Logic Apps) instead of a connection string. This avoids any credentials in the workflow definition itself.

14Function Apps Integration

Key Vault References in App Settings (Recommended)

The cleanest pattern: store a Key Vault reference in App Settings rather than the actual value. Azure resolves the reference at runtime — the Function App never sees the raw secret in config.

text
# App Setting value format for Key Vault reference
@Microsoft.KeyVault(SecretUri=https://<vault>.vault.azure.net/secrets/<name>/<version>)

# Or always latest version:
@Microsoft.KeyVault(VaultName=<vault>;SecretName=<name>)

# Example in Azure Portal → Function App → Configuration:
# Name:  DB_CONNECTION_STRING
# Value: @Microsoft.KeyVault(VaultName=myVault;SecretName=db-connection-string)
bash
# Set Key Vault reference via CLI
az functionapp config appsettings set \
  --resource-group myRG \
  --name myFunctionApp \
  --settings "DB_CONNECTION_STRING=@Microsoft.KeyVault(VaultName=myVault;SecretName=db-connection-string)"

Read Secret via SDK in Function Code

csharp
[FunctionName("ProcessData")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
    ILogger log)
{
    // Option 1: Read from App Settings (Key Vault reference resolved by platform)
    string connectionString = Environment.GetEnvironmentVariable("DB_CONNECTION_STRING");

    // Option 2: Read directly from Key Vault via SDK
    var kvClient = new SecretClient(
        new Uri("https://myvault.vault.azure.net/"),
        new DefaultAzureCredential()); // Uses Function App's Managed Identity

    KeyVaultSecret secret = await kvClient.GetSecretAsync("api-key");
    string apiKey = secret.Value;

    return new OkObjectResult("Processed");
}

15App Service & Container Apps

Key Vault References in App Service

App Service and Azure Container Apps support the same Key Vault Reference syntax as Function Apps. Enable system-assigned managed identity, grant Key Vault Secrets User role, then use the reference syntax.

bash
# Enable identity on App Service
az webapp identity assign --resource-group myRG --name myWebApp
PRINCIPAL_ID=$(az webapp identity show --resource-group myRG --name myWebApp --query principalId -o tsv)

# Grant access to Key Vault
az role assignment create \
  --assignee $PRINCIPAL_ID \
  --role "Key Vault Secrets User" \
  --scope /subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/myVault

# Set Key Vault reference as app setting
az webapp config appsettings set \
  --resource-group myRG \
  --name myWebApp \
  --settings "STRIPE_API_KEY=@Microsoft.KeyVault(VaultName=myVault;SecretName=stripe-api-key)"

Container Apps — Secrets from Key Vault

bicep
resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {
  name: 'myapp'
  identity: { type: 'SystemAssigned' }
  properties: {
    configuration: {
      secrets: [
        {
          name: 'db-password'
          keyVaultUrl: 'https://myvault.vault.azure.net/secrets/db-password'
          identity: 'system'  // Use system-assigned identity
        }
      ]
    }
    template: {
      containers: [{
        env: [
          { name: 'DB_PASSWORD', secretRef: 'db-password' }
        ]
      }]
    }
  }
}

16AKS & Kubernetes Integration

Two main approaches for AKS: Workload Identity (Entra ID pod-level identity) combined with the Secrets Store CSI Driver to mount Key Vault secrets as Kubernetes secrets or volume files.

Secrets Store CSI Driver Setup

bash
# Enable CSI driver add-on on AKS
az aks enable-addons \
  --resource-group myRG \
  --name myAKS \
  --addons azure-keyvault-secrets-provider

# Enable Workload Identity
az aks update \
  --resource-group myRG \
  --name myAKS \
  --enable-workload-identity \
  --enable-oidc-issuer

SecretProviderClass Manifest

yaml
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: azure-keyvault-secrets
spec:
  provider: azure
  parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "false"
    clientID: "<workload-identity-client-id>"
    keyvaultName: "myVault"
    cloudName: ""
    objects: |
      array:
        - |
          objectName: db-password
          objectType: secret
          objectVersion: ""
        - |
          objectName: my-tls-cert
          objectType: cert
          objectVersion: ""
    tenantId: "<tenant-id>"
  secretObjects:
    - secretName: app-secrets
      type: Opaque
      data:
        - objectName: db-password
          key: DB_PASSWORD

17CI/CD & DevOps Integration

CI/CD pipelines often need secrets for deployment — database credentials, API keys, signing certificates — but storing them as pipeline variables or repository secrets creates sprawl and audit gaps. Key Vault integration lets your pipelines fetch secrets at runtime using OIDC federated credentials (GitHub Actions) or service connections (Azure DevOps), keeping all secrets centralized with full audit logging. The best practice is to use OIDC authentication where possible, eliminating long-lived service principal secrets entirely from your CI/CD configuration.

GitHub Actions — Read Key Vault Secrets

yaml
name: Deploy with Key Vault Secrets
on:
  push:
    branches: [main]

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

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

    - name: Login to Azure (OIDC — no secrets stored in GitHub)
      uses: azure/login@v1
      with:
        client-id: ${{ secrets.AZURE_CLIENT_ID }}
        tenant-id: ${{ secrets.AZURE_TENANT_ID }}
        subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

    - name: Get secrets from Key Vault
      uses: azure/get-keyvault-secrets@v1
      with:
        keyvault: myKeyVault
        secrets: 'db-connection-string, stripe-api-key, jwt-signing-key'
      id: kvSecrets

    - name: Deploy
      run: |
        # Secrets available as env vars: DB-CONNECTION-STRING, STRIPE-API-KEY, etc.
        echo "Deploying with retrieved secrets..."
      env:
        DB_CONN: ${{ steps.kvSecrets.outputs.db-connection-string }}

Azure DevOps — Key Vault Task

yaml
- task: AzureKeyVault@2
  displayName: 'Get Key Vault Secrets'
  inputs:
    azureSubscription: 'myServiceConnection'
    KeyVaultName: 'myKeyVault'
    SecretsFilter: 'db-password,api-key,jwt-secret'
    RunAsPreJob: true

# Secrets now available as pipeline variables: $(db-password), $(api-key)
- script: echo "Deploying..."
  env:
    DB_PASS: $(db-password)

18Monitoring, Audit & Alerts

Key Vault logs every single operation — every secret read, every failed access, every key use. This audit trail is critical for compliance and breach detection. Enable diagnostic settings to ship logs to Log Analytics.

Log CategoryDescriptionAlert On
AuditEventAll data plane operations: GET, SET, DELETE, SIGN, etc.Unauthorized access attempts
AzurePolicyEvaluationDetailsPolicy compliance evaluationsNon-compliant resources
MetricsAPI latency, availability, total requestsHigh error rate, latency spikes

KQL — Unauthorized Access Attempts

kql
AzureDiagnostics
| where ResourceType == "VAULTS"
| where ResultType != "Success"
| where OperationName in ("SecretGet", "KeySign", "CertificateGet")
| project TimeGenerated, OperationName, ResultType, CallerIpAddress,
          identity_claim_appid_g, requestUri_s
| order by TimeGenerated desc

KQL — Who Accessed a Specific Secret

kql
AzureDiagnostics
| where ResourceType == "VAULTS"
| where OperationName == "SecretGet"
| where id_s contains "db-password"
| project TimeGenerated, CallerIpAddress,
          identity_claim_appid_g,
          identity_claim_oid_g,
          requestUri_s, ResultType
| order by TimeGenerated desc

Enable Diagnostic Settings via CLI

bash
az monitor diagnostic-settings create \
  --resource /subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/<vault> \
  --name "kv-audit-logs" \
  --workspace <log-analytics-workspace-id> \
  --logs '[{"category":"AuditEvent","enabled":true,"retentionPolicy":{"enabled":true,"days":365}}]' \
  --metrics '[{"category":"AllMetrics","enabled":true}]'

19Managed HSM

Azure Managed HSM is a fully managed, single-tenant, FIPS 140-2 Level 3 HSM service. Unlike Key Vault Premium (Level 2, shared HSM), Managed HSM gives you a dedicated HSM pool where Microsoft cannot access your keys — the security domain is entirely customer-controlled.

FeatureKey Vault PremiumManaged HSM
FIPS Level140-2 Level 2140-2 Level 3
Tenant modelMulti-tenant HSMSingle-tenant dedicated
Microsoft key accessPossible (shared)Not possible (customer security domain)
AES-256 keys✗ No✓ Yes
RSA / EC keys✓ Yes✓ Yes
BYOK✓ Yes✓ Yes
Role managementAzure RBACLocal RBAC (Managed HSM-specific roles)
AvailabilityZone-redundant99.9% SLA, zone-redundant
Use caseMost regulated workloadsHighest assurance, payment schemes, govt
PricePer operation~$3.20/hr per HSM unit
bash
# Create Managed HSM
az keyvault create \
  --hsm-name myManagedHSM \
  --resource-group myRG \
  --location eastus \
  --sku Standard_B1 \
  --administrators <admin-object-id>

# Activate (download security domain — required first time)
# This is a ceremony requiring 3+ security domain officers
az keyvault security-domain download \
  --hsm-name myManagedHSM \
  --sd-wrapping-keys cert1.cer cert2.cer cert3.cer \
  --sd-quorum 2 \
  --security-domain-file sd.json

20Security Patterns & Best Practices

These battle-tested patterns represent the collective wisdom of securing secrets at scale across enterprise Azure environments. Each pattern addresses a specific threat vector — from credential leakage in source code to blast radius containment when a single application is compromised. Implementing these patterns together creates a defense-in-depth strategy where no single failure can expose your entire secrets estate. Start with zero-secret architecture and least privilege access as your foundation, then layer on rotation and vault isolation as your organization matures.

0️⃣
Zero-Secret Architecture
No credentials in code, config, or environment variables. Every secret fetched at runtime via Managed Identity + Key Vault.
🔏
Envelope Encryption
Key Vault wraps your AES data encryption key. Raw data never sent to vault. Protects large datasets at rest.
♻️
Event-Driven Rotation
Event Grid fires on SecretNearExpiry → Function rotates credential at source → updates Key Vault version. Zero downtime.
🏗️
One Vault Per Environment
Separate vaults for dev, staging, production. Never share credentials across environments. Different access policies per env.
🕵️
Least Privilege Access
Grant Key Vault Secrets User (read-only) to apps. Reserve Key Vault Secrets Officer for rotation functions. Admins use PIM.
🧱
Vault Per Workload
Separate vaults per team or application domain. Limits blast radius if one app is compromised. Simplifies audit.
🚨
Things You Should Never DoNever hardcode secrets in source code. Never store secrets in App Settings as plain values (use KV references). Never log secret values (even partially). Never use the same vault across environments. Never grant Key Vault Administrator to application identities.

21Pricing Overview

Key Vault pricing is operation-based — you pay per 10,000 transactions rather than a flat monthly fee, making it extremely cost-effective for most workloads. A typical application reading secrets a few hundred times per day will cost pennies per month. HSM-backed key operations on Premium tier cost more due to the dedicated hardware, and Managed HSM has a significant hourly charge reflecting its single-tenant nature. The most important cost optimization is caching secrets in memory rather than fetching on every request.

Key Vault Standard & Premium

Operation TypeStandardPremium
Secrets operations~$0.03 per 10,000~$0.03 per 10,000
Certificate operations~$3.00 per certificate renewal~$3.00 per certificate renewal
RSA 2048 key operations~$0.03 per 10,000~$0.03 per 10,000 (SW)
RSA 2048 HSM key operationsN/A~$0.15 per 10,000
RSA 4096 HSM key operationsN/A~$0.30 per 10,000
Advanced threat protection~$0.02 per vault/month~$0.02 per vault/month

Managed HSM

SKUPrice
Standard B1 (3 HSM partitions)~$2.30/hr (~$1,660/month)
Key operations (all types)~$0.30 per 10,000
💰
Cost OptimizationCache secrets in memory for short periods (30–60 min) rather than fetching from Key Vault on every request — dramatically reduces operation count. Use Key Vault References in App Settings where possible — the platform caches and refreshes automatically. One vault per environment is sufficient for most teams.

22Quick Reference Cheat Sheet

This cheat sheet consolidates the most frequently needed Key Vault URIs, CLI commands, and service limits into a single quick-reference section. Bookmark this for daily operations — it covers everything from constructing secret URIs and App Setting references to the essential CLI commands for vault management. Keep the transaction rate limits in mind when designing high-throughput applications, and remember that secret names are limited to 127 characters with only alphanumeric characters and dashes allowed.

URIs & Reference Formats
text
# Vault base URL
https://<vault-name>.vault.azure.net/

# Secret URI (latest version)
https://<vault>.vault.azure.net/secrets/<name>

# Secret URI (specific version)
https://<vault>.vault.azure.net/secrets/<name>/<version>

# Key URI
https://<vault>.vault.azure.net/keys/<name>/<version>

# Certificate URI
https://<vault>.vault.azure.net/certificates/<name>/<version>

# App Setting Key Vault Reference (latest)
@Microsoft.KeyVault(VaultName=<vault>;SecretName=<name>)

# App Setting Key Vault Reference (specific version)
@Microsoft.KeyVault(SecretUri=https://<vault>.vault.azure.net/secrets/<name>/<version>)
Essential CLI Commands
bash
# Create vault (RBAC mode, purge protection on)
az keyvault create -g myRG -n myVault \
  --enable-rbac-authorization true \
  --enable-purge-protection true

# Set / Get / Delete a secret
az keyvault secret set --vault-name myVault -n mySecret --value "value"
az keyvault secret show --vault-name myVault -n mySecret --query value -o tsv
az keyvault secret delete --vault-name myVault -n mySecret
az keyvault secret recover --vault-name myVault -n mySecret

# List all secrets
az keyvault secret list --vault-name myVault --query "[].name" -o table

# List secret versions
az keyvault secret list-versions --vault-name myVault -n mySecret -o table

# Assign role
az role assignment create --assignee <oid> \
  --role "Key Vault Secrets User" \
  --scope /subscriptions/<sub>/.../vaults/<vault>

# Show Key Vault access policies / identity
az keyvault show --name myVault --query "properties.accessPolicies"
az webapp identity show -g myRG -n myApp --query principalId -o tsv
LimitValue
Max vaults per subscriptionUnlimited (soft limit: 500/region)
Max secrets per vaultUnlimited
Max keys per vaultUnlimited
Max secret size25 KB
Max secret name length127 characters
Max versions per secret/keyUnlimited
Max transaction rate2,000 combined secret/key/cert operations per 10 seconds
Soft delete retention7–90 days (default 90)
Managed HSM key operations/sec2,000 (RSA 2048)