Getting Started with Azure Key Vault
Key Vault is the first thing I configure in any new Azure environment. Before the first application gets deployed, before the first pipeline runs, there needs to be somewhere to put secrets that isn't a config file or an environment variable committed to a repo. Key Vault is that place.
Creating a vault
Creating a Key Vault
Using Azure CLI
# Create resource group
az group create --name rg-keyvault --location eastus
# Create Key Vault with soft delete and purge protection
az keyvault create \
--name kv-myapp-prod \
--resource-group rg-keyvault \
--location eastus \
--enable-soft-delete true \
--enable-purge-protection true \
--retention-days 90
Using Azure Portal
- Navigate to Key Vault in Azure Portal
- Click + Create
- Select subscription and resource group
- Enter unique vault name (3-24 characters, alphanumeric and hyphens)
- Choose region and pricing tier (Standard or Premium with HSM)
- Configure Access Configuration (Azure RBAC recommended over legacy access policies)
- Enable Soft delete (90-day retention recommended)
- Enable Purge protection for production vaults
- Review and create
Storing and Retrieving Secrets
# Store a secret
az keyvault secret set \
--vault-name kv-myapp-prod \
--name db-connection-string \
--value "Server=myserver;Database=mydb;User=admin;Password=P@ssw0rd"
# Retrieve a secret
az keyvault secret show \
--vault-name kv-myapp-prod \
--name db-connection-string \
--query value -o tsv
# List all secrets
az keyvault secret list --vault-name kv-myapp-prod -o table
Managing Encryption Keys
# Create an encryption key
az keyvault key create \
--vault-name kv-myapp-prod \
--name my-encryption-key \
--protection software \
--kty RSA \
--size 2048
# For HSM-protected keys (Premium tier only)
az keyvault key create \
--vault-name kv-myapp-prod \
--name my-hsm-key \
--protection hsm \
--kty RSA-HSM \
--size 4096
Terraform Example
# Create Key Vault
resource "azurerm_key_vault" "main" {
name = "kv-${var.project}-${var.environment}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = "premium"
# Security features
soft_delete_retention_days = 90
purge_protection_enabled = true
# Network security
network_acls {
default_action = "Deny"
bypass = "AzureServices"
ip_rules = ["203.0.113.0/24"]
virtual_network_subnet_ids = [azurerm_subnet.private.id]
}
# Enable Azure RBAC for data plane
enable_rbac_authorization = true
}
# Store a secret
resource "azurerm_key_vault_secret" "db_password" {
name = "database-password"
value = random_password.db_password.result
key_vault_id = azurerm_key_vault.main.id
}
# Grant access using RBAC (recommended)
resource "azurerm_role_assignment" "secrets_user" {
scope = azurerm_key_vault.main.id
role_definition_name = "Key Vault Secrets User"
principal_id = azurerm_user_assigned_identity.app.principal_id
}
CI/CD Integration
GitHub Actions
name: Deploy with Key Vault Secrets
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Get secrets from Key Vault
uses: azure/get-keyvault-secrets@v1
with:
keyvault: "kv-myapp-prod"
secrets: 'db-connection-string, api-key, storage-account-key'
id: keyvault
- name: Use secrets in deployment
run: |
echo "Deploying with connection string"
# Secrets available as: ${{ steps.keyvault.outputs.db-connection-string }}
Azure DevOps
variables:
- group: KeyVaultSecrets # Variable group linked to Key Vault
steps:
- task: AzureKeyVault@2
inputs:
azureSubscription: 'Production'
KeyVaultName: 'kv-myapp-prod'
SecretsFilter: '*'
RunAsPreJob: true
Best Practices
Use Managed Identities for Access
I never store Key Vault credentials in application code or environment variables. I assign a Managed Identity to the application and grant Key Vault permissions to that identity.
# Assign system-assigned managed identity to VM
az vm identity assign \
--resource-group rg-app \
--name vm-app-01
# Grant Key Vault Secrets User role
az role assignment create \
--role "Key Vault Secrets User" \
--assignee <managed-identity-principal-id> \
--scope /subscriptions/{sub-id}/resourceGroups/rg-keyvault/providers/Microsoft.KeyVault/vaults/kv-myapp-prod
Enable Soft Delete and Purge Protection
- Soft Delete: Retains deleted vaults and secrets for 7-90 days (90 recommended)
- Purge Protection: Prevents permanent deletion during retention period
- I enable both for every production vault — I've never had a good reason not to
Use Azure RBAC Over Access Policies
- Modern Approach: Azure RBAC provides centralized access management
- Granular Control: Built-in roles like Key Vault Secrets User, Key Vault Crypto Officer
- Consistency: Same access control model as other Azure resources
Implement Network Security
# Restrict access to specific networks
az keyvault network-rule add \
--name kv-myapp-prod \
--resource-group rg-keyvault \
--vnet-name vnet-prod \
--subnet subnet-app
# Set default action to deny
az keyvault update \
--name kv-myapp-prod \
--resource-group rg-keyvault \
--default-action Deny
Use Private Endpoints
I always disable public access on production vaults and use Private Link:
- Secrets never traverse public internet
- Integrated with VNet DNS resolution
- Compliant with zero-trust security model
Secret Rotation Strategy
- Automate Rotation: Use Key Vault's integrated rotation for supported services
- Versioning: Key Vault maintains secret versions automatically
- Monitoring: Set up alerts for expiring secrets and certificates
Things to Avoid
I never hardcode secrets in application code, even temporarily. I also never use legacy access policies for new deployments — RBAC is the right model and I enforce it consistently.
I avoid granting overly permissive access. Applications get the Key Vault Secrets User role, not Key Vault Administrator. I never disable soft delete and purge protection in production, and I never allow unrestricted network access on production vaults.
I don't put non-sensitive configuration data in Key Vault. That belongs in App Configuration. I also keep secrets separated by environment — sharing secrets between dev and prod is a compliance issue waiting to happen.
I use separate Key Vaults per environment, grant least privilege access with RBAC, enable diagnostic logging to Log Analytics, use managed identities for service-to-service authentication, set secret expiration dates and rotation schedules, and use the Premium tier with HSM for highly sensitive keys.
Monitoring and Logging
Enable Diagnostic Settings
# Send logs to Log Analytics
az monitor diagnostic-settings create \
--name KeyVaultLogs \
--resource /subscriptions/{sub-id}/resourceGroups/rg-keyvault/providers/Microsoft.KeyVault/vaults/kv-myapp-prod \
--logs '[{"category": "AuditEvent", "enabled": true}]' \
--metrics '[{"category": "AllMetrics", "enabled": true}]' \
--workspace /subscriptions/{sub-id}/resourceGroups/rg-monitoring/providers/Microsoft.OperationalInsights/workspaces/law-prod
Key Metrics to Monitor
- Vault Availability: Target 99.9%+
- Total API Hits: Monitor for unusual spikes
- Failed Requests: Investigate authentication failures
- Latency: Service API latency (P95, P99)
Set Up Alerts
# Alert on failed authentication attempts
az monitor metrics alert create \
--name FailedKeyVaultAuth \
--resource-group rg-keyvault \
--scopes /subscriptions/{sub-id}/resourceGroups/rg-keyvault/providers/Microsoft.KeyVault/vaults/kv-myapp-prod \
--condition "count ServiceApiResult == 401 > 10" \
--window-size 5m \
--evaluation-frequency 1m
Common Use Cases
- Application Secrets: Database connection strings, API keys
- Encryption Keys: For Azure Storage, Disk Encryption, Always Encrypted
- SSL/TLS Certificates: Automated provisioning and renewal
- DevOps Pipelines: Secure credential storage for CI/CD
- Bring Your Own Key (BYOK): Import your own encryption keys
- Signing Operations: Code signing, document signing