Azure Key Vault: Securing Your Application Secrets
- Azure subscription (free trial available)
- Azure CLI version 2.40 or later
- Basic understanding of Azure Active Directory (now Microsoft Entra ID)
- An application or service that needs to store secrets (we'll use a simple example)
- Contributor or Key Vault Contributor role on production
Overview
In this guide, you'll create an Azure Key Vault, store various types of secrets, implement secure access patterns using managed identities, and integrate Key Vault with a Node.js application. We'll cover:

- Creating and configuring Azure Key Vault
- Storing secrets, keys, and certificates
- Implementing secure access with managed identities
- Integrating Key Vault with applications
- Monitoring and auditing secret access
- Production-ready security patterns
Figure: Decision matrix mapping secret types (API keys, connection strings, encryption keys, certificates) to retrieval patterns (per-request, startup prefetch, scheduled rotation) and enforcement controls (RBAC, purge protection, private endpoint). Embedded early to contextualize architectural trade-offs before deep implementation.
Architecture Overview
$Expiry = [DateTime]$Secret.attributes.expires
if ((($Expiry - (Get-Date)).TotalDays) -lt $ThresholdDays) {
Write-Host "Rotating $SecretName"
$NewValue = (New-Guid).Guid.Replace('-', '').Substring(0,32)
az keyvault secret set --vault-name $VaultName --name $SecretName --value $NewValue --expires $((Get-Date).AddMonths(12).ToString('s')+'Z') | Out-Null
}

Key rotation via Bicep module (using verified AVM module):
```bicep
module kv 'br/public:avm/res/key-vault/vault:<version>' = {
name: 'kvEnterprise'
params: {
```yaml
name: 'kv-enterprise-prod'
enablePurgeProtection: true
enableRbacAuthorization: true
keys: [
{
name: 'encKey'
rotationPolicy: {
attributes: {
expiryTime: 'P2Y'
}
lifetimeActions: [
{
action: { type: 'rotate' }
trigger: { timeBeforeExpiry: 'P30D' }
}
{
action: { type: 'notify' }
trigger: { timeBeforeExpiry: 'P60D' }
}
]
}
}
]```
}
}
Performance & Caching Strategies
| Concern | Symptom | Strategy | Notes |
|---|---|---|---|
| Latency | Repeated secret fetch calls | In-memory cache (TTL) | Avoid long TTL > rotation cycle |
| Startup Delay | Cold start needs many secrets | Batch fetch asynchronously | Parallelize when safe |
| Throttling | 429 responses from vault | Exponential backoff + jitter | Monitor metrics for sustained 429s |
| Regional Resilience | Higher latency cross-region | Prefer local vault per region | Sync secrets via pipeline, not app |
| Large Secret Payloads | Increased transfer time | Keep secrets small (config → App Config) | Separate config vs sensitive values |

Example Node.js cache wrapper:
class SecretCache {
constructor(client, ttlMs = 300000) { // 5 min default
```text
this.client = client; this.ttlMs = ttlMs; this.store = new Map();```
}
_Figure: Application runtime flow—managed identity authentication, first secret fetch populating cache, subsequent requests served locally until TTL expires, with rotation invalidation path. Reinforces performance & caching strategies in this section._
async get(name) {
```javascript
const entry = this.store.get(name);
const now = Date.now();
if (entry && (now - entry.ts) < this.ttlMs) return entry.value;
const secret = await this.client.getSecret(name);
this.store.set(name, { value: secret.value, ts: now });
return secret.value;```
}
}
Observability & Auditing Enhancements
Kusto queries:

AzureDiagnostics
| where ResourceType == 'VAULTS'
| summarize count() by OperationName
AzureDiagnostics
| where OperationName == 'SecretGet'
| summarize requests = count() by identity_claim_upn_s
| order by requests desc
Alert Strategy:
- Spike in SecretGet operations for single identity.
- Unexpected operations (e.g., purge attempts) outside maintenance window.
- Approaching secret expiry (< 14 days) without rotation event.
Resilience & Disaster Recovery
| Risk | Mitigation | Detail |
|---|---|---|
| Accidental deletion | Soft delete + purge protection | Mandatory for production |
| Regional outage | Multiple vaults (active-active) | Deploy per region + pipeline sync |
| Compromised secret | Immediate version rollback | Re-point apps to previous version |
| Access escalation | Principle of least privilege reviews | Quarterly RBAC audit |
| Network isolation failure | Private endpoint + deny public | Policy enforcement |

Rotation rollback pattern:
- Detect failure (app telemetry errors after deploy).
- Fetch prior secret version ID.
- Promote previous version by updating reference (env variable / config store).
- Trigger targeted app restart.
Troubleshooting Matrix (Extended)
| Symptom | Root Cause | Diagnosis | Resolution |
|---|---|---|---|
| High latency (p95 > 200ms) | Excessive per-request secret fetch | App Insights dependency traces | Implement TTL caching layer |
| Frequent 401 on secret reads | Managed identity token failure | Identity logs / AzureDiagnostics | Reassign role; check identity state |
| Rotation script fails silently | Missing Function permissions | Function app role assignments | Grant Secrets Officer to function identity |
| Unauthorized purge attempt | Malicious / misconfigured principal | AuditEvent logs | Review RBAC; disable access; alert SOC |
| Expired certificate not auto-renewed | CA integration misconfigured | Certificate status in portal | Re-import with correct policy |
| Inconsistent secrets across regions | Manual sync oversight | Compare versions via CLI | Automate sync in pipeline |

Image References
- Key Vault RBAC vs Access Policies
- Key Rotation Concepts
- Secret Expiration & Alerts
- Event Grid Integration
- Azure Policy Samples for Key Vault

Step-by-Step Guide
Step 1: Create an Azure Key Vault

First, set up a resource group and Key Vault:
## Set variables for reusability
$RESOURCE_GROUP="rg-keyvault-demo"
$LOCATION="eastus"
$KEY_VAULT_NAME="kv-secure-app-$((Get-Random -Maximum 9999))"

## Create resource group
az group create --name $RESOURCE_GROUP --location $LOCATION

## Create Key Vault with RBAC authorization (recommended over access policies)
az keyvault create `
--name $KEY_VAULT_NAME `
--resource-group $RESOURCE_GROUP `
--location $LOCATION `
--enable-rbac-authorization true `
--enabled-for-deployment false `
--enabled-for-disk-encryption false `
--enabled-for-template-deployment false

## Get your user object ID
$USER_OBJECT_ID = az ad signed-in-user show --query id -o tsv

## Grant yourself Key Vault Secrets Officer role
az role assignment create `
--role "Key Vault Secrets Officer" `
--assignee $USER_OBJECT_ID `
--scope "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.KeyVault/vaults/$KEY_VAULT_NAME"

Expected output:
{ "name": "rg-myapp-prod", "location": "eastus2", "properties": { "provisioningState": "Succeeded" } }
Important Configuration Choices:
- RBAC Authorization: Modern, recommended approach for access control
- Deployment Flags: Disabled for security (enable only if needed)
- Soft Delete: Enabled by default (retains deleted secrets for 90 days)
- Purge Protection: Consider enabling for production to prevent permanent deletion
Step 2: Store Secrets in Key Vault
Add different types of secrets:

## Store a database connection string
az keyvault secret set `
--vault-name $KEY_VAULT_NAME `
--name "DatabaseConnectionString" `
--value "Server=myserver.database.windows.net;Database=mydb;User Id=admin;Password=SecureP@ssw0rd!"

## Store an API key with expiration
$EXPIRY_DATE = (Get-Date).AddYears(1).ToString("yyyy-MM-ddTHH:mm:ssZ")
az keyvault secret set `
--vault-name $KEY_VAULT_NAME `
--name "ThirdPartyApiKey" `
--value "sk_live_51H..." `
--expires $EXPIRY_DATE

## Store a certificate (upload from file)
az keyvault certificate import `
--vault-name $KEY_VAULT_NAME `
--name "AppServiceCertificate" `
--file "path/to/certificate.pfx" `
--password "cert-password"

## Create a cryptographic key for encryption
az keyvault key create `
--vault-name $KEY_VAULT_NAME `
--name "DataEncryptionKey" `
--kty RSA `
--size 2048 `
--ops encrypt decrypt

Expected output:
{ "id": "https://kv-myapp.vault.azure.net/secrets/MySecret/abc123", "attributes": { "enabled": true } }
Retrieve a secret to verify:
## Get secret value
az keyvault secret show `
--vault-name $KEY_VAULT_NAME `
--name "DatabaseConnectionString" `
--query "value" -o tsv

Step 3: Implement Managed Identity Access
Create a managed identity for secure, password-less authentication:

## Create a user-assigned managed identity
$IDENTITY_NAME="id-secure-app"
az identity create `
--name $IDENTITY_NAME `
--resource-group $RESOURCE_GROUP `
--location $LOCATION

## Get the identity's principal ID
$IDENTITY_PRINCIPAL_ID = az identity show `
--name $IDENTITY_NAME `
--resource-group $RESOURCE_GROUP `
--query principalId -o tsv

## Grant the identity read access to secrets
az role assignment create `
--role "Key Vault Secrets User" `
--assignee $IDENTITY_PRINCIPAL_ID `
--scope "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.KeyVault/vaults/$KEY_VAULT_NAME"

Expected output:
{ "roleDefinitionName": "Key Vault Secrets Officer", "scope": "/subscriptions/xxxxxxxx/resourceGroups/rg-myapp" }
For Azure services (App Service, Functions, VMs), enable system-assigned identity:
## Example: Enable managed identity on an App Service
az webapp identity assign `
--name "my-app-service" `
--resource-group $RESOURCE_GROUP

## Then grant it Key Vault access using the same role assignment pattern

Step 4: Integrate Key Vault with Your Application
Here's how to access Key Vault from a Node.js application using managed identity:

Install dependencies:
npm install @azure/keyvault-secrets @azure/identity
Expected output:
added 245 packages in 8s
found 0 vulnerabilities
Application code:
const { SecretClient } = require("@azure/keyvault-secrets");
const { DefaultAzureCredential } = require("@azure/identity");
async function getSecret(secretName) {
```javascript
try {
// DefaultAzureCredential automatically uses managed identity in Azure
// and falls back to Azure CLI credentials locally
const credential = new DefaultAzureCredential();
const vaultUrl = `https://${process.env.KEY_VAULT_NAME}.vault.azure.net`;
const client = new SecretClient(vaultUrl, credential);
// Retrieve the secret
const secret = await client.getSecret(secretName);
console.log(`Retrieved secret: ${secretName}`);
return secret.value;
} catch (error) {
console.error(`Error retrieving secret: ${error.message}`);
throw error;
}```
}
async function main() {
```javascript
// Example: Get database connection string
const dbConnectionString = await getSecret("DatabaseConnectionString");
// Use the secret (never log the actual value!)
console.log("Successfully retrieved database connection");
// Example: Get API key
const apiKey = await getSecret("ThirdPartyApiKey");
// Use secrets in your application
// initializeDatabase(dbConnectionString);
// configureApiClient(apiKey);```
}
main().catch(error => {
```text
console.error("Application error:", error);
process.exit(1);```
});
Set environment variable:
## Locally (for development)
$env:KEY_VAULT_NAME="kv-secure-app-1234"

## In Azure App Service (production)
az webapp config appsettings set `
--name "my-app-service" `
--resource-group $RESOURCE_GROUP `
--settings KEY_VAULT_NAME="kv-secure-app-1234"

Step 5: Enable Monitoring and Auditing
Configure diagnostic settings to track secret access:

## Create a Log Analytics workspace
az monitor log-analytics workspace create `
--workspace-name "law-keyvault-audit" `
--resource-group $RESOURCE_GROUP `
--location $LOCATION

## Get workspace ID
$WORKSPACE_ID = az monitor log-analytics workspace show `
--workspace-name "law-keyvault-audit" `
--resource-group $RESOURCE_GROUP `
--query id -o tsv

## Enable diagnostic logging
az monitor diagnostic-settings create `
--name "KeyVaultAuditLogs" `
--resource "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.KeyVault/vaults/$KEY_VAULT_NAME" `
--workspace $WORKSPACE_ID `
--logs '[{"category": "AuditEvent", "enabled": true}]' `
--metrics '[{"category": "AllMetrics", "enabled": true}]'

Expected output:
{ "value": [{ "name": { "value": "Requests" }, "timeseries": [{ "data": [{ "total": 1234 }] }] }] }
Query audit logs:
// Run in Log Analytics workspace
AzureDiagnostics
| where ResourceType == "VAULTS"
| where OperationName == "SecretGet"
| project TimeGenerated, CallerIPAddress, identity_claim_upn_s, requestUri_s
| order by TimeGenerated desc
Step 6: Implement Secret Rotation
Automate secret rotation using Azure Key Vault rotation policy:

## Set rotation policy for a secret (90-day rotation)
az keyvault secret set-attributes `
--vault-name $KEY_VAULT_NAME `
--name "ThirdPartyApiKey" `
--expires $EXPIRY_DATE

## For automated rotation, use Azure Functions or Logic Apps

## Trigger rotation based on expiry notifications
Expected output:
{ "id": "https://kv-myapp.vault.azure.net/secrets/MySecret/abc123", "attributes": { "enabled": true } }

Best practice pattern:
- Store both current and previous secret versions
- Update applications to use new secrets
- Deprecate old secrets after grace period
- Monitor for applications still using old versions
Best Practices
- Use Managed Identities: Never store Key Vault credentials in code or configuration files. Always use managed identities for Azure-hosted applications and service principals with minimal permissions for external services.

Implement Least Privilege: Grant only the specific permissions needed (e.g., "Key Vault Secrets User" for read-only access). Avoid broad roles like "Key Vault Administrator" for application identities.
Enable Soft Delete and Purge Protection: Protect against accidental or malicious deletion. Soft delete retains secrets for 90 days; purge protection prevents permanent deletion during that period.
Separate Key Vaults by Environment: Use different Key Vaults for dev, test, and production. This isolation prevents cross-environment secret leaks and simplifies access management.
Implement Secret Versioning: Key Vault maintains secret versions automatically. Reference specific versions in production; use "latest" cautiously and only after thorough testing.
Monitor and Alert: Configure alerts for unusual access patterns, failed authentication attempts, or secret expirations. Use Azure Monitor and Log Analytics for comprehensive auditing.
Rotate Secrets Regularly: Establish rotation policies (30-90 days for highly sensitive secrets). Automate rotation where possible using Azure Functions or Key Vault rotation features.
Use Network Restrictions: Configure firewall rules and private endpoints to limit Key Vault access to specific networks. Disable public access for production Key Vaults when possible.
Common Issues & Troubleshooting
Issue: The user, group or application does not have secrets get permission
Solution: Verify RBAC role assignments using az role assignment list --scope <key-vault-resource-id>. Ensure the identity has "Key Vault Secrets User" or higher role. Role propagation can take 5-10 minutes after assignment.

Issue: DefaultAzureCredential failed to retrieve a token when running locally
Solution: Ensure you're logged into Azure CLI (az login). For managed identity to work locally, the CLI credentials are used as a fallback. In production, verify the managed identity is enabled and has proper role assignments.
Issue: Secrets not accessible from Azure App Service
Solution: 1) Verify managed identity is enabled on the App Service. 2) Check the identity has appropriate Key Vault role assignments. 3) Ensure firewall rules allow App Service outbound IP addresses if network restrictions are configured. 4) Verify the Key Vault URL is correct in application settings.
Issue: Certificate import fails with parsing error
Solution: Ensure the certificate is in PFX/PKCS12 format with a password. PEM files need conversion first. Verify the certificate chain is complete and the password is correct.
Architecture Decision and Tradeoffs
When designing cloud infrastructure solutions with Azure, consider these key architectural trade-offs:
| Approach | Best For | Tradeoff |
|---|---|---|
| Managed / platform service | Rapid delivery, reduced ops burden | Less customisation, potential vendor lock-in |
| Custom / self-hosted | Full control, advanced tuning | Higher operational overhead and cost |
Recommendation: Start with the managed approach for most workloads and move to custom only when specific requirements demand it.
Validation and Versioning
- Last validated: April 2026
- Validate examples against your tenant, region, and SKU constraints before production rollout.
- Keep module, CLI, and SDK versions pinned in automation pipelines and review quarterly.
Security and Governance Considerations
- Apply least-privilege access using RBAC roles and just-in-time elevation for admin tasks.
- Store secrets in managed secret stores and avoid embedding credentials in scripts or source files.
- Enable audit logging, data protection policies, and periodic access reviews for regulated workloads.
Cost and Performance Notes
- Define budgets and alerts, then monitor usage and cost trends continuously after go-live.
- Baseline performance with synthetic and real-user checks before and after major changes.
- Scale resources with measured thresholds and revisit sizing after usage pattern changes.
Official Microsoft References
- https://learn.microsoft.com/azure/
- https://learn.microsoft.com/azure/architecture/
- https://learn.microsoft.com/azure/well-architected/
Public Examples from Official Sources
- These examples are sourced from official public Microsoft documentation and sample repositories.
- Documentation examples: https://learn.microsoft.com/azure/architecture/
- Sample repositories: https://github.com/Azure-Samples
- Prefer adapting these examples to your tenant, subscriptions, and governance requirements before production use.
Key Takeaways
- ✅ Azure Key Vault centralizes secret management, eliminating hardcoded credentials and secret sprawl
- ✅ Managed identities provide secure, password-less authentication between Azure services
- ✅ RBAC-based access control offers fine-grained permissions aligned with zero-trust principles
- ✅ Comprehensive audit logging tracks all secret access for compliance and security monitoring
- ✅ Built-in features like soft delete, versioning, and rotation policies protect against common security pitfalls

Next Steps
- Integrate Key Vault with Azure App Configuration for centralized configuration management
- Explore Key Vault Managed HSM for FIPS 140-2 Level 3 compliance requirements
- Implement Azure Policy to enforce Key Vault best practices across subscriptions
- Set up Azure Key Vault rotation with Azure Functions
- Learn about Customer-Managed Keys for encrypting Azure services
Additional Resources
- Microsoft Learn - Azure Key Vault
- Azure Key Vault Documentation
- Key Vault Security Recommendations
- Azure SDK for JavaScript - Key Vault
- Zero Trust Security Model
How are you managing secrets in your applications? Have you implemented Key Vault? Share your security journey and questions below!
```