Our Azure plugin loads secrets from Azure Key Vault and settings from Azure App Configuration using declarative instructions within your .env files.
Both services share one @initAzure() instance and one authentication chain: Managed Identity for Azure-hosted applications, Azure CLI credentials for local development, and service principal or OIDC federated credentials for non-Azure environments. App Configuration can also be read with an access-key connection string.
Features
Section titled “Features”- Zero-config authentication - Just provide your vault URL, authentication happens automatically
- Managed Identity support - No credentials needed for Azure-hosted apps (App Service, Container Instances, VMs, Functions, AKS)
- Azure CLI authentication - Works with
az loginfor local development - Auto-infer secret names from environment variable names (e.g.,
DATABASE_URL→database-url) - Support for service principal credentials (for non-Azure environments)
- Support for versioned secrets
- App Configuration settings - Read single settings with
azureAppConfig()or load many at once withazureAppConfigBulk(), with label support - Key Vault references stored in App Configuration are dereferenced automatically
- Automatic token caching and renewal
- OIDC workload identity - Authenticate using platform OIDC tokens (Vercel, GitHub Actions, etc.)
- Lightweight implementation using the REST APIs (no Azure SDK dependencies)
For global cache mode behavior and CLI cache controls, see the Caching guide.
Installation and setup
Section titled “Installation and setup”In a JS/TS project, you may install the @varlock/azure-key-vault-plugin package as a normal dependency.
Otherwise you can just load it directly from your .env.schema file, as long as you add a version specifier.
See the plugins guide for more instructions on installing plugins.
# 1. Load the plugin# @plugin(@varlock/azure-key-vault-plugin)## 2. Initialize the plugin - see below for more details on options# @initAzure(vaultUrl="https://my-vault.vault.azure.net/")To read from App Configuration instead of (or in addition to) Key Vault, pass appConfigEndpoint. At least one of vaultUrl, appConfigEndpoint, or appConfigConnectionString is required.
# @plugin(@varlock/azure-key-vault-plugin)# @initAzure(# vaultUrl="https://my-vault.vault.azure.net/",# appConfigEndpoint="https://my-store.azconfig.io"# )Authentication options
Section titled “Authentication options”The plugin tries authentication methods in this priority order:
- Service Principal - If all three credentials (
tenantId,clientId,clientSecret) are provided - OIDC federated credentials - If
tenantIdandclientIdare provided withoutclientSecret, uses OIDC workload identity federation - Managed Identity - Automatically used when running on Azure infrastructure
- Azure CLI - Falls back to
az loginfor local development
The same chain is used for both services. Only the token scope differs (https://vault.azure.net for Key Vault, https://azconfig.io for App Configuration), and tokens are cached per scope. App Configuration additionally accepts an access-key connection string via appConfigConnectionString, which bypasses this chain for App Configuration requests only (see Access-key connection string below).
Automatic authentication (Recommended)
Section titled “Automatic authentication (Recommended)”For most use cases, you only need to provide the vault URL:
# @plugin(@varlock/azure-key-vault-plugin)# @initAzure(vaultUrl="https://my-vault.vault.azure.net/")How this works:
- Local development: Run
az login→ automatically uses Azure CLI credentials - Azure-hosted apps (App Service, Container Instances, VMs, Functions, AKS): Enable Managed Identity → automatically authenticates (no secrets needed!)
- Works everywhere with zero configuration beyond the vault URL!
Service principal credentials (For non-Azure environments)
Section titled “Service principal credentials (For non-Azure environments)”If you’re deploying outside of Azure (e.g., AWS, GCP, on-premises), wire up service principal credentials:
-
Create a service principal with the necessary permissions (see Azure Setup section below)
-
Wire up the credentials in your config. Add config items for the tenant ID, client ID, and client secret, and reference them when initializing the plugin.
.env.schema # @plugin(@varlock/azure-key-vault-plugin)# @initAzure(# vaultUrl="https://my-vault.vault.azure.net/",# tenantId=$AZURE_TENANT_ID,# clientId=$AZURE_CLIENT_ID,# clientSecret=$AZURE_CLIENT_SECRET# )# ---# @type=azureTenantId @sensitiveAZURE_TENANT_ID=# @type=azureClientId @sensitiveAZURE_CLIENT_ID=# @type=azureClientSecret @sensitive @internalAZURE_CLIENT_SECRET= -
Set your credentials in deployed environments. Use your platform’s env var management UI to securely inject these values.
OIDC federated credentials
Section titled “OIDC federated credentials”For deployments on platforms that issue OIDC tokens (Vercel, GitHub Actions, GitLab CI, Fly.io, GCP), you can use OIDC workload identity federation to authenticate without a client secret. Varlock auto-detects the platform’s OIDC token and exchanges it for an Azure access token via federated credential authentication.
Provider-side setup:
- Create an App Registration (or use an existing one)
- Add a federated credential:
az ad app federated-credential create \ --id <app-object-id> \ --parameters '{ "name": "vercel-oidc", "issuer": "https://oidc.vercel.com", "subject": "<your-vercel-subject-claim>", "audiences": ["api://AzureADTokenExchange"] }'- Grant the App Registration “Key Vault Secrets User” role on your vault.
Varlock config:
# @plugin(@varlock/azure-key-vault-plugin)# @initAzure(# vaultUrl="https://my-vault.vault.azure.net/",# tenantId=$AZURE_TENANT_ID,# clientId=$AZURE_CLIENT_ID# )When clientSecret is omitted but tenantId and clientId are provided, the plugin automatically uses OIDC federated credentials. For platforms not auto-detected, pass an explicit token via oidcToken.
Multiple vaults
Section titled “Multiple vaults”If you need to connect to multiple vaults, but never at the same time, you can alter the vault URL using a function:
# @plugin(@varlock/azure-key-vault-plugin)# @initAzure(vaultUrl="https://my-vault-${ENV}.vault.azure.net/")Or if you need to connect to multiple vaults simultaneously, register multiple named instances:
# @initAzure(id=prod, vaultUrl="https://my-vault-prod.vault.azure.net/")# @initAzure(id=dev, vaultUrl="https://my-vault-dev.vault.azure.net/")# ---
PROD_SECRET=azureSecret(prod, "database-url")DEV_SECRET=azureSecret(dev, "database-url")The same applies to App Configuration stores: give each instance its own appConfigEndpoint and select it with azureAppConfig(id, "key").
Loading secrets
Section titled “Loading secrets”Once the plugin is installed and initialized, you can start adding config items that load values using the azureSecret() resolver function.
Basic usage
Section titled “Basic usage”The azureSecret() function fetches secrets from Azure Key Vault.
# Auto-infer secret names (DATABASE_URL -> "database-url")DATABASE_URL=azureSecret()API_KEY=azureSecret()
# Explicit secret namesCUSTOM_SECRET=azureSecret("my-custom-secret-name")Versioned secrets
Section titled “Versioned secrets”You can fetch specific versions of secrets by appending @version to the secret name:
# Fetch latest version (default)API_KEY=azureSecret("api-key")
# Fetch specific versionAPI_KEY_V1=azureSecret("api-key@abc123def456")Loading App Configuration settings
Section titled “Loading App Configuration settings”Azure App Configuration is a central store for key-value settings. It is the Azure counterpart of AWS Parameter Store: settings are plain values (or JSON), organized by key and optional label, and secrets are usually stored as references to Key Vault rather than in the store itself.
Point the instance at your store with appConfigEndpoint. For Azure Government or Azure China, also set cloud=usgov or cloud=china so tokens are minted for the right endpoints. Authentication uses the same chain as Key Vault, and the identity needs the App Configuration Data Reader role on the store (see Azure Setup below).
# @plugin(@varlock/azure-key-vault-plugin)# @initAzure(appConfigEndpoint="https://my-store.azconfig.io")Use defaultLabel to select a label for every lookup that does not name one explicitly. This is the usual way to keep one schema across environments:
# @initAzure(appConfigEndpoint="https://my-store.azconfig.io", defaultLabel="${APP_ENV}")Access-key connection string
Section titled “Access-key connection string”If you cannot use Entra ID authentication for the store, pass an access-key connection string instead of appConfigEndpoint. The plugin signs each request with HMAC-SHA256 using the key in the connection string. This only authenticates App Configuration requests; Key Vault (including Key Vault references) still goes through the Entra chain.
# @plugin(@varlock/azure-key-vault-plugin)# @initAzure(appConfigConnectionString=$AZURE_APPCONFIG_CONNECTION_STRING)# ---
# @type=azureAppConfigConnectionStringAZURE_APPCONFIG_CONNECTION_STRING=Get a connection string with az appconfig credential list --name my-store --query "[?name=='Primary Read Only'].connectionString" -o tsv. Prefer the read-only key.
Reading a single setting
Section titled “Reading a single setting”The azureAppConfig() function reads one setting. With no arguments it uses the config item key verbatim (App Configuration allows underscores, so no case conversion happens). Pass label= to override defaultLabel for one item; with no label configured, the unlabeled setting is read.
# Reads the setting named "DATABASE_URL" (unlabeled, or defaultLabel if set)DATABASE_URL=azureAppConfig()
# Explicit key, using App Configuration's usual ":" hierarchyAPI_URL=azureAppConfig("services:api:url")
# Explicit labelAPI_URL_PROD=azureAppConfig("services:api:url", label=production)
# Force the unlabeled setting even when defaultLabel is setAPI_URL_BASE=azureAppConfig("services:api:url", label="")
# From a specific instanceAPI_URL_STAGING=azureAppConfig(staging, "services:api:url")Bulk loading
Section titled “Bulk loading”azureAppConfigBulk() returns every matching setting as a JSON object, for use with @setValuesBulk(). Filter by key (* is a wildcard) and label, and strip a common prefix so the remaining part of each key matches your config item names. Settings whose keys collide after trimming produce an error.
# @plugin(@varlock/azure-key-vault-plugin)# @initAzure(appConfigEndpoint="https://my-store.azconfig.io")# @setValuesBulk(azureAppConfigBulk(keyFilter="myapp:*", labelFilter=production, trimKeyPrefix="myapp:"), format=json)# ---
# Populated from "myapp:DATABASE_URL" and "myapp:API_HOST" with the "production" labelDATABASE_URL=API_HOST=keyFilter defaults to *. labelFilter defaults to defaultLabel if set, otherwise to unlabeled settings only. Results are paged through automatically.
Key Vault references
Section titled “Key Vault references”App Configuration can store a pointer to a Key Vault secret instead of the value itself (content type application/vnd.microsoft.appconfig.keyvaultref+json). Both azureAppConfig() and azureAppConfigBulk() detect these and fetch the referenced secret, using the instance’s Key Vault credentials. The identity needs “Key Vault Secrets User” (or the “Get” access policy) on each referenced vault.
The reference URI is data written by whoever can edit the store, so the plugin only follows it to a vault it can trust: an https://<vault>.vault.azure.net host (or the equivalent suffix for the selected cloud), or the exact vaultUrl configured on the instance. Any other origin is rejected with an error and never receives the vault token. Because of this, vaultUrl does not need to be set for references within the same cloud.
# In App Configuration, "DB_PASSWORD" is a Key Vault reference to# https://my-vault.vault.azure.net/secrets/db-password# @sensitiveDB_PASSWORD=azureAppConfig()Feature flags
Section titled “Feature flags”Feature flags are stored as JSON settings (content type application/vnd.microsoft.appconfig.ff+json) under keys prefixed with .appconfig.featureflag/. They are returned as their JSON string; the plugin does not evaluate them.
# Resolves to the flag's JSON, e.g. {"id":"beta","enabled":true,...}BETA_FLAG=azureAppConfig(".appconfig.featureflag/beta")Azure Setup
Section titled “Azure Setup”Required permissions
Section titled “Required permissions”Your managed identity, service principal, or user needs one of:
- Access Policy: “Get” permission for secrets
- RBAC: “Key Vault Secrets User” role
App Configuration Data Reader role
Section titled “App Configuration Data Reader role”To read from App Configuration with Entra ID authentication, the identity (managed identity, service principal, or your az login user) needs the App Configuration Data Reader role on the store. Access keys (connection strings) do not need a role assignment.
STORE_ID=$(az appconfig show --name my-store --query id -o tsv)
az role assignment create \ --role "App Configuration Data Reader" \ --assignee <principal-id-or-appId> \ --scope $STORE_IDIf the store holds Key Vault references, the same identity also needs “Key Vault Secrets User” on each referenced vault.
Managed Identity for Azure-hosted apps (Recommended)
Section titled “Managed Identity for Azure-hosted apps (Recommended)”Managed Identity is the Azure-native way to authenticate - no credentials needed!
-
Enable system-assigned managed identity for your Azure resource
Terminal window # For App Serviceaz webapp identity assign --name my-app --resource-group my-rg# For Container Instanceaz container create --assign-identity --name my-container ...# For VMaz vm identity assign --name my-vm --resource-group my-rg -
Grant Key Vault access to the identity
Get the identity’s principal ID:
Terminal window PRINCIPAL_ID=$(az webapp identity show --name my-app --resource-group my-rg --query principalId -o tsv)Then grant access using either RBAC or Access Policy:
Option A: RBAC (Recommended)
Terminal window az role assignment create \--role "Key Vault Secrets User" \--assignee $PRINCIPAL_ID \--scope /subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/<vault-name>Option B: Access Policy
Terminal window az keyvault set-policy \--name my-vault \--object-id $PRINCIPAL_ID \--secret-permissions get -
That’s it! Your app will automatically authenticate using Managed Identity.
Service principal for non-Azure environments
Section titled “Service principal for non-Azure environments”-
Create a service principal
Terminal window az ad sp create-for-rbac --name "varlock-keyvault-reader"Save the
appId,password, andtenantfrom the output. -
Grant Key Vault access
Option A: RBAC (Recommended)
Terminal window az role assignment create \--role "Key Vault Secrets User" \--assignee <appId> \--scope /subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/<vault-name>Option B: Access Policy
Terminal window az keyvault set-policy \--name my-vault \--spn <appId> \--secret-permissions get
Azure CLI for local development
Section titled “Azure CLI for local development”-
Install the Azure CLI if you haven’t already: Installation guide
-
Log in to Azure
Terminal window az login -
Verify your identity
Terminal window az account show -
Grant Key Vault access to your user account (if needed)
Terminal window az keyvault set-policy \--name my-vault \--upn your-email@domain.com \--secret-permissions get
Reference
Section titled “Reference”Root decorators
Section titled “Root decorators”@initAzure()
Section titled “@initAzure()”Initialize an Azure plugin instance for Key Vault and/or App Configuration. At least one of vaultUrl, appConfigEndpoint, or appConfigConnectionString is required.
Key/value args:
vaultUrl(optional): Azure Key Vault URL (e.g.,https://my-vault.vault.azure.net/). Required forazureSecret().appConfigEndpoint(optional): Azure App Configuration store endpoint (e.g.,https://my-store.azconfig.io). Required forazureAppConfig()/azureAppConfigBulk()unlessappConfigConnectionStringis set.appConfigConnectionString(optional): App Configuration access-key connection string (Endpoint=...;Id=...;Secret=...). Alternative toappConfigEndpointplus Entra ID auth; applies to App Configuration requests only.defaultLabel(optional): label used byazureAppConfig()andazureAppConfigBulk()when none is givencloud(optional):public(default),usgov, orchina. Selects the Entra authority host, the token audiences for Key Vault and App Configuration, and the Key Vault DNS suffix trusted for Key Vault references. Theaz cloud listnames (AzureCloud,AzureUSGovernment,AzureChinaCloud) are accepted too.authorityHost(optional): override the Entra ID authority host chosen bycloud. Rarely needed.tenantId(optional): Azure AD tenant ID (directory ID)clientId(optional): Service principal application (client) IDclientSecret(optional): Service principal client secret (password)oidcToken(optional): Explicit OIDC JWT token (auto-detected from platform if omitted)id(optional): Instance identifier for multiple vaultscacheTtl(optional): cache resolved values for the specified duration (e.g."5m","1h","1d", orforeverto cache until manually cleared). For cache mode behavior and CLI cache controls, see the Caching guide.
# @initAzure(vaultUrl="https://my-vault.vault.azure.net/")# @initAzure(id=config, appConfigEndpoint="https://my-store.azconfig.io", defaultLabel=production)Data types
Section titled “Data types”azureTenantId
Section titled “azureTenantId”Represents an Azure AD tenant ID (UUID format). This type is marked as @sensitive.
# @type=azureTenantIdAZURE_TENANT_ID=azureClientId
Section titled “azureClientId”Represents a service principal application (client) ID (UUID format). This type is marked as @sensitive.
# @type=azureClientIdAZURE_CLIENT_ID=azureClientSecret
Section titled “azureClientSecret”Represents a service principal client secret (password). This type is marked as @sensitive.
# @type=azureClientSecretAZURE_CLIENT_SECRET=azureAppConfigConnectionString
Section titled “azureAppConfigConnectionString”Represents an App Configuration access-key connection string (Endpoint=...;Id=...;Secret=...). This type is marked as @sensitive and @internal.
# @type=azureAppConfigConnectionStringAZURE_APPCONFIG_CONNECTION_STRING=Resolver functions
Section titled “Resolver functions”azureSecret()
Section titled “azureSecret()”Fetch a secret from Azure Key Vault.
Array args:
instanceId(optional): instance identifier to use when multiple plugin instances are initializedsecretName(optional): secret name or name with version using@syntax. If omitted, uses the variable name (converted to kebab-case).
# Auto-infer secret name (DATABASE_URL -> "database-url")DATABASE_URL=azureSecret()
# Explicit secret nameCUSTOM_SECRET=azureSecret("my-custom-secret")
# Specific versionAPI_KEY_V1=azureSecret("api-key@abc123def456")
# With instance IDPROD_SECRET=azureSecret(prod, "database-url")azureAppConfig()
Section titled “azureAppConfig()”Fetch a single setting from Azure App Configuration. Key Vault references are dereferenced; feature flags are returned as their JSON string.
Array args:
instanceId(optional): instance identifier to use when multiple plugin instances are initializedkey(optional): setting key. If omitted, uses the config item key verbatim.
Key/value args:
label(optional): setting label. Overrides the instancedefaultLabel; an empty string selects the unlabeled setting.
# Auto-infer key (DATABASE_URL -> "DATABASE_URL")DATABASE_URL=azureAppConfig()
# Explicit key and labelAPI_URL=azureAppConfig("services:api:url", label=production)
# With instance IDSTAGING_API_URL=azureAppConfig(staging, "services:api:url")azureAppConfigBulk()
Section titled “azureAppConfigBulk()”List settings from Azure App Configuration and return them as a JSON object string, for use with @setValuesBulk(..., format=json).
Array args:
instanceId(optional): instance identifier to use when multiple plugin instances are initialized
Key/value args:
keyFilter(optional): key filter,*matches any characters (default*)labelFilter(optional): label filter (default:defaultLabelif set, otherwise unlabeled settings only)trimKeyPrefix(optional): prefix removed from each key before it is matched to a config item
# @setValuesBulk(azureAppConfigBulk(keyFilter="myapp:*", labelFilter=production, trimKeyPrefix="myapp:"), format=json)Troubleshooting
Section titled “Troubleshooting”Secret not found
Section titled “Secret not found”- Verify the secret exists:
az keyvault secret list --vault-name my-vault - Remember: Azure uses hyphens, not underscores (use
database-urlnotdatabase_url) - Check for typos in the secret name
Setting not found
Section titled “Setting not found”- List settings in the store:
az appconfig kv list --endpoint https://my-store.azconfig.io --auth-mode login - Check the label: an unlabeled setting and a labeled one with the same key are different settings. Set
defaultLabelor passlabel= - App Configuration keys are case-sensitive and used verbatim (no kebab-case conversion)
Permission denied
Section titled “Permission denied”- Check your RBAC role:
az role assignment list --assignee <your-id> --scope <vault-scope> - Or check access policies:
az keyvault show --name my-vault --query properties.accessPolicies - Ensure your identity has “Get” permission for secrets
- For App Configuration, ensure the identity has the “App Configuration Data Reader” role on the store
Authentication failed
Section titled “Authentication failed”- Local dev: Run
az loginand ensure service principal env vars are empty - Azure-hosted apps: Verify Managed Identity is enabled and has Key Vault permissions
- Other environments: Verify service principal credentials are correct and properly injected
- Test identity:
az account show
Vault not accessible
Section titled “Vault not accessible”- Verify the vault URL is correct
- Check network access: Ensure firewall rules allow access from your IP/resource
- Verify the vault exists in the specified subscription