Azure Key Vault
Implement secrets management with Azure Key Vault
Azure Key Vault
Azure Key Vault provides secure storage for secrets, keys, and certificates in Azure. It integrates with Azure Active Directory for authentication and supports managed identities for seamless application access.
Core Concepts
+-----------------+ +-----------------+ +-----------------+
| Application | | Azure AD | | Key Vault |
| (with MI) | --> | (AuthN) | --> | (Secrets) |
+-----------------+ +-----------------+ +-----------------+
| |
v v
+-----------------+ +-----------------+
| Managed | | Access |
| Identity | | Policies |
+-----------------+ +-----------------+
- Secrets - API keys, passwords, connection strings
- Keys - Cryptographic keys for encryption
- Certificates - SSL/TLS certificates with auto-renewal
- Managed Identity - Azure-managed service principal
Creating a Key Vault
Using Azure CLI
# Create resource group
az group create \
--name myapp-rg \
--location eastus
# Create Key Vault
az keyvault create \
--name myapp-vault \
--resource-group myapp-rg \
--location eastus \
--enable-rbac-authorization true
# Set a secret
az keyvault secret set \
--vault-name myapp-vault \
--name database-password \
--value "s3cr3t"
# Get a secret
az keyvault secret show \
--vault-name myapp-vault \
--name database-password
# List all secrets
az keyvault secret list \
--vault-name myapp-vault
# Delete a secret (soft delete)
az keyvault secret delete \
--vault-name myapp-vault \
--name database-password
# Purge a deleted secret (permanent)
az keyvault secret purge \
--vault-name myapp-vault \
--name database-password
Using Terraform
data "azurerm_client_config" "current" {}
resource "azurerm_key_vault" "main" {
name = "myapp-vault"
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 = "standard"
# Enable RBAC (recommended over access policies)
enable_rbac_authorization = true
# Soft delete settings
soft_delete_retention_days = 90
purge_protection_enabled = true
# Network rules
network_acls {
bypass = "AzureServices"
default_action = "Deny"
ip_rules = ["10.0.0.0/24"]
virtual_network_subnet_ids = [
azurerm_subnet.app.id
]
}
tags = {
Environment = "production"
}
}
# Create a secret
resource "azurerm_key_vault_secret" "database_password" {
name = "database-password"
value = random_password.db.result
key_vault_id = azurerm_key_vault.main.id
content_type = "text/plain"
tags = {
Application = "myapp"
}
}
# Grant access to an application
resource "azurerm_role_assignment" "app_secrets_reader" {
scope = azurerm_key_vault.main.id
role_definition_name = "Key Vault Secrets User"
principal_id = azurerm_user_assigned_identity.app.principal_id
}
Authentication with Managed Identity
System-Assigned Managed Identity
# Azure App Service with system-assigned identity
resource "azurerm_linux_web_app" "main" {
name = "myapp-web"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
service_plan_id = azurerm_service_plan.main.id
identity {
type = "SystemAssigned"
}
app_settings = {
AZURE_KEY_VAULT_URL = azurerm_key_vault.main.vault_uri
}
site_config {
application_stack {
python_version = "3.11"
}
}
}
# Grant the app access to secrets
resource "azurerm_role_assignment" "app_secrets" {
scope = azurerm_key_vault.main.id
role_definition_name = "Key Vault Secrets User"
principal_id = azurerm_linux_web_app.main.identity[0].principal_id
}
User-Assigned Managed Identity
resource "azurerm_user_assigned_identity" "app" {
name = "myapp-identity"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
}
# Use with AKS
resource "azurerm_kubernetes_cluster" "main" {
# ... other config ...
identity {
type = "UserAssigned"
identity_ids = [azurerm_user_assigned_identity.app.id]
}
}
Application Integration
Python SDK
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
from functools import lru_cache
class AzureKeyVault:
def __init__(self, vault_url):
credential = DefaultAzureCredential()
self.client = SecretClient(
vault_url=vault_url,
credential=credential
)
@lru_cache(maxsize=100)
def get_secret(self, name, version=None):
secret = self.client.get_secret(name, version=version)
return secret.value
def set_secret(self, name, value, content_type=None, tags=None):
return self.client.set_secret(
name,
value,
content_type=content_type,
tags=tags
)
def list_secrets(self):
return [
{'name': s.name, 'enabled': s.enabled}
for s in self.client.list_properties_of_secrets()
]
# Usage
vault = AzureKeyVault('https://myapp-vault.vault.azure.net/')
db_password = vault.get_secret('database-password')
.NET SDK
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
public class KeyVaultService
{
private readonly SecretClient _client;
public KeyVaultService(string vaultUrl)
{
_client = new SecretClient(
new Uri(vaultUrl),
new DefaultAzureCredential()
);
}
public async Task<string> GetSecretAsync(string name)
{
KeyVaultSecret secret = await _client.GetSecretAsync(name);
return secret.Value;
}
public async Task SetSecretAsync(string name, string value)
{
await _client.SetSecretAsync(name, value);
}
}
// In Program.cs with configuration
builder.Configuration.AddAzureKeyVault(
new Uri("https://myapp-vault.vault.azure.net/"),
new DefaultAzureCredential()
);
// Access as configuration
var dbPassword = builder.Configuration["database-password"];
Using with AKS (CSI Driver)
# SecretProviderClass for Azure Key Vault
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: azure-kv-secrets
spec:
provider: azure
parameters:
usePodIdentity: "false"
useVMManagedIdentity: "true"
userAssignedIdentityID: "<client-id-of-managed-identity>"
keyvaultName: "myapp-vault"
objects: |
array:
- |
objectName: database-password
objectType: secret
- |
objectName: api-key
objectType: secret
tenantId: "<tenant-id>"
secretObjects:
- secretName: app-secrets
type: Opaque
data:
- objectName: database-password
key: DB_PASSWORD
- objectName: api-key
key: API_KEY
---
apiVersion: v1
kind: Pod
metadata:
name: myapp
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: secrets
mountPath: "/mnt/secrets"
readOnly: true
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DB_PASSWORD
volumes:
- name: secrets
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: azure-kv-secrets
Certificate Management
# Create a certificate (self-signed for testing)
az keyvault certificate create \
--vault-name myapp-vault \
--name myapp-cert \
--policy "$(az keyvault certificate get-default-policy)"
# Import existing certificate
az keyvault certificate import \
--vault-name myapp-vault \
--name myapp-cert \
--file certificate.pfx \
--password "pfx-password"
# Download certificate
az keyvault certificate download \
--vault-name myapp-vault \
--name myapp-cert \
--file cert.pem
Access Policies vs RBAC
Access Policies (Legacy)
# Grant access via access policy
az keyvault set-policy \
--name myapp-vault \
--object-id "<principal-id>" \
--secret-permissions get list
RBAC (Recommended)
# Built-in roles for Key Vault:
# - Key Vault Administrator
# - Key Vault Secrets User
# - Key Vault Secrets Officer
# - Key Vault Certificates Officer
# - Key Vault Crypto Officer
# Grant secrets read access
az role assignment create \
--role "Key Vault Secrets User" \
--assignee "<principal-id>" \
--scope "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/myapp-vault"
Monitoring and Auditing
# Enable diagnostic logging
az monitor diagnostic-settings create \
--name kv-diagnostics \
--resource "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/myapp-vault" \
--logs '[{"category": "AuditEvent", "enabled": true}]' \
--workspace "<log-analytics-workspace-id>"
// Log Analytics query for secret access
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.KEYVAULT"
| where OperationName == "SecretGet"
| project TimeGenerated, CallerIPAddress, identity_claim_upn_s, id_s
| order by TimeGenerated desc
Pricing
As of 2024:
- Standard tier: $0.03 per 10,000 operations
- Premium tier: $1 per key per month (HSM-backed)
- Secrets: No storage cost, only operations
- Certificates: $3 per renewal
Best Practices
- Use RBAC over access policies - More granular control
- Enable managed identity - No credentials to manage
- Enable soft delete and purge protection - Prevent accidental loss
- Use private endpoints - Keep traffic on Azure backbone
- Enable diagnostic logging - Track all access
- Rotate secrets regularly - Use automation where possible
- Tag resources - For cost tracking and management
Part of: Secrets Management
Updated: 1/24/2025
Found an issue?