Secrets in Azure Key Vault
IdentityMesh ships with two secret-store backends:
| Provider | When to use |
|---|---|
| DPAPI (default) | On-prem deployments, single host, no cloud dependency. See secrets-and-dpapi.md. |
| Azure Key Vault | Cloud or hybrid deployments where secrets must travel across hosts (HA failover, DR rebuild) or are governed by a central vault policy. |
This document covers the Key Vault provider.
Why Key Vault
The DPAPI provider encrypts each secret blob with the host’s
LocalMachine key. That key never leaves the host, so the blobs
are not portable: a re-image, a DR rebuild, or a cluster failover
to a second host all require re-provisioning every connector
secret by hand.
Key Vault keeps the ciphertext in the vault, not on the host. Any authorised IdentityMesh process — original host, replacement host, DR-region host — can read the same secret using the same vault URI and an authenticated managed identity.
Enabling Key Vault
In the sync engine’s appsettings.json:
{
"Secrets": {
"Provider": "AzureKeyVault",
"KeyVault": {
"VaultUri": "https://contoso-im.vault.azure.net/"
}
}
}
When Secrets:Provider is unset or any value other than
AzureKeyVault, IdentityMesh uses the DPAPI store and Key Vault
configuration is ignored. So flipping the switch is the only
deployment change required.
Authentication
The engine uses DefaultAzureCredential from the Azure SDK to
acquire a token for the vault. That credential walks a chain of
sources in this order:
- Environment variables (
AZURE_CLIENT_ID,AZURE_TENANT_ID,AZURE_CLIENT_SECRETfor service principal; orAZURE_FEDERATED_TOKEN_FILEfor workload identity). - Managed identity assigned to the host (system-assigned VM
identity, or a user-assigned identity referenced by
AZURE_CLIENT_ID). This is the recommended setup in production. - Azure CLI session (
az login) — useful for development and first-touch provisioning, not for production.
Whichever identity the host ends up using needs the Key Vault Secrets User role on the vault (and Key Vault Secrets Officer if it will write secrets, e.g. via the Admin UI).
Secret naming
Callers pass arbitrary refs (e.g. secret://ad/svc/password) but
Key Vault names allow only [A-Za-z0-9-] and must start with a
letter. IdentityMesh maps each ref to a stable Key Vault name:
secretRef → "im-" + lowercase-hex(SHA-256(secretRef))[:32]
For example, secret://ad/svc/password becomes
im-3f2a1b... — predictable, collision-resistant, and never
truncates a long ref. The engine logs the mapping at INFO when it
writes a secret, so you can correlate refs to vault entries when
needed.
The mapping is one-way (you can’t recover the original ref from the vault name). Keep the ref strings in your connector configs as the canonical identifier; the vault name is an implementation detail.
Provisioning a secret
Secrets are still provisioned via the Admin UI / Admin API exactly as they are with the DPAPI store. The engine handles the encoding-and-storage step transparently:
- Operator enters the cleartext in the Admin UI.
- The Admin API calls
ISecretStore.SetAsync(ref, bytes). - With
Secrets:Provider = AzureKeyVault, that resolves to the Key Vault store, which Base64-encodes the bytes and callsSetSecreton the configured vault. - The connector config keeps only the ref. Rotation is ref-stable: re-setting the same ref creates a new vault version, the engine always reads the current version.
What if the vault is unreachable
The engine attempts to read the secret on each connector run. If Key Vault is unreachable (network outage, identity revoked, vault deleted) the connector run fails with the underlying SDK error surfaced. The watermark is not advanced, so the next run re-tries from the last good checkpoint.
For longer outages, vault availability — not IdentityMesh — is the SLA you should monitor. Use the Azure status page and per-vault metrics to decide whether the engine is the right place to alert.
Migration from DPAPI to Key Vault
There is no automated migration today. The procedure is:
- Provision the vault and grant the engine’s identity
Key Vault Secrets Officer. - Take the engine offline.
- For each secret in the existing DPAPI store, re-enter the
cleartext via the Admin UI. With
Secrets:Provideralready flipped toAzureKeyVaultin the engine’s config, the new value lands in the vault under its mapped name. - Restart the engine.
The DPAPI rows in IM_Secrets remain in the database after
migration. They are unused but harmless; an explicit cleanup pass
can purge them once you’re confident the vault store is the
source of truth.
Related
secrets-and-dpapi.md— the default on-prem provider.secret-rotation.md— rotation runbook (ref-stable rotation works the same way for both providers).deployment-architecture.md— where secrets fit in the broader topology.