Azure Key Vault resolver

Config resolver that swaps azure-kv: placeholders for live values fetched from Azure Key Vault.

Overview

The Piko config loader resolves <prefix>:<value> placeholder strings against any registered resolver. The Azure package handles the azure-kv: prefix. The loader strips the prefix and hands the resolver a vault-name/secret-name body. The resolver looks up https://<vault-name>.vault.azure.net/, fetches the secret through the official azsecrets SDK, and returns the value. A #<key> suffix selects a field inside a JSON-shaped secret.

The resolver code stays small because it inherits the loader machinery. The loader holds a shared circuit breaker and a short-lived resolution cache, so a failing vault fails fast and repeated lookups of the same placeholder skip the round trip. The resolver itself caches one azsecrets client per vault, so repeated lookups against the same vault reuse the connection pool.

The resolver authenticates with DefaultAzureCredential, which walks an authentication chain: environment variables, Workload Identity, Managed Identity, Azure CLI, and so on. The constructor accepts no static credentials.

The Azure SDK dependency tree lives in a separately versioned module, piko.sh/piko/wdk/config/config_resolver_azure. The core Piko module does not depend on it, so an application that never touches Key Vault never pulls in the Azure SDK. Add the module and run go mod tidy to bring in the dependency. This resolver slots into the same config.Resolver contract as the AWS, GCP, Kubernetes, and Vault siblings, so combining or swapping secret backends stays uniform.

Requirements

  • Azure credentials reachable through DefaultAzureCredential. In production this means a Managed Identity assigned to the host, for example App Service, an AKS pod identity, or a VM. Locally it means az login or environment-variable credentials.
  • An Azure RBAC role on the Key Vault that includes Get for secrets. The Key Vault Secrets User role covers this. The Key Vault Reader role covers both metadata and values. The legacy access policy permission Get for secrets also works.
  • Network egress to <vault-name>.vault.azure.net over HTTPS.

Configuration

NewResolver takes no arguments. It constructs DefaultAzureCredential internally.

import "piko.sh/piko/wdk/config/config_resolver_azure"

resolver, err := config_resolver_azure.NewResolver()
if err != nil {
    return err
}

Reference secrets in your config struct with the azure-kv: prefix and the vault-name/secret-name body:

database:
  password: azure-kv:my-prod-vault/db-password
stripe:
  secret_key: azure-kv:my-prod-vault/api-keys#stripe

Bootstrap

import (
    "piko.sh/piko"
    "piko.sh/piko/wdk/config/config_resolver_azure"
)

resolver, err := config_resolver_azure.NewResolver()
if err != nil {
    return err
}

ssr := piko.New(
    piko.WithConfigResolvers(resolver),
)

That single WithConfigResolvers option is the only wiring step. NewResolver takes no arguments and the returned *Resolver satisfies the config.Resolver contract, so the integration adds no glue code beyond these two lines.

The package also exports a Register() shortcut that constructs a resolver and registers it in the global resolver registry through config.RegisterResolver, useful from init().

Secret resolution runs in the compiled bootstrap path that loads config, so the interpreted dev mode used for pure-Go providers does not affect it.

See also

Sibling resolvers:

Companion provider:

Framework docs:

External: