Azure Key Vault Provider
Summary
Use Azure Key Vault as a provider behind the Secrets Platform (VaultService). Callers use canonical URIs; the platform enforces policy via PDP, adds tracing/metrics, and publishes audit events. This page is production‑ready guidance for admins and developers.
- Provider:
azure - Purpose: Retrieve and manage app secrets stored in Azure Key Vault (KV)
- Highlights:
- Canonical URI parsing (fragments, versions)
- Retries, circuit‑breaker, and timeouts
- OpenTelemetry traces and Prometheus metrics
- Kafka audit events on delete
- Metadata helpers (tags) and version discovery
Architecture
Canonical URI
- Scheme:
azure:// - Mount:
vault - Path: free‑form name (supports slashes for logical grouping)
- Fragment: optional key selector for JSON payloads
- Query: optional version string (Azure version ID)
Examples:
- Full secret dict:
azure://vault/app/db - Single field:
azure://vault/app/db#username - Versioned read:
azure://vault/app/db#username?version=v1
Behavior:
- JSON values return a parsed dict; with a fragment, returns
{fragment: value} - Scalar values return
{value: "<scalar>"}; with a fragment, returns{fragment: None} - Versions are strings and map to Azure version IDs
API surface (normalized)
get_credentials(ref: str) -> dict- Accepts canonical URIs or raw names; applies retries, circuit‑breaker, and timeouts
create_credentials(name: str, data: dict) -> dict- Stores JSON‑serialized value; creates a new version
update_credentials(name: str, data: dict) -> dict- Requires existence; creates a new version
delete_credentials(name: str) -> bool- Soft‑deletes (per KV settings) and emits Kafka audit event
list_credentials() -> list[str]- All secret names
list_keys(path: str) -> list[str]- Prefix filter using slash‑separated names
read_secret_metadata(name: str) -> dict- Returns current version, versions (timestamps/enabled), and
custom_metadata(tags)
- Returns current version, versions (timestamps/enabled), and
update_custom_metadata(name: str, custom: dict, merge=True) -> None- Maps to KV tags (merge or replace)
get_latest_version(name: str) -> Optional[str>
Sequence: get_credentials
Security & authorization
- Enforce PDP checks in front of the provider (
ENABLE_AUTHORIZATION=true) - Require auth on
/api/secrets/*(SECRETS_API_REQUIRE_AUTH=true) - Scope enforcement recommended (
SECRETS_ENFORCE_SCOPES=true) - Tenant guard:
TENANT_ID,TENANT_ALLOWED_MOUNTSrestrict accessible mounts/paths - Sender binding: prefer DPoP or mTLS for grant usage
Observability & audit
- Tracing: spans per operation (e.g.,
azure.get_secret,azure.create_secret,azure.update_secret,azure.delete_secret,azure.list_secrets,azure.read_secret_metadata,azure.update_custom_metadata) with attributessecret.name,secret.version(orlatest) - Metrics: emitted via OTEL exporters → Prometheus
- Auditing: on delete, emits
EventType.SECRET_VERSION_DELETEDviaCrudKafkaProducercredential_id:azure://vault/<name>resource_ref: HMAC‑masked whenTENANT_SALTorSECRET_TENANT_SALTis set
Configuration
Required:
vault_url:https://<vault-name>.vault.azure.net/
Optional:
managed_identity_client_id: prefer Managed Identity when presenttimeout_s(default 30),max_retries(default 3)cb_failure_threshold(default 5),cb_reset_timeout_s(default 30)
Configuration should be supplied via service env/compose; avoid per‑app .env files.
Error semantics (normalized)
VaultSecretNotFoundError: secret or version not foundVaultAuthenticationError: permission/auth failures (future mapping)VaultTimeoutError: circuit breaker open or timeoutVaultOperationError: generic provider errors
Quick usage
Fetch a JSON field:
creds = await strategy.get_credentials("azure://vault/app/db#username")
Update tags/metadata:
await strategy.update_custom_metadata("app/db", {"owner": "payments"}, merge=True)
Delete and emit audit:
await strategy.delete_credentials("app/db")
Notes & limitations
- Azure Key Vault does not support response wrapping; helpers in this provider are dev‑only stubs for parity with other providers
- Hierarchical listing is emulated via prefix matching on slash‑separated names
Related
- Platform overview:
../index.md - Authorization model (PDP):
../11-authorization-model-authzen.md - Observability:
../06-observability.md - Admin how‑to:
../07-admin-howto.md - API reference:
../reference/secrets-api.mdI'll embed two Mermaid diagrams: an architecture overview and a get_credentials sequence. Then I'll provide the detailed documentation text. Ran tool Ran tool
Azure Key Vault Strategy
This document describes how to use the Azure Key Vault strategy in VaultService, including canonical URI syntax, supported operations, error semantics, observability, and audit behavior.
Overview
- Provider: azure
- Purpose: Retrieve and manage application secrets stored in Azure Key Vault
- Key features:
- Canonical URI parsing with fragments and version selection
- Retries, circuit breaker, and request timeouts
- OpenTelemetry tracing
- Kafka audit events on delete
- Metadata helpers (tags) and version discovery
Architecture
[Mermaid] Architecture overview:
Canonical URI
- Scheme: azure://
- Mount: vault
- Path: free-form name (supports slashes for logical grouping)
- Fragment: optional key selector for JSON payloads
- Query: optional version string
Examples:
- Full secret dict:
azure://vault/app/db - Single field:
azure://vault/app/db#username - Versioned read:
azure://vault/app/db#username?version=v1
Behavior:
- If secret value is JSON, returns parsed dict. With a fragment, returns
{fragment: value}. - If secret value is a scalar and a fragment is requested, returns
{fragment: None}. - Versions are treated as strings (Azure version IDs).
API Surface
- get_credentials(ref: str) -> dict
- Accepts canonical URIs or raw secret names
- Uses retries, circuit breaker, timeouts
- create_credentials(name: str, data: dict) -> dict
- Stores JSON-serialized value; creates a new version
- update_credentials(name: str, data: dict) -> dict
- Validates existence; creates a new version
- delete_credentials(name: str) -> bool
- Soft-deletes the secret (per Key Vault settings) and emits a Kafka audit event
- list_credentials() -> list[str]
- Returns all secret names
- list_keys(path: str) -> list[str]
- Prefix filter using slash-separated names
- read_secret_metadata(name: str) -> dict
- Returns current version, versions list (with timestamps and enablement), and custom_metadata (tags)
- update_custom_metadata(name: str, custom: dict, merge=True) -> None
- Maps to Key Vault tags (merge or replace)
- get_latest_version(name: str) -> Optional[str]
- get_credentials_wrapped(ref: str, wrap_ttl: str) -> dict
- Dev-friendly stub returning a wrap token
- unwrap_token(wrap_token: str) -> dict
- Dev-friendly stub with a success payload
Error Semantics
- VaultSecretNotFoundError: secret or version not found
- VaultAuthenticationError: permission/auth failures (future mapping)
- VaultTimeoutError: circuit breaker open or timeout
- VaultOperationError: generic provider errors
All errors are normalized for consistent handling across providers.
Observability
- Tracing: Spans per operation with attributes:
- azure.get_secret, azure.create_secret, azure.update_secret, azure.delete_secret, azure.list_secrets, azure.read_secret_metadata, azure.update_custom_metadata
- Attributes: secret.name, secret.version (or “latest”)
- Metrics: leverage OpenTelemetry exporters configured globally
Auditing
- On delete: emits EventType.SECRET_VERSION_DELETED to Kafka via
CrudKafkaProducer- credential_id:
azure://vault/<name> - resource_ref: hashed reference when TENANT_SALT or SECRET_TENANT_SALT is set
- credential_id:
Sequence for get_credentials
[Mermaid] Sequence diagram:
Configuration
- Required:
- vault_url:
https://<vault-name>.vault.azure.net/
- vault_url:
- Optional:
- managed_identity_client_id: use Managed Identity when present
- timeout_s (default 30), max_retries (default 3)
- cb_failure_threshold (default 5), cb_reset_timeout_s (default 30)
Configuration is provided via service environment/compose. Avoid per-app .env files.
Return Shapes
- JSON secrets: returns dict; with fragment →
{fragment: value} - Scalar secrets: returns
{value: "<scalar>"}; with fragment →{fragment: None}
Notes and Limitations
- Response wrapping is not natively supported by Azure Key Vault; provided helpers are dev stubs for caller parity.
- Hierarchical listing is emulated via prefix matching on slash-separated secret names.
Quick Usage
- Fetch JSON field:
creds = await strategy.get_credentials("azure://vault/app/db#username")
- Update metadata/tags:
await strategy.update_custom_metadata("app/db", {"owner": "payments"}, merge=True)
- Delete and audit:
await strategy.delete_credentials("app/db")