@varlock/azure-key-vault-plugin
v1.3.0
Published
Varlock plugin to load secrets from Azure Key Vault and settings from Azure App Configuration
Readme
@varlock/azure-key-vault-plugin
This package is a Varlock plugin that loads secrets from Azure Key Vault and settings from Azure App Configuration into your configuration.
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 seamlessly with
az loginfor local development - Auto-infer secret names from environment variable names (e.g.,
DATABASE_URL→database-url) - OIDC workload identity - Authenticate from Vercel, GitHub Actions, and other platforms using federated credentials
- Support for service principal credentials (for non-Azure environments)
- Support for versioned secrets
- Extract individual values from JSON-encoded 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
- Lightweight implementation using the REST APIs (no Azure SDK dependencies)
Installation
If you are in a JavaScript based project and have a package.json file, you can either install the plugin explicitly
npm install @varlock/azure-key-vault-pluginAnd then register the plugin without any version number
# @plugin(@varlock/azure-key-vault-plugin)Otherwise just set the explicit version number when you register it
# @plugin(@varlock/[email protected])See our Plugin Guide for more details.
Setup + Auth
After registering the plugin, you must initialize it with the @initAzure root decorator. One instance can serve Key Vault, App Configuration, or both; at least one of vaultUrl, appConfigEndpoint, or appConfigConnectionString is required.
Automatic auth
For most use cases, you only need to provide the vault URL and/or App Configuration endpoint:
# @plugin(@varlock/azure-key-vault-plugin)
# @initAzure(vaultUrl="https://my-vault.vault.azure.net/")
# Or, with App Configuration as well
# @initAzure(
# vaultUrl="https://my-vault.vault.azure.net/",
# appConfigEndpoint="https://my-store.azconfig.io"
# )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!
Explicit credentials (For non-Azure environments)
If you're deploying outside of Azure (e.g., AWS, GCP, on-premises), wire up service principal credentials:
# @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
AZURE_TENANT_ID=
# @type=azureClientId
AZURE_CLIENT_ID=
# @type=azureClientSecret @sensitive @internal
AZURE_CLIENT_SECRET=You would then need to inject these env vars using your CI/CD system.
@internalkeeps this credential out of your app's environment — varlock only uses it to fetch your secrets. If you need the credential at runtime for other purposes (e.g. via the Azure SDK), set@internal=falseto keep it injected.
OIDC workload identity (For Vercel, GitHub Actions, etc.)
If you're deploying on a platform that supports OIDC, you can authenticate without a client secret:
# @plugin(@varlock/azure-key-vault-plugin)
# @initAzure(
# vaultUrl="https://my-vault.vault.azure.net/",
# tenantId=$AZURE_TENANT_ID,
# clientId=$AZURE_CLIENT_ID
# )
# ---
# @type=azureTenantId
AZURE_TENANT_ID=
# @type=azureClientId
AZURE_CLIENT_ID=When tenantId and clientId are provided without clientSecret, the plugin automatically uses the platform's OIDC token as a federated credential. You need to configure a federated credential on your Azure App Registration.
See the OIDC Workload Identity guide for full setup instructions.
Authentication Priority
The plugin tries authentication methods in this order:
- Service Principal - If all three credentials (
tenantId,clientId,clientSecret) are provided and non-empty - OIDC Federated Credential - If
tenantIdandclientIdare provided withoutclientSecret, and an OIDC token is available - Managed Identity - Automatically used when running on Azure infrastructure
- Azure CLI - Falls back to
az loginfor local development
The same chain serves both Key Vault and App Configuration; tokens are requested and cached per service scope. App Configuration can alternatively use an access-key connection string (see below), which does not affect Key Vault requests.
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:
# @initAzure(vaultUrl="https://my-vault-${ENV}.vault.azure.net/")Or if in some cases you need to connect to both, or you want more explicit separation, you can 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/")Reading secrets
This plugin introduces a new function azureSecret() to fetch secret values from your vaults.
# @plugin(@varlock/azure-key-vault-plugin)
# @initAzure(vaultUrl="https://my-vault.vault.azure.net/")
# ---
# Auto-infer secret names (DATABASE_URL -> "database-url")
DATABASE_URL=azureSecret()
API_KEY=azureSecret()
# Explicit secret names
CUSTOM_SECRET=azureSecret("my-custom-secret-name")
# Versioned secrets - using @ suffix or named version= param
API_KEY_V1=azureSecret("api-key@abc123def456")
API_KEY_V2=azureSecret("api-key", version=abc123def456)
# Extract a value from a JSON-encoded secret - using # suffix or named key= param
# (e.g. the secret "db-creds" holds `{"username":"admin","password":"..."}`)
DB_PASSWORD=azureSecret("db-creds#password")
DB_USERNAME=azureSecret("db-creds", key=username)
# If using multiple named vault instances
PROD_SECRET=azureSecret(prod, "database-url")
DEV_SECRET=azureSecret(dev, "database-url")Reading App Configuration settings
Point the instance at your store with appConfigEndpoint (find it with az appconfig show --name my-store --query endpoint -o tsv). The identity needs the App Configuration Data Reader role on the store. defaultLabel selects a label for every lookup that does not name one.
# @plugin(@varlock/azure-key-vault-plugin)
# @initAzure(appConfigEndpoint="https://my-store.azconfig.io", defaultLabel="${APP_ENV}")
# ---
# Reads the setting named "DATABASE_URL" (key used verbatim, no kebab-case conversion)
DATABASE_URL=azureAppConfig()
# Explicit key and label
API_URL=azureAppConfig("services:api:url", label=production)
# Empty label forces the unlabeled setting even when defaultLabel is set
API_URL_BASE=azureAppConfig("services:api:url", label="")
# From a specific instance
API_URL_STAGING=azureAppConfig(staging, "services:api:url")Access-key connection string
If Entra ID auth is not an option for the store, pass a connection string instead of appConfigEndpoint. Requests are signed with HMAC-SHA256. This authenticates App Configuration only; Key Vault (including Key Vault references) still uses the Entra chain.
# @initAzure(appConfigConnectionString=$AZURE_APPCONFIG_CONNECTION_STRING)
# ---
# @type=azureAppConfigConnectionString
AZURE_APPCONFIG_CONNECTION_STRING=Bulk loading
azureAppConfigBulk() returns all matching settings as JSON for @setValuesBulk(). keyFilter defaults to *; labelFilter defaults to defaultLabel, or to unlabeled settings only. trimKeyPrefix strips a common prefix so keys line up with your config item names; keys that collide after trimming produce an error. Pagination is handled automatically.
# @setValuesBulk(azureAppConfigBulk(keyFilter="myapp:*", labelFilter=production, trimKeyPrefix="myapp:"), format=json)
# ---
DATABASE_URL=
API_HOST=Key Vault references and feature flags
Settings with content type application/vnd.microsoft.appconfig.keyvaultref+json point at a Key Vault secret. Both resolvers detect them and fetch the secret using the instance's Key Vault credentials. Since the reference URI is written by whoever can edit the store, it is only followed to an https://<vault>.vault.azure.net host (or the selected cloud's Key Vault suffix) or to the configured vaultUrl; other origins are rejected and never receive the vault token. vaultUrl does not need to be set for references within the same cloud.
Feature flags (application/vnd.microsoft.appconfig.ff+json) are returned as their JSON string and are not evaluated.
Reference
Root decorators
@initAzure()
Initialize an Azure plugin instance. At least one of vaultUrl, appConfigEndpoint, or appConfigConnectionString is required.
Parameters:
vaultUrl?: string- Azure Key Vault URL (e.g.,https://my-vault.vault.azure.net/); required forazureSecret()appConfigEndpoint?: string- Azure App Configuration endpoint (e.g.,https://my-store.azconfig.io); required forazureAppConfig()/azureAppConfigBulk()unlessappConfigConnectionStringis setappConfigConnectionString?: string- App Configuration access-key connection string (Endpoint=...;Id=...;Secret=...); alternative toappConfigEndpointplus Entra auth, App Configuration onlydefaultLabel?: string- label used byazureAppConfig()/azureAppConfigBulk()when none is givencloud?: "public" | "usgov" | "china"- Azure cloud (defaultpublic); selects the authority host, token audiences, and the Key Vault DNS suffix trusted for Key Vault references.az cloud listnames are accepted tooauthorityHost?: string- override the Entra ID authority host chosen bycloud(rarely needed)tenantId?: string- Azure AD tenant ID (directory ID)clientId?: string- Service principal application (client) IDclientSecret?: string- Service principal client secret (password)oidcToken?: string- Explicit OIDC JWT token (auto-detected from platform if not provided)cacheTtl?: string | number- Cache resolved values for the provided TTL ("5m","1h","1d", or"forever"to cache until manually cleared); set tofalse(or an empty string) to disable cachingid?: string- Instance identifier for multiple vaults (defaults to_default)
Functions
azureSecret()
Fetch a secret from Azure Key Vault.
Signatures:
azureSecret()- Auto-infers secret name from variable name (DATABASE_URL→database-url)azureSecret(secretName)- Fetch by explicit secret nameazureSecret(instanceId, secretName)- Fetch from a specific vault instanceazureSecret(secretName, version=versionId)- Fetch a specific versionazureSecret(secretName, key=jsonKey)- Extract a value from a JSON-encoded secret
Named parameters:
version=- Secret version (alternative to@versionsuffix in the secret name)key=- Key to extract from a JSON-encoded secret value (alternative to#keysuffix in the secret name)
Secret Name Formats:
- Latest version:
"my-secret" - Specific version:
"my-secret@abc123def456"orazureSecret("my-secret", version=abc123def456) - JSON key extraction:
"my-secret#password"orazureSecret("my-secret", key=password) - Combined:
"my-secret@abc123def456#password"
azureAppConfig()
Fetch a single setting from Azure App Configuration. Key Vault references are dereferenced; feature flags are returned as JSON strings.
Signatures:
azureAppConfig()- Uses the config item key verbatim as the setting keyazureAppConfig(key)- Fetch by explicit keyazureAppConfig(instanceId, key)- Fetch from a specific instance
Named parameters:
label=- Setting label; overrides the instancedefaultLabel. An empty string selects the unlabeled setting.
azureAppConfigBulk()
List settings and return them as a JSON object string for @setValuesBulk(..., format=json).
Signatures:
azureAppConfigBulk()- Default instanceazureAppConfigBulk(instanceId)- Specific instance
Named parameters:
keyFilter=- Key filter,*matches any characters (default*)labelFilter=- Label filter (default:defaultLabelif set, otherwise unlabeled settings only)trimKeyPrefix=- Prefix removed from each key before matching config items
Data Types
azureTenantId- Azure AD tenant ID (UUID format)azureClientId- Service principal application ID (UUID format)azureClientSecret- Service principal client secret (sensitive)azureAppConfigConnectionString- App Configuration access-key connection string (sensitive, internal)
Azure Setup
Required Permissions
Your managed identity, service principal, or user needs one of:
- Access Policy: "Get" permission for secrets
- RBAC: "Key Vault Secrets User" role
For App Configuration, the identity needs the App Configuration Data Reader role on the store (access-key connection strings need no role):
az role assignment create \
--role "App Configuration Data Reader" \
--assignee <principal-id-or-appId> \
--scope $(az appconfig show --name my-store --query id -o tsv)Enable Managed Identity (Recommended for Azure-hosted apps)
Managed Identity is the Azure-native way to authenticate - no credentials needed!
Enable system-assigned managed identity:
# For App Service
az webapp identity assign --name my-app --resource-group my-rg
# For Container Instance
az container create --assign-identity --name my-container ...
# For VM
az vm identity assign --name my-vm --resource-group my-rgGrant Key Vault access to the identity:
# Get the identity's principal ID
PRINCIPAL_ID=$(az webapp identity show --name my-app --resource-group my-rg --query principalId -o tsv)
# Grant RBAC role
az role assignment create \
--role "Key Vault Secrets User" \
--assignee $PRINCIPAL_ID \
--scope /subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/<vault-name>
# Or set Access Policy
az keyvault set-policy \
--name my-vault \
--object-id $PRINCIPAL_ID \
--secret-permissions getThat's it! Your app will now automatically authenticate using Managed Identity.
Create a Service Principal (For non-Azure environments)
# Create service principal
az ad sp create-for-rbac --name "varlock-keyvault-reader"
# Grant access (Access Policy)
az keyvault set-policy \
--name my-vault \
--spn <appId> \
--secret-permissions get
# Or grant access (RBAC)
az role assignment create \
--role "Key Vault Secrets User" \
--assignee <appId> \
--scope /subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/<vault-name>Find Your Key Vault URL
az keyvault show --name my-vault --query properties.vaultUri -o tsv
# Output: https://my-vault.vault.azure.net/Troubleshooting
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)
Setting not found
- List settings:
az appconfig kv list --endpoint https://my-store.azconfig.io --auth-mode login - Check the label: unlabeled and labeled settings with the same key are different settings
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 - For App Configuration, ensure the identity has the "App Configuration Data Reader" role on the store
Authentication failed
- Local dev: Run
az loginand ensure your env vars (AZURE_TENANT_ID, etc.) 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
