Azure Key Vault provider (packs:secrets-azure)
packs:secrets-azure
Azure Key Vault is a SecretsProvider kind:
azure-key-vault. It is additive on the open kind set — the same
connection object, CRUD verbs, test verb, health recording, audit events
and admin RBAC gate as the vault kinds, with Azure-shaped configuration.
Everything on this page requires the packs:secrets-azure licence
entitlement (see entitlements): creating an
azure-key-vault provider, or enabling a disabled one, refuses with the
typed entitlement error naming the pack. Already-enabled providers keep
resolving on licence lapse — only the create/enable writes gate (the
polite-lapse posture), and disabling stays open for lapse cleanup.
Configuration
| Field | Meaning for this kind |
|---|---|
kind | azure-key-vault |
address | Required. The vault URL (https://<name>.vault.azure.net). |
tenant_id | Required. The Microsoft Entra ID tenant the client credentials authenticate against. |
auth_method | client-secret (the kind's only method, and its default): an Entra ID app registration's client credentials. |
username | The application (client) id — the non-secret identity half, exactly as userpass auth carries its username. |
credential | The client secret — encrypted at rest, write-only (reads serve credential_set), rotated by the update verb with an audit event that never carries the value. |
mount_path | Optional secret-name prefix: every OpsChain path is stored under it, scoping the provider to one corner of the vault's namespace. |
ssl_verify / ssl_ca_cert | The same TLS options the vault kinds carry — for proxied endpoints. |
cipher_stacking / password_length | As for every provider: whether /api/secrets/store cipher-stacks written values, and the generated-secret length. |
kv_version, role_id and region belong to the other kinds and are
refused on this one.
The adapter speaks the Key Vault secrets REST surface (api-version 7.4)
and the OAuth2 client-credentials grant directly — the server carries no
Azure SDK, exactly as the vault kinds speak their HTTP API with no vendor
client. Tokens are requested from the public-cloud authority
(login.microsoftonline.com) with the public Key Vault scope; the
signing identity is always a client-credential pair — ambient mechanisms
(managed identity, workload identity) are not consulted, so the provider
behaves identically wherever the server runs. Sovereign-cloud authorities
are not configurable on the connection object yet; that is the kind's
recorded configuration gap.
How secrets map
One Key Vault secret holds one OpsChain secret node: the secret's value
is a JSON object of key/value pairs, and a secret-vault://<path>/<key>
reference reads key <key> from it. A secret whose value is not a JSON
object reads as the single key value, so a portal-created plain secret
is reachable as secret-vault://<path>/value.
Key Vault secret names allow only letters, digits and dashes, so the
adapter encodes the path separator as a double dash: OpsChain path
app/db is the secret named app--db (the mount_path prefix joins the
same way). Path segments are therefore restricted to [0-9A-Za-z-] on
this kind — a segment the name alphabet cannot carry (an underscore, a
dot) refuses loudly naming the segment. Listings (the vault browser, the
audit/migrate walkers) decode the names back into the directory view.
The test verb
TestSecretsProvider (CLI: opschain admin secrets providers test <code>) acquires a client-credentials token and performs a single-page
secrets listing — proving the tenant, the credential pair, the vault URL
and the data-plane list permission. The typed outcome is recorded as the
connection's health (last_test_*), and the failure reason never carries
credential material. The app registration needs Key Vault data-plane
permissions (RBAC: Key Vault Secrets User for reads and the browser,
Key Vault Secrets Officer where /api/secrets/store writes; or the
equivalent access-policy grants: get/list, plus set for writes).
Resolution lanes
- Server-side resolution —
secret-vault://references in properties, the/api/secretsread/write seam and the vault browser all resolve through the provider object, routed by the declared scope bindings exactly as the vault kinds are. Resolved values join the log mask set. - Interactive reads use the USER's own credential (the
posture): each user stores their own Entra ID client credentials
in preferences (
usernamethe client id, the credential the client secret; auth methodclient-secret), and Azure's own authorisation for that principal governs what the reveal/browser shows them. No stored personal credential → the typed configure-your-credential refusal, never a silent fall-back to the platform credential. Note the shape honestly: Azure has no per-human client-credential analogue of a vault token, so "per-user" here means one app registration (service principal) per user — organisations that cannot provision those should treat the interactive lane as unavailable on this kind and rely on the vault's own portal/CLI for human reads. - No runner environment, no step lease. The
OPSCHAIN_VAULT_*projection and the response-wrapped step lease speak the vault wire protocol, which opschain-core inside the runner expects — an Azure-backed provider projects neither. Step properties still resolve (server-side, before dispatch, masked in logs); what an Azure-backed estate does not get is in-runnersecret-vault://resolution by MintPress action code readingOPSCHAIN_VAULT_*directly (only applicable to existing MintPress customers and those using the MintPress SDK or thepacks:mintpressfeature). That lane needs a coordinated opschain-core release and is recorded as the kind's follow-on gap.
Pack gating end to end
CreateSecretsProviderwithkind: azure-key-vault→permission_denied,ERROR_REASON_FEATURE_ENTITLEMENT_REQUIRED, metadatafeature: packs:secrets-azure— unless the licence grants the pack.UpdateSecretsProviderenabling a disabled Azure provider → the same typed refusal.- The
secrets:multi-vaultgate composes independently: a second enabled provider still needs it, whatever the kinds.