Operational How-Tos#
Short, copy-pasteable runbooks for recurring operational tasks. Each entry is self-contained; pick the one you need.
Environment naming#
The resource group and App Service names are not symmetric across environments, so double-check both before running a command against production.
Environment |
App Service ( |
Resource group ( |
Key Vault |
|---|---|---|---|
dev |
|
|
|
staging |
|
|
|
prd |
|
|
|
Note the mismatch: the App Service name uses prd, but its resource group is
…-production; and the dev resource group is …-xomnia, not …-dev.
Force refresh of changed Key Vault values#
Secrets reach the App Service as
Key Vault references
(e.g. @Microsoft.KeyVault(SecretUri=…)) in the app settings. App Service
caches the resolved values, so a freshly rotated secret does not take effect
immediately: for a versionless reference the platform refetches the cache on its
own only about every 24 hours,
so a rotation can take up to a day to land unless you force it.
To force an immediate re-read, change any app setting. Per Microsoft, “any configuration change to the app causes an app restart and an immediate refetch of all referenced secrets” — so writing a throwaway setting does the job:
# Substitute -n / -g for your environment from the table above.
az webapp config appsettings set \
-n qfa-prd-backend -g qualitative-feedback-analysis-production \
--settings KV_REFRESH_TOUCH=$(date +%s)
KV_REFRESH_TOUCH is an arbitrary, unused setting; the $(date +%s) value just
guarantees it changes each run, so every save is a real configuration change and
therefore forces the refetch of all references.
Alternatives:
Refresh API — forces re-resolution with no throwaway setting and no restart, by POSTing to the
configreferencesrefresh endpoint:az rest --method post --url \ "https://management.azure.com/subscriptions/<sub-id>/resourceGroups/qualitative-feedback-analysis-production/providers/Microsoft.Web/sites/qfa-prd-backend/config/configreferences/appsettings/refresh?api-version=2022-03-01"
Portal: open the App Service → Settings → Environment variables and add/save any setting. The Portal also shows a per-setting resolution status, which is the quickest way to confirm a reference resolved rather than silently keeping a stale value.
Don’t rely on a bare restart.
az webapp restart(or Portal → Restart) without a configuration change is not documented to re-read Key Vault references, and in practice it often keeps serving the cached values. Force a refresh with a configuration change or the refresh API above — not a restart alone.
Tip — token staleness on the CLI. If
azreturnsAuthorizationFailedforMicrosoft.Web/sites/config/list/actionright after a role was granted (or a PIM role activated), your cached access token predates the grant. Runaz account clear && az loginto mint a fresh token, or use the Portal, which re-authenticates per action.
Versionless vs. pinned secret references#
If a Key Vault reference’s SecretUri ends with a version GUID
(…/secrets/<name>/<version>), App Service is pinned to that exact version and
will never pick up a rotated secret — even after a restart or a
configuration change — until the app setting itself is repointed. Use a
versionless URI (no /<version> suffix, e.g. …/secrets/<name>) so
rotations are picked up by the periodic (~24 h) refresh, or immediately via a
configuration change / the refresh API above. The Terraform-managed references
in app_service.tf are versionless by design.
Resize the App Service plan for an environment#
The plan SKU is per environment: dev/staging on B2, prd on P0v3 (see
ADR-019). Substitute
-n / -g below from the table above.
1. Check the tier exists in that environment’s region first. This is the
step that saves an aborted apply — P0v4, for example, was unavailable in the
region when ADR-019 was
written.
az group show -g qualitative-feedback-analysis-production --query location -o tsv
az appservice list-locations --sku P0V3 --linux-workers-enabled
azwants the SKU upper-cased (P0V3) here; the azurerm provider wantsP0v3. Same tier, different casing — worth twenty minutes if you miss it.
2. Make the change. Edit var.app_service_plan_sku_by_env in
infra/variables.tf and open a PR. CI plans dev only, so a prd-only
change shows no plan diff on the PR — that is expected. To see the real diff,
dispatch the Terraform workflow with command: plan for the target
environment, then again with command: apply. Applies fan out one run per
environment and are never automatic (see
Release flow § Infrastructure changes).
Read the plan output before applying: it must be an in-place update (~) of
azurerm_service_plan.main. If Terraform proposes a replacement (-/+),
stop — replacing the plan detaches and re-attaches the web app, turning a
restart into a real outage.
3. Expect a restart. Scaling moves the site to new workers: the container
re-runs python -m qfa.cli.migrate and reloads the embedding model, so there is
a cold-start gap. With health_check_eviction_time_in_min = 10 and a
severity-1 health-check alert, a prolonged failure pages Teams — apply in a
quiet window and watch /v1/health.
4. Verify against Azure, not state. Plan names are qfa-<env>-plan:
az appservice plan show -n qfa-prd-plan -g qualitative-feedback-analysis-production \
--query "{sku:sku.name, capacity:sku.capacity, tier:sku.tier}"
If the apply fails with a scale-unit / SKU-not-supported error, that is a
real Azure constraint: an existing plan can only be scaled to a tier its scale
unit supports. The remedy is a new azurerm_service_plan in a Pv3-capable
scale unit plus repointing azurerm_linux_web_app.backend.service_plan_id — a
longer outage and a separate PR.