Azure App Configuration Provider
The Azure App Configuration provider reads and manages ordinary key-values and resolves canonical Azure Key Vault references.
At a glance
| Provider | aac (0.20+) |
| URI | aac://STORE[?OPTIONS] |
| Access | Read, write, delete, and discovery; native references are read-only |
| Best for | Azure applications that centralize configuration and Key Vault references |
| Authentication | Service principal, Azure CLI, managed identity, workload identity, or App Configuration connection string |
| Availability | SecretSpec 0.20+; included in official and default builds (aac feature for custom minimal builds) |
| Default storage | secretspec:{project}:{profile}:{key} with no label |
Quick start
The official SecretSpec CLI includes the AAC provider. These commands assume the store already exists and your signed-in Azure identity has App Configuration Data Owner on it:
$ az login$ secretspec set DATABASE_URL --provider aac://payments-production$ secretspec get DATABASE_URL --provider aac://payments-production$ secretspec run --provider aac://payments-production -- your-command
Terminal window
Use App Configuration Data Reader instead for identities that only
run get, check, or run. See Assign Azure
roles for the complete setup.
Setup
Prerequisites
- An Azure App Configuration store
- For Entra authentication, App Configuration Data Reader for reads or App Configuration Data Owner for writes and deletes
- For connection-string authentication, a read-only access key for reads or a read-write access key for writes and deletes
- Key Vault Secrets User on each referenced vault when entries are Key Vault references
- SecretSpec 0.20+. Official binaries and default Cargo builds include
AAC; custom
--no-default-featuresbuilds must enable--features aac.
Authentication
Select authentication with auth:
env(default): a completetenant_id,client_id, andclient_secretprovider-credential triple, withAZURE_TENANT_ID,AZURE_CLIENT_ID, andAZURE_CLIENT_SECRETas fallbacks. With none configured, SecretSpec uses the signed-in Azure CLI or Azure Developer CLI session. A partial triple is an error.cli: Azure CLI or Azure Developer CLI only.managed_identity: system-assigned managed identity.workload_identity: Azure workload identity federation.connection_string: theconnection_stringprovider credential, falling back toAZURE_APPCONFIG_CONNECTION_STRING. This environment variable is a SecretSpec fallback name. The connection string’sEndpointmust exactly match the provider URI’s endpoint, so the URI still selects the store and a credential cannot redirect requests to another endpoint. Read-only and read-write access keys can expose direct values and routing metadata across their store permissions; selectors do not narrow those permissions.
Assign Azure roles
Azure control-plane roles such as Reader, Contributor, and App Configuration Contributor do not grant Entra-authenticated access to stored key-values. Assign an App Configuration data-plane role to the exact user, service principal, workload identity, or managed identity that SecretSpec uses.
For a deployed identity, obtain the store resource ID and assign
read-only access. Replace the principal object ID and use User instead
of ServicePrincipal when assigning a human account:
$ APP_CONFIG_ID=$(az appconfig show \ --name payments-production \ --resource-group production \ --query id \ --output tsv)$ az role assignment create \ --assignee-object-id "<principal-object-id>" \ --assignee-principal-type ServicePrincipal \ --role "App Configuration Data Reader" \ --scope "$APP_CONFIG_ID"
Terminal window
Change the role to App Configuration Data Owner only for an identity
that runs set, delete, cache invalidation, or another write path.
Azure role assignments can take several minutes to propagate, so a new
assignment may briefly continue returning HTTP 403.
When selected entries can be Key Vault references, grant the same
runtime identity—or the separate identity selected by
key_vault_auth—read access to each referenced vault:
$ KEY_VAULT_ID=$(az keyvault show \ --name payments-vault \ --resource-group production \ --query id \ --output tsv)$ az role assignment create \ --assignee-object-id "<principal-object-id>" \ --assignee-principal-type ServicePrincipal \ --role "Key Vault Secrets User" \ --scope "$KEY_VAULT_ID"
Terminal window
SecretSpec never needs Key Vault write or delete permission. See the
Azure CLI az role assignment create
reference for
other principal types and scopes.
Provider credentials
| Credential | Environment fallback | Available since |
|---|---|---|
tenant_id | AZURE_TENANT_ID | 0.20+ |
client_id | AZURE_CLIENT_ID | 0.20+ |
client_secret | AZURE_CLIENT_SECRET | 0.20+ |
connection_string | AZURE_APPCONFIG_CONNECTION_STRING | 0.20+ |
See the complete provider credential reference for all supported providers and environment fallbacks.
Configuration
URI format
aac://STORE[?auth=env|cli|managed_identity|workload_identity|connection_string] [&suffix=DNS_SUFFIX][&audience=TOKEN_AUDIENCE] [&key_vault_auth=inherit|env|cli|managed_identity|workload_identity] [&key_vault_suffix=DNS_SUFFIX] [&label=LABEL][&prefix=PREFIX][&tag=NAME=VALUE]...
STORE: a bare store name, which uses.azconfig.io, or a complete host.suffix: App Configuration DNS suffix for a bare store name. Do not combine it with a dotted host.audience: Entra token audience. Public Azure defaults tohttps://appconfig.azure.com; non-public hosts require an explicit HTTPS origin. Forenvservice-principal orworkload_identityauthentication in a sovereign cloud, also setAZURE_AUTHORITY_HOSTto that cloud’s Entra authority. Forcli, select the matching Azure cloud before signing in.audiencecontrols the requested token scope; it does not select the Entra authority.label: selects one exact label. Omitting it selects the null label rather than every label.prefix: prepended literally to convention keys. Include any separator the desired key requires.tag: exactNAME=VALUEselector. Up to five unique tag names may be repeated in the URI; all must match.key_vault_auth: identity used to resolve Key Vault references.inherituses the App Configuration Entra identity, and omission behaves asinherit. Connection-string authentication cannot be inherited because it does not authenticate to Key Vault.key_vault_suffix: allowed Key Vault DNS suffix, defaulting tovault.azure.net. Referenced vaults must be direct subdomains of this suffix.
aac://payments-productionaac://shared?label=production&prefix=payments:aac://shared?tag=app=payments&tag=stage=productionaac://shared?auth=connection_string&key_vault_auth=managed_identityaac://store.example.com?audience=https%3A%2F%2Fappconfig.example.com&key_vault_suffix=vault.example.comProject configuration
[providers.app_config]uri = "aac://shared?label=production&prefix=payments:"
[providers.app_config.credentials]tenant_id = "keyring"client_id = "keyring"client_secret = "keyring"
[profiles.production]DATABASE_URL = { description = "Database URL", providers = ["app_config"] }
secretspec.toml
Store the declared credentials, then use the alias:
$ secretspec config provider login app_config$ secretspec run --provider app_config -- deploy
Terminal window
Storage and selection
Convention entries use {prefix}secretspec:{project}:{profile}:{key}.
Project and profile components accept ASCII letters, digits,
underscores, and hyphens. Secret keys start with an ASCII letter or
underscore and continue with ASCII letters, digits, or underscores;
defaults is reserved for profile configuration. App Configuration keys
cannot contain % or be exactly . or ...
Azure App Configuration identifies an entry by its key and label. Label, prefix, and tags select values; they do not grant access. Reads require the exact key, configured label, and all tag selectors. New entries receive the configured tags. Updates preserve existing tags, content type, and description. Writes and deletes refuse locked entries, non-matching tags, special content types, and concurrent changes detected through ETags.
Azure RBAC applies to the store, not to a SecretSpec prefix, label, or tag route. Use a dedicated App Configuration store when workloads must not be able to list or read one another’s direct values or reference metadata. A narrower provider URI is selection configuration, not permission isolation.
Use existing key-values
A secret’s ref names an
existing App Configuration key through item. Other coordinates are
rejected. Native references are read-only, even when they point to an
ordinary direct value.
[profiles.production]DATABASE_URL = { description = "Database URL", ref = { item = "payments:database-url" }, providers = ["aac://shared?label=production"]}Azure Key Vault references
Entries with the canonical Key Vault-reference content
type
application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8 are
resolved through their HTTPS Key Vault secret URI. A pinned version in
that URI remains pinned; an unversioned URI reads the latest version.
Key Vault references are never changed by set or delete.
Feature flags, snapshot references, and other Azure App Configuration special content types are rejected rather than returned as opaque strings. Ordinary content types remain direct values.
Connection strings authenticate only App Configuration. When auth is
connection_string, select key_vault_auth=env, cli,
managed_identity, or workload_identity before resolving Key Vault
references.
CI/CD
Prefer workload identity or managed identity for deployed workloads. Grant the runtime App Configuration Data Reader and, only when it resolves Key Vault references, Key Vault Secrets User on the required vaults:
$ secretspec run \ --provider 'aac://payments-production?auth=workload_identity' \ -- deploy
Terminal window
A provisioning or rotation job that calls set or delete needs App
Configuration Data Owner instead. Keep that writer identity separate
from read-only runtime identities. SecretSpec never needs Key Vault
write or delete permission: Key Vault references are read-only.
For a stored connection string, route the semantic connection_string
provider credential through another SecretSpec provider instead of
putting the connection string in the URI:
[providers.app_config_ci]uri = "aac://payments-production?auth=connection_string"
[providers.app_config_ci.credentials]connection_string = "keyring"
secretspec.toml
In CI, omit that credential route and set
AZURE_APPCONFIG_CONNECTION_STRING, the SecretSpec-defined environment
fallback for connection_string. In either form, the provider URI
selects the store and the connection string must name that same
endpoint.
If selected values may be Key Vault references, add an explicit Entra
key_vault_auth mode to the URI and provide that identity separately.
The App Configuration connection string cannot authenticate to Key
Vault.
Deployment topologies
| Topology | App Configuration role | Key Vault role | Boundary |
|---|---|---|---|
| Direct values, read-only runtime | App Configuration Data Reader | None | Runtime can read direct values and metadata allowed by store RBAC |
| Key Vault references, read-only runtime | App Configuration Data Reader | Key Vault Secrets User | App Configuration exposes reference URIs; Key Vault controls resolved values |
| SecretSpec-managed direct values | App Configuration Data Owner | None | Writer can create, replace, and delete direct values in management scope |
| Mixed direct values and Key Vault references | Reader or Owner, according to operation | Key Vault Secrets User for referenced vaults | App Configuration and Key Vault permissions remain independent |
| Dedicated store per trust boundary | Reader or Owner, according to operation | Only when references are used | Azure resource separation enforces isolation that selectors cannot provide |
Shared stores reduce resource count but expose direct values, labels, tags, retained revisions, and Key Vault reference URIs to principals with store-level data access. Dedicated stores provide a clearer permission boundary. Prefixes, labels, and tags can organize a shared store, but cannot turn it into separate authorization domains.
Choose Key Vault boundaries from runtime identities, ownership, and rotation responsibilities. A separate vault per application or environment is a common starting point when those boundaries differ; sharing a vault can be reasonable when the same principals and policies intentionally govern every secret. One App Configuration route can resolve references across multiple authorized vaults without changing its shape.
Discovery and caching
secretspec init --from aac://STORE discovers convention entries for
the active project, profile, prefix, label, and tags. Direct values and
Key Vault references are discoverable; unsupported special content types
stop discovery.
Azure App Configuration (0.20+) can be the authoritative side of a cached provider route. Use an encrypted cache when direct values or resolved Key Vault values must remain encrypted at rest.
SecretSpec caches the resolved value, not a Key Vault reference. A local
plaintext cache therefore exposes the resolved secret directly. Using
another Azure App Configuration route as the cache writes a logical
plaintext/direct value, although Azure encrypts it at rest. App
Configuration readers can retrieve that value, and Azure retains
key-value
revisions
for a tier-dependent history period after update or deletion.
cache clear removes the active cache entry; it is not a
revision-history purge. Use a distinct store, label, or prefix for the
cache; changing only authentication or tags does not create a distinct
storage identity. Choose the cache according to the resolved value’s
sensitivity.
Security considerations
App Configuration readers can see direct values, metadata, retained revisions, and Key Vault reference URIs within their data-plane permissions. Key Vault reference values remain protected by separate Key Vault permissions. Prefer Key Vault references for secrets that should not be exposed to App Configuration readers, and scope both services’ permissions to least privilege.
Store management scope is not value provenance. A ref.item proves
which key SecretSpec requested, not who created or approved its current
value. Likewise, a Key Vault reference media type proves only that the
entry contains a syntactically valid reference.
Convention namespace, label, and tags define SecretSpec’s management scope; they do not prove SecretSpec created every matching direct entry. External tools must not place mutable direct values in that scope unless SecretSpec may adopt, update, and delete them.
A principal with App Configuration Data Owner or a read-write
connection string can replace a direct value or redirect a Key Vault
reference to another vault beneath the allowed key_vault_suffix.
SecretSpec then follows that URI with its configured Key Vault identity.
Treat App Configuration writers and read-write access keys as trusted
routing administrators, restrict their permissions and distribution, and
limit the Key Vault identity to explicitly required vaults and secrets.
A versionless reference tracks the latest Key Vault version; pin the
32-character version in the stored URI when review or rollout requires a
fixed revision.
Troubleshooting
HTTP 403 from App Configuration
Confirm that the identity selected by auth has App Configuration
Data Reader or App Configuration Data Owner on the store. Azure
control-plane roles do not provide Entra data-plane access. If the
assignment is new, wait for role propagation before retrying. With
auth=env, use auth=cli temporarily to prove whether the signed-in
developer identity behaves differently from the configured service
principal.
Partial service-principal configuration
auth=env accepts all three of tenant_id, client_id, and
client_secret, or none of them. If only part of the triple is present
across provider credentials and AZURE_TENANT_ID, AZURE_CLIENT_ID,
and AZURE_CLIENT_SECRET, SecretSpec fails rather than silently using
another identity. Complete the triple, remove all three inputs to allow
the Azure CLI/azd fallback, or select auth=cli explicitly.
Connection string rejected
auth=connection_string requires the connection_string provider
credential or AZURE_APPCONFIG_CONNECTION_STRING. Its Endpoint must
exactly match the store selected by the provider URI. Use the connection
string from that store, and do not place it directly in the URI.
Key Vault reference cannot be resolved
App Configuration and Key Vault authenticate independently. Confirm that
the Key Vault identity has Key Vault Secrets User, that the vault
host is a direct child of key_vault_suffix, and that the referenced
secret and optional version exist. With App Configuration
connection-string authentication, set an explicit Entra mode such as
key_vault_auth=workload_identity because a connection string cannot
authenticate to Key Vault.
A key is reported as missing
Omitting label selects only the null label; it does not search every
label. All configured tag selectors must also match. prefix applies to
SecretSpec convention keys but is not prepended to a native ref.item,
which names the complete existing App Configuration key.
Limitations
- One provider instance resolves references from at most 16 distinct Key Vault hosts. Split larger sets across provider aliases.
- Feature flags, snapshot references, and unknown Azure App Configuration special content types are rejected. Only direct values and canonical Key Vault references are resolved.
init --fromreflects only SecretSpec convention keys for the selected project, profile, prefix, label, and tags. It does not import arbitrary keys from the store; declare those with read-onlyref.itementries.- Tag selectors require non-empty names and values. Empty values and
Azure null-valued tags cannot be expressed as
tag=NAME=VALUEselectors. - Native
ref.itementries are read-only. Entries containing Key Vault references cannot be written or deleted through this provider, and SecretSpec never writes or deletes the referenced Key Vault secret. - Provider reads do not create an implicit durable cache, refresh loop, or watch subscription. Configure a SecretSpec cache explicitly and run commands again to observe changes.
- No store-wide clear or watch operation is exposed.
secretspec cache clearapplies only to configured SecretSpec cache entries. - SecretSpec honors an App Configuration key’s lock by refusing writes and deletes, but does not create, remove, or manage locks.
- The provider does not assign a native TTL. When Azure App
Configuration is a cache provider, SecretSpec enforces its logical
max_ageand deletes stale entries when encountered; Azure revision history remains separate. - AAC does not currently apply Azure-specific retry/backoff to HTTP 429 or 5xx responses or configure a provider-specific request deadline. Apply an outer command timeout and retry policy where a deployment requires bounded execution. After an indeterminate write or delete network error, read the current entry before retrying the mutation.
Last updated Oct 08, 2026