RO EN

Azure Key Vault (3) — Granular RBAC and References

Azure Key Vault (3) — Granular RBAC and References ✨ Imagine generată cu AI
Doru Bulubașa
29 June 2026
43 views

The last part of the series about Azure Key Vault. In part 1 we integrated Key Vault with IConfiguration, and in part 2 we covered rotation and certificates. Here we look at access control and integration with App Service.


Granular access through RBAC

With RBAC enabled on Key Vault, you can control access at the individual secret level, not just at the vault level. This allows the principle of least privilege: an application that only needs Stripe--ApiKey does not have to have access to all secrets in the vault.

Built-in roles for Key Vault

Role Permissions Use case
Key Vault Administrator Full control Management operations, not for applications
Key Vault Secrets Officer CRUD secrets CI/CD pipelines that rotate secrets
Key Vault Secrets User Read secrets Applications that consume secrets
Key Vault Certificates Officer CRUD certificates Certificate management services
Key Vault Crypto Officer CRUD cryptographic keys Encryption services
Key Vault Reader Read metadata Monitoring, audit

Assigning role at individual secret level

KV_ID=$(az keyvault show \
  --name my-app-keyvault \
  --resource-group my-rg \
  --query id -o tsv)

PRINCIPAL_ID=$(az webapp identity show \
  --name my-app-service \
  --resource-group my-rg \
  --query principalId -o tsv)

# Access to ALL secrets in the vault
az role assignment create \
  --assignee $PRINCIPAL_ID \
  --role "Key Vault Secrets User" \
  --scope $KV_ID

# Access ONLY to the secret Stripe--ApiKey
az role assignment create \
  --assignee $PRINCIPAL_ID \
  --role "Key Vault Secrets User" \
  --scope "$KV_ID/secrets/Stripe--ApiKey"

The granular scope (/secrets/SecretName) means the application can read exactly the secrets it needs and nothing else. If another application needs SendGrid--ApiKey, it gets separate access, exactly to that secret.

Complete audit trail

# Enable diagnostic logs for Key Vault
az monitor diagnostic-settings create \
  --name "keyvault-audit" \
  --resource $KV_ID \
  --logs '[{"category": "AuditEvent", "enabled": true}]' \
  --workspace "/subscriptions/.../workspaces/my-workspace"
// Query in Log Analytics: who accessed which secret
AzureDiagnostics
| where ResourceType == "VAULTS"
| where OperationName == "SecretGet"
| project TimeGenerated, CallerIPAddress, identity_claim_oid_g, id_s
| order by TimeGenerated desc

With audit logging enabled, every access to a secret is recorded: which identity accessed it, which secret, at what time, from which IP. Exactly what was missing with environment variables.


Key Vault References in App Service

Key Vault References is an App Service feature that allows you to reference a secret from Key Vault directly in Application Settings, without any code changes. App Service resolves the reference at runtime and injects the real value as an environment variable.

Syntax for a Key Vault reference in Application Settings:

@Microsoft.KeyVault(SecretUri=https://my-app-keyvault.vault.azure.net/secrets/Stripe--ApiKey/)

Configuration via Azure CLI

az webapp config appsettings set \
  --name my-app-service \
  --resource-group my-rg \
  --settings \
    "Stripe__ApiKey=@Microsoft.KeyVault(SecretUri=https://my-app-keyvault.vault.azure.net/secrets/Stripe--ApiKey/)" \
    "SendGrid__ApiKey=@Microsoft.KeyVault(SecretUri=https://my-app-keyvault.vault.azure.net/secrets/SendGrid--ApiKey/)"

App Service resolves the reference automatically. Your application reads configuration["Stripe:ApiKey"] and gets the real value — not the reference. If you rotate the secret in Key Vault (and omit the version from the URI), App Service automatically picks up the new version.

References vs. AddAzureKeyVault

Criterion Key Vault References AddAzureKeyVault
Code changes Zero — transparent Provider in Program.cs
Platform support App Service, Functions, Container Apps Any .NET application
Prefix filtering No Yes, via custom manager
Reload without restart Automatic on rotation Yes, with ReloadInterval
RBAC granularity At secret level At vault or secret level

Practical recommendation: use Key Vault References for secrets injected as simple environment variables, and AddAzureKeyVault when you need prefix filtering, granular reload control, or when the application does not run on App Service.


Complete pattern: configuration without any hardcoded secret

Combining Managed Identity with Key Vault, the complete application configuration looks like this:

// appsettings.json -- zero secrets, only structure and non-sensitive values
{
  "Logging": { "LogLevel": { "Default": "Information" } },
  "KeyVaultUri": "https://my-app-keyvault.vault.azure.net/",
  "CosmosDb": {
    "Endpoint": "https://my-cosmos.documents.azure.com:443/",
    "DatabaseName": "MyAppDb"
  },
  "Storage": {
    "ServiceUri": "https://mystorage.blob.core.windows.net"
  }
  // Stripe:ApiKey and SendGrid:ApiKey come from Key Vault
}
var builder = WebApplication.CreateBuilder(args);

if (builder.Environment.IsProduction())
{
    var kvUri = builder.Configuration["KeyVaultUri"]!;
    builder.Configuration.AddAzureKeyVault(
        new Uri(kvUri),
        new DefaultAzureCredential(),
        new AzureKeyVaultConfigurationOptions
        {
            ReloadInterval = TimeSpan.FromMinutes(30)
        });
}

var credential = new DefaultAzureCredential();

// Cosmos DB and Blob Storage -- no secrets (Managed Identity)
builder.Services.AddSingleton(_ => new CosmosClient(
    builder.Configuration["CosmosDb:Endpoint"]!, credential));
builder.Services.AddSingleton(_ => new BlobServiceClient(
    new Uri(builder.Configuration["Storage:ServiceUri"]!), credential));

// Stripe and SendGrid -- secrets from Key Vault, validated at startup
builder.Services.AddOptions()
    .BindConfiguration("Stripe").ValidateDataAnnotations().ValidateOnStart();
builder.Services.AddOptions()
    .BindConfiguration("SendGrid").ValidateDataAnnotations().ValidateOnStart();

var app = builder.Build();
app.MapControllers();
await app.RunAsync();

Azure Key Vault Checklist

  • Key Vault created with RBAC enabled--enable-rbac-authorization true, not Access Policies
  • Secrets have expiration dates — and notifications configured before expiration
  • ReloadInterval configured — for rotation without application restart
  • IOptionsSnapshot or IOptionsMonitor — instead of IOptions when you need updated values
  • Granular RBAC — each application has access only to the secrets it needs
  • Audit logging enabled — all secret accesses recorded in Log Analytics
  • Key Vault References — for Application Settings in App Service, without code changes
  • Certificates with AutoRenew — automatic renewal 30 days before expiration

If you have questions or want to discuss how to structure your Key Vault for your project, write to me at contact@ludoprogramming.com.