App Configuration
Introduction
Section titled “Introduction”Azure App Configuration is a managed service for centralizing application settings and feature flags. It stores configuration as key-values that can be filtered by label, versioned through revisions, frozen into point-in-time snapshots, and referenced against secrets held in Azure Key Vault. For more information, see What is Azure App Configuration.
LocalStack for Azure provides a local environment for building and testing applications that make use of Azure App Configuration. The supported APIs are available on our API Coverage section, which provides information on the extent of App Configuration’s integration with LocalStack.
Getting started
Section titled “Getting started”This guide is designed for users new to App Configuration and assumes basic knowledge of the Azure CLI and our lstk az proxy. The Observe RBAC enforcement section additionally requires jq to read a claim out of an access token.
Launch LocalStack using your preferred method. For more information, see Introduction to LocalStack for Azure. Once the container is running, enable Azure CLI interception by running:
lstk az start-interceptionThis command points the az CLI away from the public Azure management REST API and toward the LocalStack for Azure emulator API.
To revert this configuration, run:
lstk az stop-interceptionThis reconfigures the az CLI to send commands to the official Azure management REST API.
Create a resource group
Section titled “Create a resource group”Create a resource group to hold all resources created in this guide:
az group create \ --name rg-appconfig-demo \ --location westeurope{ "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-appconfig-demo", "location": "westeurope", "name": "rg-appconfig-demo", "properties": { "provisioningState": "Succeeded" }, "type": "Microsoft.Resources/resourceGroups"}Create an App Configuration store
Section titled “Create an App Configuration store”Create a configuration store. The store name is a DNS label: alphanumerics and hyphens only, between 5 and 50 characters, and globally unique:
az appconfig create \ --name appconfig-demo-localstack \ --resource-group rg-appconfig-demo \ --location westeurope \ --sku Standard \ --retention-days 7 \ --tags environment=demo{ "creationDate": "2026-09-16T14:11:29.455779+00:00", "defaultKeyValueRevisionRetentionPeriodInSeconds": 2592000, "disableLocalAuth": false, "enablePurgeProtection": false, "endpoint": "https://appconfig-demo-localstack.azure.localhost.localstack.cloud:4566", "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-appconfig-demo/providers/Microsoft.AppConfiguration/configurationStores/appconfig-demo-localstack", "location": "westeurope", "name": "appconfig-demo-localstack", "provisioningState": "Succeeded", "resourceGroup": "rg-appconfig-demo", "sku": { "name": "standard" }, "softDeleteRetentionInDays": 7, "tags": { "environment": "demo" }, "type": "Microsoft.AppConfiguration/configurationStores" ...}The endpoint property is the data-plane address that SDKs and configuration providers use verbatim.
Clients should read it back from the service rather than build it from the store name.
Show and list stores
Section titled “Show and list stores”Retrieve a single store:
az appconfig show \ --name appconfig-demo-localstack \ --resource-group rg-appconfig-demo \ --query "{Name:name, Sku:sku.name, Endpoint:endpoint, Retention:softDeleteRetentionInDays}"{ "Endpoint": "https://appconfig-demo-localstack.azure.localhost.localstack.cloud:4566", "Name": "appconfig-demo-localstack", "Retention": 7, "Sku": "standard"}List every store in a resource group:
az appconfig list \ --resource-group rg-appconfig-demo \ --query "[].{Name:name, Location:location, Sku:sku.name, State:provisioningState}" \ --output tableName Location Sku State------------------------- ---------- -------- ---------appconfig-demo-localstack westeurope standard SucceededUpdate a store
Section titled “Update a store”Update the store to change its tags:
az appconfig update \ --name appconfig-demo-localstack \ --resource-group rg-appconfig-demo \ --tags environment=demo owner=platform \ --query "tags"{ "environment": "demo", "owner": "platform"}Manage access keys
Section titled “Manage access keys”Every store is created with four access keys: a primary and secondary pair with read and write access, and a read-only pair. List them:
az appconfig credential list \ --name appconfig-demo-localstack \ --resource-group rg-appconfig-demo \ --query "[].{Name:name, Id:id, ReadOnly:readOnly}" \ --output tableName Id ReadOnly------------------- ---------------------- ----------Primary MujyyVBRNGLykfLG3RkB1A FalseSecondary DfbAmyIZEVAx9iE6KnBt9A FalsePrimary Read Only wxWGWZrgcR7VP8oyubuQ8Q TrueSecondary Read Only LarYNCfPTx0XPIFzNsj_8g TrueEach key carries a ready-to-use connection string in the form Endpoint=<endpoint>;Id=<id>;Secret=<secret>.
Capture the secondary key’s identifier and regenerate it:
SECONDARY_KEY_ID=$(az appconfig credential list \ --name appconfig-demo-localstack \ --resource-group rg-appconfig-demo \ --query "[?name=='Secondary'].id | [0]" \ --output tsv)
az appconfig credential regenerate \ --name appconfig-demo-localstack \ --resource-group rg-appconfig-demo \ --id="$SECONDARY_KEY_ID" \ --query "{Name:name, Id:id, ReadOnly:readOnly}"{ "Id": "hSS0LdDUJPo7oR8BXT3BdA", "Name": "Secondary", "ReadOnly": false}Regenerating a key replaces the whole credential for that slot: both the identifier and the secret change, while the name and the read-only flag are preserved. Azure behaves the same way, so the identifier in the output above differs from the one that was listed. The other three keys are unaffected, so a running application using the primary key keeps working.
Set and read key-values
Section titled “Set and read key-values”A key-value is identified by its key and an optional label. The same key can carry a different value under each label, which is how per-environment configuration is modelled:
az appconfig kv set \ --name appconfig-demo-localstack \ --key "app/settings/color" \ --label production \ --value blue \ --content-type "text/plain" \ --tags tier=frontend \ --yes{ "contentType": "text/plain", "etag": "fba4697ee4724387aa9b15f061d9b433", "key": "app/settings/color", "label": "production", "lastModified": "2026-09-16T14:11:33.819984+00:00", "locked": false, "tags": { "tier": "frontend" }, "value": "blue"}Read a single key-value back:
az appconfig kv show \ --name appconfig-demo-localstack \ --key "app/settings/color" \ --label production{ "contentType": "text/plain", "etag": "fba4697ee4724387aa9b15f061d9b433", "key": "app/settings/color", "label": "production", "lastModified": "2026-09-16T14:11:33.819984+00:00", "locked": false, "tags": { "tier": "frontend" }, "value": "blue"}Add a second key-value so the listing below has more than one entry:
az appconfig kv set \ --name appconfig-demo-localstack \ --key "app/settings/size" \ --label production \ --value large \ --yes{ "contentType": "", "etag": "75505fc1d5004e009e85563ec9b32f0c", "key": "app/settings/size", "label": "production", "lastModified": "2026-09-16T14:11:34.703800+00:00", "locked": false, "tags": {}, "value": "large"}List key-values, filtering by a key prefix. Both --key and --label accept a star as a wildcard, and --label also accepts a comma-separated list:
az appconfig kv list \ --name appconfig-demo-localstack \ --key "app/settings/*" \ --label production \ --all \ --output tableCONTENT TYPE KEY VALUE LAST MODIFIED TAGS LABEL LOCKED-------------- ------------------ ------- -------------------- -------------------- ---------- --------text/plain app/settings/color blue 2026-09-16T14:11:33Z {'tier': 'frontend'} production False app/settings/size large 2026-09-16T14:11:34Z {} production FalseEvery write creates a new revision. Update app/settings/color:
az appconfig kv set \ --name appconfig-demo-localstack \ --key "app/settings/color" \ --label production \ --value red \ --yes \ --query "value""red"Both versions are now visible, newest first:
az appconfig revision list \ --name appconfig-demo-localstack \ --key "app/settings/color" \ --label production \ --all \ --output tableCONTENT TYPE KEY VALUE LAST MODIFIED TAGS LABEL LOCKED-------------- ------------------ ------- -------------------- -------------------- ---------- --------text/plain app/settings/color red 2026-09-16T14:11:37Z {'tier': 'frontend'} production Falsetext/plain app/settings/color blue 2026-09-16T14:11:33Z {'tier': 'frontend'} production FalseLock and unlock a key-value
Section titled “Lock and unlock a key-value”Locking a key-value makes it read-only, which protects a setting from accidental change:
az appconfig kv lock \ --name appconfig-demo-localstack \ --key "app/settings/color" \ --label production \ --yes \ --query "locked"trueA write to a locked key-value is rejected:
az appconfig kv set \ --name appconfig-demo-localstack \ --key "app/settings/color" \ --label production \ --value green \ --yesERROR: Failed to update read only key-value. Unlock the key-value before updating it.Unlock it to allow writes again:
az appconfig kv unlock \ --name appconfig-demo-localstack \ --key "app/settings/color" \ --label production \ --yes \ --query "locked"falseRead key-values as of a past instant
Section titled “Read key-values as of a past instant”Because every write is retained as a revision, the store can be read as it stood at an earlier moment.
Capture the current key-value’s timestamp and trim it to whole seconds, which is the only precision --datetime accepts:
LAST_MODIFIED=$(az appconfig kv show \ --name appconfig-demo-localstack \ --key "app/settings/color" \ --label production \ --query lastModified \ --output tsv)
CUTOFF="${LAST_MODIFIED%%.*}"CUTOFF="${CUTOFF%%+*}Z"echo "$CUTOFF"2026-09-16T14:11:37ZWait for the next second before writing again. A whole-second cutoff covers the whole of that second, so a write made inside it would fall within the window being queried and would be returned instead of the earlier value:
sleep 1Change the value, so there is something to look back past:
az appconfig kv set \ --name appconfig-demo-localstack \ --key "app/settings/color" \ --label production \ --value amber \ --yes \ --query "value""amber"Pass the captured instant to --datetime to read the store as it stood before that change:
az appconfig kv list \ --name appconfig-demo-localstack \ --key "app/settings/color" \ --label production \ --datetime "$CUTOFF" \ --output tableCONTENT TYPE KEY VALUE LAST MODIFIED TAGS LABEL LOCKED-------------- ------------------ ------- -------------------- -------------------- ---------- --------text/plain app/settings/color red 2026-09-16T14:11:37Z {'tier': 'frontend'} production FalseThe same listing without --datetime returns the current value:
az appconfig kv list \ --name appconfig-demo-localstack \ --key "app/settings/color" \ --label production \ --output tableCONTENT TYPE KEY VALUE LAST MODIFIED TAGS LABEL LOCKED-------------- ------------------ ------- -------------------- -------------------- ---------- --------text/plain app/settings/color amber 2026-09-16T14:11:42Z {'tier': 'frontend'} production FalseCreate and manage snapshots
Section titled “Create and manage snapshots”A snapshot freezes the key-values matching a filter at the moment it is created, giving an application a configuration set that cannot shift underneath it:
az appconfig snapshot create \ --name appconfig-demo-localstack \ --snapshot-name baseline \ --filters '{"key":"app/settings/*","label":"production"}'{ "compositionType": "key", "created": "2026-09-16T14:11:43.730861+00:00", "etag": "49c270f7426ed076c6387c4bdc45afaa", "expires": null, "filters": [ { "key": "app/settings/*", "label": "production" } ], "itemsCount": 2, "name": "baseline", "retentionPeriod": 2592000, "size": 1000, "status": "ready", "tags": {}}List the snapshots on a store:
az appconfig snapshot list \ --name appconfig-demo-localstack \ --query "[].{Name:name, Status:status, Items:itemsCount, Created:created}" \ --output tableName Status Items Created-------- -------- ------- --------------------------------baseline ready 2 2026-09-16T14:11:43.730861+00:00A snapshot is materialized once, when it is created, so changing a key-value afterwards does not affect it.
Change app/settings/color again:
az appconfig kv set \ --name appconfig-demo-localstack \ --key "app/settings/color" \ --label production \ --value violet \ --yes \ --query "value""violet"The store now holds violet, but listing through the snapshot still returns the value captured when it was created:
az appconfig kv list \ --name appconfig-demo-localstack \ --snapshot baseline \ --output tableCONTENT TYPE KEY VALUE LAST MODIFIED TAGS LABEL LOCKED-------------- ------------------ ------- -------------------- -------------------- ---------- --------text/plain app/settings/color amber 2026-09-16T14:11:42Z {'tier': 'frontend'} production False app/settings/size large 2026-09-16T14:11:34Z {} production FalseArchive a snapshot to mark it for expiry, and recover it to cancel that:
az appconfig snapshot archive \ --name appconfig-demo-localstack \ --snapshot-name baseline \ --query "{Status:status, Expires:expires}"{ "Expires": "2026-10-16T14:11:53.597151+00:00", "Status": "archived"}az appconfig snapshot recover \ --name appconfig-demo-localstack \ --snapshot-name baseline \ --query "{Status:status, Expires:expires}"{ "Expires": null, "Status": "ready"}An archived snapshot still serves its contents. Archiving starts the retention clock; recovering stops it.
Reference a Key Vault secret
Section titled “Reference a Key Vault secret”Rather than storing a secret in App Configuration, store a reference to it in Key Vault.
Create a vault that uses Azure RBAC, which is the permission model the Key Vault Secrets User role requires:
az keyvault create \ --name kv-appconfig-demo \ --resource-group rg-appconfig-demo \ --location westeurope \ --enable-rbac-authorization true \ --retention-days 7 \ --query "{Name:name, Uri:properties.vaultUri, Rbac:properties.enableRbacAuthorization}"{ "Name": "kv-appconfig-demo", "Rbac": true, "Uri": "https://kv-appconfig-demo.vault.azure.localhost.localstack.cloud:4566"}Writing a secret to an RBAC-enabled vault requires the Key Vault Secrets Officer role on the vault.
Resolve the object ID of the principal you are signed in as, then grant it that role:
KEY_VAULT_ID=$(az keyvault show \ --name kv-appconfig-demo \ --resource-group rg-appconfig-demo \ --query id \ --output tsv)
CALLER_OBJECT_ID=$(az ad signed-in-user show --query id --output tsv 2>/dev/null \ || az ad sp show --id "$(az account show --query user.name --output tsv)" --query id --output tsv)
az role assignment create \ --assignee-object-id "$CALLER_OBJECT_ID" \ --assignee-principal-type ServicePrincipal \ --role "Key Vault Secrets Officer" \ --scope "$KEY_VAULT_ID" \ --query "roleDefinitionName" \ --output tsvKey Vault Secrets OfficerWrite the secret and capture the identifier it returns:
SECRET_ID=$(az keyvault secret set \ --vault-name kv-appconfig-demo \ --name db-password \ --value "P@ssw0rd-from-key-vault" \ --query "id" \ --output tsv)
echo "$SECRET_ID"https://kv-appconfig-demo.vault.azure.localhost.localstack.cloud:4566/secrets/db-password/587b6bfb287d49a9bc498eca45f61cd0Create the reference using that secret identifier:
az appconfig kv set-keyvault \ --name appconfig-demo-localstack \ --key "app/secrets/db-password" \ --label production \ --secret-identifier "$SECRET_ID" \ --yes{ "contentType": "application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8", "etag": "2c01f41547944aecad34f963c5477324", "key": "app/secrets/db-password", "label": "production", "lastModified": "2026-09-16T14:11:57.108598+00:00", "locked": false, "tags": {}, "value": "{\"uri\": \"https://kv-appconfig-demo.vault.azure.localhost.localstack.cloud:4566/secrets/db-password/587b6bfb287d49a9bc498eca45f61cd0\"}"}A Key Vault reference is an ordinary key-value whose content type is application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8 and whose value is a JSON document holding the secret identifier.
Reading it back plainly returns that identifier, not the secret.
Pass --resolve-keyvault to have the CLI fetch the secret from Key Vault with your credentials:
az appconfig kv list \ --name appconfig-demo-localstack \ --key "app/secrets/db-password" \ --label production \ --resolve-keyvault \ --all \ --output tableCONTENT TYPE KEY VALUE LAST MODIFIED TAGS LABEL LOCKED------------------------------------------------------------------ ----------------------- ----------------------- -------------------- ------ ---------- --------application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8 app/secrets/db-password P@ssw0rd-from-key-vault 2026-09-16T14:11:57Z {} production FalseControl data-plane access with RBAC
Section titled “Control data-plane access with RBAC”An application should read configuration with its own identity rather than an access key. Create a user-assigned managed identity and capture its principal ID, which is what role assignments are evaluated against:
az identity create \ --name id-appconfig-demo \ --resource-group rg-appconfig-demo \ --location westeurope \ --query "{Name:name, ClientId:clientId, PrincipalId:principalId}"{ "ClientId": "5886b11c-c8aa-41c7-b5e4-28a74405fd3e", "Name": "id-appconfig-demo", "PrincipalId": "6828983e-8850-42cc-904a-c1e04dce057f"}IDENTITY_PRINCIPAL_ID=$(az identity show \ --name id-appconfig-demo \ --resource-group rg-appconfig-demo \ --query principalId \ --output tsv)
STORE_ID=$(az appconfig show \ --name appconfig-demo-localstack \ --resource-group rg-appconfig-demo \ --query id \ --output tsv)Grant the identity the App Configuration Data Reader role at the scope of the store.
The assignment targets the principal ID, not the client ID:
az role assignment create \ --assignee-object-id "$IDENTITY_PRINCIPAL_ID" \ --assignee-principal-type ServicePrincipal \ --role "App Configuration Data Reader" \ --scope "$STORE_ID" \ --query "roleDefinitionName" \ --output tsvApp Configuration Data ReaderVerify the assignment:
az role assignment list \ --assignee "$IDENTITY_PRINCIPAL_ID" \ --scope "$STORE_ID" \ --query "[].{Role:roleDefinitionName, PrincipalType:principalType}" \ --output tableRole PrincipalType----------------------------- ----------------App Configuration Data Reader ServicePrincipalAn application running in Azure obtains a token for this identity from the instance metadata service, and that token carries the principal ID above.
Observe RBAC enforcement
Section titled “Observe RBAC enforcement”By default LocalStack stores role assignments without enforcing them, so every data-plane request succeeds. Enforcement is enabled by an environment variable read once at startup, so it requires restarting the emulator, which clears the in-memory state created so far. The sequence below is therefore self-contained: it starts from a fresh emulator and creates its own resource group and store.
Restart the emulator with enforcement enabled, then re-enable interception:
localstack stopIMAGE_NAME=localstack/localstack-azure localstack start -d -e LS_AZURE_ENFORCE_RBAC=1lstk az start-interceptionCreate a store and seed a key-value using an access key, which is never subject to RBAC:
az group create --name rg-appconfig-rbac-demo --location westeurope --output none
az appconfig create \ --name appconfig-rbac-localstack \ --resource-group rg-appconfig-rbac-demo \ --location westeurope \ --sku Standard \ --output none
az appconfig kv set \ --name appconfig-rbac-localstack \ --key "app/settings/color" \ --value blue \ --auth-mode key \ --yes \ --query "value""blue"Capture the store’s identifiers, then sign in as a second service principal that holds no administrative role. The Azure CLI cannot acquire a managed identity token outside Azure compute, so a plain service principal stands in for the workload here:
STORE_ID=$(az appconfig show \ --name appconfig-rbac-localstack \ --resource-group rg-appconfig-rbac-demo \ --query id \ --output tsv)
STORE_ENDPOINT=$(az appconfig show \ --name appconfig-rbac-localstack \ --resource-group rg-appconfig-rbac-demo \ --query endpoint \ --output tsv)
az login --service-principal -u appcs-workload -p any-pass --tenant any-tenant --output noneRead a key-value through the data-plane endpoint with --auth-mode login. A caller holding no data role is refused:
az appconfig kv show \ --endpoint "$STORE_ENDPOINT" \ --key "app/settings/color" \ --auth-mode loginERROR: Failed to retrieve key-values from config store. Operation returned an invalid status 'FORBIDDEN'Content: {"type": "https://azconfig.io/errors/forbidden", "title": "Forbidden.", "detail": "The principal 'b70f3654-c056-4bfa-8c6d-620e27ff4a92' does not have the required permission 'Microsoft.AppConfiguration/configurationStores/keyValues/read' on '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-appconfig-rbac-demo/providers/Microsoft.AppConfiguration/configurationStores/appconfig-rbac-localstack'.", "status": 403}The denial names the principal, the permission it lacked, and the scope it was evaluated at.
Capture that principal’s object ID from the oid claim of its own access token, which is what a role assignment must target:
WORKLOAD_OBJECT_ID=$(az account get-access-token --query accessToken --output tsv \ | jq -Rr 'split(".")[1] | @base64d | fromjson | .oid')
echo "$WORKLOAD_OBJECT_ID"b70f3654-c056-4bfa-8c6d-620e27ff4a92Sign back in as the administrative principal and grant the workload the App Configuration Data Reader role:
az login --service-principal -u any-app -p any-pass --tenant any-tenant --output none
az role assignment create \ --assignee-object-id "$WORKLOAD_OBJECT_ID" \ --assignee-principal-type ServicePrincipal \ --role "App Configuration Data Reader" \ --scope "$STORE_ID" \ --query "roleDefinitionName" \ --output tsvApp Configuration Data ReaderSign back in as the workload. The read now succeeds:
az login --service-principal -u appcs-workload -p any-pass --tenant any-tenant --output none
az appconfig kv show \ --endpoint "$STORE_ENDPOINT" \ --key "app/settings/color" \ --query "value" \ --output tsv \ --auth-mode loginblueA write is still refused, because App Configuration Data Reader grants no write permission:
az appconfig kv set \ --endpoint "$STORE_ENDPOINT" \ --key "app/settings/color" \ --value red \ --auth-mode login \ --yesERROR: Failed to set the key-value due to an exception: Operation returned an invalid status 'FORBIDDEN'Content: {"type": "https://azconfig.io/errors/forbidden", "title": "Forbidden.", "detail": "The principal 'b70f3654-c056-4bfa-8c6d-620e27ff4a92' does not have the required permission 'Microsoft.AppConfiguration/configurationStores/keyValues/write' on '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-appconfig-rbac-demo/providers/Microsoft.AppConfiguration/configurationStores/appconfig-rbac-localstack'.", "status": 403}Assigning App Configuration Data Owner instead allows both reads and writes:
az login --service-principal -u any-app -p any-pass --tenant any-tenant --output none
az role assignment create \ --assignee-object-id "$WORKLOAD_OBJECT_ID" \ --assignee-principal-type ServicePrincipal \ --role "App Configuration Data Owner" \ --scope "$STORE_ID" \ --query "roleDefinitionName" \ --output tsv
az login --service-principal -u appcs-workload -p any-pass --tenant any-tenant --output none
az appconfig kv set \ --endpoint "$STORE_ENDPOINT" \ --key "app/settings/color" \ --value red \ --auth-mode login \ --yes \ --query "value""red"Access keys are not affected by role assignments. A client authenticating with a connection string is authenticated by the key it signed with and is never evaluated against RBAC, exactly as in Azure.
Sign back in as the administrative principal before cleaning up, because the workload principal is not authorized to delete these resources:
az login --service-principal -u any-app -p any-pass --tenant any-tenant --output none
az appconfig delete \ --name appconfig-rbac-localstack \ --resource-group rg-appconfig-rbac-demo \ --yes
az appconfig purge --name appconfig-rbac-localstack --yes
az group delete --name rg-appconfig-rbac-demo --yesDelete and verify
Section titled “Delete and verify”Delete an individual key-value:
az appconfig kv delete \ --name appconfig-demo-localstack \ --key "app/settings/size" \ --label production \ --yes \ --query "[].key"[ "app/settings/size"]Delete the store. On the Standard tier this is a soft delete:
az appconfig delete \ --name appconfig-demo-localstack \ --resource-group rg-appconfig-demo \ --yesThe store no longer appears in az appconfig list, but it is retained until its scheduled purge date:
az appconfig show-deleted --name appconfig-demo-localstack{ "configurationStoreId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-appconfig-demo/providers/Microsoft.AppConfiguration/configurationStores/appconfig-demo-localstack", "deletionDate": "2026-09-16T14:12:02.287739+00:00", "id": "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.AppConfiguration/locations/westeurope/deletedConfigurationStores/appconfig-demo-localstack", "location": "westeurope", "name": "appconfig-demo-localstack", "purgeProtectionEnabled": false, "scheduledPurgeDate": "2026-09-23T14:12:02.287739+00:00", "systemData": null, "tags": { "environment": "demo", "owner": "platform" }, "type": "Microsoft.AppConfiguration/deletedConfigurationStores"}Restore it with az appconfig recover, which takes no resource group because a deleted store is addressed at subscription scope:
az appconfig recover --name appconfig-demo-localstack --yesTo remove a store permanently, delete it and then purge it. A soft-deleted store keeps its globally unique name reserved, so purging is what allows the same name to be reused:
az appconfig delete \ --name appconfig-demo-localstack \ --resource-group rg-appconfig-demo \ --yes
az appconfig purge --name appconfig-demo-localstack --yesFinally, remove the resource group and confirm it is gone:
az group delete --name rg-appconfig-demo --yes
az group exists --name rg-appconfig-demofalseFeatures
Section titled “Features”The App Configuration emulator supports the following features:
- Configuration stores: Create, get, list by subscription and by resource group, update, and delete stores, across the
Free,Developer,Standard, andPremiumSKUs. - Soft delete and purge: The full lifecycle of deleting, listing and showing deleted stores, recovering them, and purging them, including purge protection and a configurable retention period.
- Access keys: List the four access keys of a store, regenerate any of them individually, and use the generated connection strings.
- Key-values: Set, show, list, and delete key-values, with content types, tags, and labels.
- Filtering: Filter listings by key and label, using exact values, star wildcards, or comma-separated lists, and project fields with
--fields. - Revisions: Every write is retained as a revision, listed newest first and trimmed according to the store’s retention period.
- Point-in-time reads: Read key-values, keys, labels, and revisions as they stood at an earlier instant with
--datetime. - Snapshots: Create snapshots over a key and label filter, show and list them, archive and recover them, and list a snapshot’s frozen contents.
- Locks: Lock a key-value to make it read-only and unlock it to restore writes.
- Feature flags: Set, show, list, enable, and disable feature flags, including percentage and other filters, stored as key-values under the
.appconfig.featureflag/prefix. - Key Vault references: Store references to Key Vault secrets with the standard reference content type, resolved client-side by the CLI or a configuration provider.
- Import and export: Move key-values between a store and a file with
az appconfig kv importandaz appconfig kv export. - Data-plane RBAC: Evaluate
App Configuration Data ReaderandApp Configuration Data Owneragainst bearer callers, including managed identities, when enforcement is enabled. - Access key authentication: Requests signed with an access key are accepted, and a read-only key is refused write operations.
- Replicas: Create, get, list, and delete replicas. A replica endpoint serves the contents of its parent store.
- Private endpoints: The full Azure Resource Manager lifecycle of private endpoint connections, driven by
Microsoft.Network, including auto-approval, rejection, and cascading deletion. - Pagination and conditional requests: Cursor-based pagination over large listings, ETag preconditions on individual key-values, conditional list requests, and
Rangerequests over revisions.
Limitations
Section titled “Limitations”- Data-plane RBAC is not enforced by default: Role assignments are stored but every request succeeds. Set
LS_AZURE_ENFORCE_RBACwhen starting the emulator to enforce them. See Role Assignment: Enabling RBAC enforcement. - Access key signatures are not verified: The credential identifier in a request is resolved and checked, but the signature itself, the date skew, and the content hash are not. The emulator accepts requests that Azure would reject, never the reverse.
- Private endpoints do not isolate traffic: Connections are created, approved, rejected, and reported on the store, but no request is blocked and
publicNetworkAccessis left as the client set it. The per-tier limit on the number of connections is not enforced. - Replicas do not replicate: A replica endpoint serves the same single store, so there is no replication lag, no regional failover, and no region-specific outage.
- Snapshot behavior differs in several details: Azure Resource Manager exposes only snapshot creation and retrieval, so listing, archiving, and recovering are available on the data plane alone. A snapshot filter is returned without the empty
tagsfield that Azure includes. Snapshot creation is synchronous, so a snapshot is alreadyreadyin the response rather than reaching that state asynchronously, and the reportedsizeis approximate. - Customer-managed keys are cosmetic: The
encryption.keyVaultPropertiessettings are stored and returned, but no data is encrypted with them. - Not implemented: Event Grid filters, private endpoint connection proxies, network security perimeter configurations, and request throttling.
- The
api-versionparameter is required: Data-plane requests that omit it are rejected, while Azure serves them. This makes the emulator stricter than the service it emulates. - Terraform cannot manage key-values: The
azurerm_app_configuration_keyandazurerm_app_configuration_featureresources require an App Configuration domain suffix that the Azure Resource Manager metadata document does not carry, so the provider cannot resolve it for any non-public environment. Use the Azure CLI or Bicep to manage key-values. Theazurerm_app_configurationstore resource itself works. - No data persistence across restarts: Store, key-value, revision, and snapshot data is held in memory and is lost when the LocalStack emulator is stopped or restarted.
Samples
Section titled “Samples”The following samples demonstrate how to use Azure App Configuration with LocalStack for Azure:
- Web App, App Configuration, and Key Vault (Python)
- Web App, App Configuration, and Key Vault (.NET)
- Azure Kubernetes Service, App Configuration, and Key Vault (Python)
- Azure Kubernetes Service, App Configuration, and Key Vault (.NET)
API Coverage
Section titled “API Coverage”| Operation ▲ | Implemented ▼ |
|---|