You are on call, an app team says "Azure OpenAI is broken", and you need answers fast: which deployments exist, what model version they run, how much quota is left, whether keys are enabled, and what the metrics say. This page collects the commands for those checks in one place.
Set these once per shell session:
RG=rg-ai-prod
ACCT=aoai-prod-swc
LOC=swedencentral
ID=$(az cognitiveservices account show -g $RG -n $ACCT --query id -o tsv)
Tip
Some newer subcommands (cost controls, agents, managed networks) come from preview extensions or recent CLI releases. Run
az versionandaz upgradeif a command in this page is missing.
Accounts
# All Azure OpenAI and AI Services accounts in the subscription
az cognitiveservices account list \
--query "[?kind=='OpenAI' || kind=='AIServices'].{name:name, rg:resourceGroup, kind:kind, location:location}" -o table
# Endpoint, network and auth settings for one account
az cognitiveservices account show -g $RG -n $ACCT \
--query "{endpoint:properties.endpoint, publicAccess:properties.publicNetworkAccess, localAuthDisabled:properties.disableLocalAuth, customDomain:properties.customSubDomainName}"
Soft-deleted accounts
A deleted account stays soft-deleted for a retention period and holds its name. If someone deleted the wrong one, recover it before you redeploy anything.
az cognitiveservices account list-deleted -o table
az cognitiveservices account recover -g $RG -n $ACCT -l $LOC
Caution
az cognitiveservices account purgeremoves a soft-deleted account permanently, including its deployments. Only purge when you are sure nothing needs to be recovered.
Deployments
# What is deployed: model, version, SKU (deployment type), capacity, upgrade policy
az cognitiveservices account deployment list -g $RG -n $ACCT \
--query "[].{deployment:name, model:properties.model.name, version:properties.model.version, sku:sku.name, capacity:sku.capacity, upgrade:properties.versionUpgradeOption, state:properties.provisioningState}" -o table
# Full detail for one deployment, including rate limits
az cognitiveservices account deployment show -g $RG -n $ACCT --deployment-name chat-prod
The sku.name value is the deployment type (Standard, GlobalStandard, DataZoneStandard, ProvisionedManaged and so on). For token-based SKUs, sku.capacity is in units of 1,000 tokens per minute; for provisioned SKUs it is PTUs.
Create or delete a deployment
az cognitiveservices account deployment create -g $RG -n $ACCT \
--deployment-name chat-canary \
--model-format OpenAI --model-name gpt-4.1 --model-version 2025-04-14 \
--sku-name GlobalStandard --sku-capacity 50
az cognitiveservices account deployment delete -g $RG -n $ACCT --deployment-name chat-canary
Model names and versions available to you depend on region and account; take them from the list commands below rather than from memory. Provisioned deployments can use --spillover-deployment-name to send overflow to a standard deployment.
Warning
Deleting a deployment breaks every app that calls it immediately. Search your app config for the deployment name first, and check the
AzureOpenAIRequestsmetric for that deployment over the last 24 hours.
Models and quota
# Models this account can deploy, with lifecycle and deprecation dates
az cognitiveservices account list-models -g $RG -n $ACCT \
--query "[?format=='OpenAI'].{model:name, version:version, lifecycle:lifecycleStatus, retire:deprecation.inference}" -o table
# Models offered in a region
az cognitiveservices model list -l $LOC \
--query "[?model.format=='OpenAI'].{model:model.name, version:model.version, kind:kind}" -o table
# Quota per region: used vs limit for each model and deployment type
az cognitiveservices usage list -l $LOC \
--query "[?currentValue > \`0\`].{quota:name.value, used:currentValue, limit:limit}" -o table
Quota names look like OpenAI.GlobalStandard.gpt-4.1; the exact format varies by deployment type, so check your output. Quota is per subscription and region, so two accounts in the same region share it.
Keys and Entra ID
# Show keys (only if local auth is enabled)
az cognitiveservices account keys list -g $RG -n $ACCT
# Regenerate one key; apps on the other key keep working
az cognitiveservices account keys regenerate -g $RG -n $ACCT --key-name key2
# Disable key auth so only Entra ID tokens work
az resource update --ids $ID --set properties.disableLocalAuth=true
Caution
Setting
disableLocalAuth=truebreaks every client still using anapi-keyheader. Confirm in the logs that no key-based calls remain before you switch it.
Role assignments for an app's managed identity:
az role assignment create --assignee <principal-id> \
--role "Cognitive Services OpenAI User" --scope $ID
az role assignment list --scope $ID \
--query "[].{who:principalName, role:roleDefinitionName}" -o table
Network rules
az cognitiveservices account network-rule list -g $RG -n $ACCT
az cognitiveservices account network-rule add -g $RG -n $ACCT --ip-address 203.0.113.10
az cognitiveservices account network-rule remove -g $RG -n $ACCT --ip-address 203.0.113.10
# Private endpoints attached to the account
az network private-endpoint-connection list --id $ID \
--query "[].{name:name, status:properties.privateLinkServiceConnectionState.status}" -o table
Test call from the shell
This confirms that DNS, network, auth and the deployment all work, independent of the app.
ENDPOINT=$(az cognitiveservices account show -g $RG -n $ACCT --query properties.endpoint -o tsv)
TOKEN=$(az account get-access-token --resource https://cognitiveservices.azure.com --query accessToken -o tsv)
curl -sS -i "${ENDPOINT}openai/deployments/chat-prod/chat/completions?api-version=2024-10-21" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"Reply with OK"}],"max_tokens":5}'
Look at the status code and the x-ratelimit-remaining-tokens and x-ratelimit-remaining-requests headers. Your identity needs a data-plane role such as Cognitive Services OpenAI User; Owner or Contributor alone is not enough for token-based calls.
| Response | Likely cause | First check |
|---|---|---|
| 401 | Wrong token audience, expired key, or local auth disabled | disableLocalAuth, token --resource value |
| 403 | Network rules or private endpoint only, or missing data-plane role | publicNetworkAccess, role assignments |
404 DeploymentNotFound | Deployment name typo or deployment deleted | deployment list |
| 429 | Deployment TPM/RPM or PTU utilisation exhausted | AzureOpenAIRequests by StatusCode, PTU utilisation |
400 content_filter | Content filter blocked prompt or output | RAIRejectedRequests metric |
Metrics
Use the Azure OpenAI metrics, not the legacy Cognitive Services ones such as Latency, TotalCalls or ClientErrors.
| Question | Metric | Useful dimensions |
|---|---|---|
| Requests and errors | AzureOpenAIRequests | ModelDeploymentName, StatusCode, IsSpillover |
| Input / output tokens | ProcessedPromptTokens, GeneratedTokens | ModelDeploymentName |
| Total tokens | TokenTransaction | ModelDeploymentName |
| PTU usage | AzureOpenAIProvisionedManagedUtilizationV2 | ModelDeploymentName |
| Latency | AzureOpenAITimeToResponse, AzureOpenAITTLTInMS | ModelDeploymentName |
| Content filter blocks | RAIRejectedRequests | ModelDeploymentName, Category |
# Requests per deployment and status code, 5-minute buckets, last 3 hours
az monitor metrics list --resource $ID --metric AzureOpenAIRequests \
--aggregation Total --interval PT5M --offset 3h \
--filter "ModelDeploymentName eq '*' and StatusCode eq '*'" -o table
# Peak PTU utilisation per deployment, last 24 hours
az monitor metrics list --resource $ID --metric AzureOpenAIProvisionedManagedUtilizationV2 \
--aggregation Maximum --interval PT1H --offset 24h \
--filter "ModelDeploymentName eq '*'" -o table
Diagnostic logs and KQL
Resource logs are not collected until you add a diagnostic setting. The categories are Audit, RequestResponse, Trace and AzureOpenAIRequestUsage (check your account's list, as it changes). They land in the AzureDiagnostics table.
WS=$(az monitor log-analytics workspace show -g rg-monitor -n law-prod --query id -o tsv)
az monitor diagnostic-settings create --name aoai-to-law --resource $ID --workspace $WS \
--logs '[{"category":"Audit","enabled":true},{"category":"RequestResponse","enabled":true}]' \
--metrics '[{"category":"AllMetrics","enabled":true}]'
Errors by operation and result code:
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.COGNITIVESERVICES"
| where Category == "RequestResponse"
| where TimeGenerated > ago(3h)
| summarize calls = count() by OperationName, ResultSignature, bin(TimeGenerated, 5m)
| order by TimeGenerated desc
Token usage per deployment from exported metrics:
AzureMetrics
| where ResourceProvider == "MICROSOFT.COGNITIVESERVICES"
| where MetricName in ("ProcessedPromptTokens", "GeneratedTokens")
| where TimeGenerated > ago(1d)
| summarize tokens = sum(Total) by MetricName, bin(TimeGenerated, 1h)
Who changed a deployment or key (control plane):
AzureActivity
| where ResourceProviderValue =~ "MICROSOFT.COGNITIVESERVICES"
| where OperationNameValue has_any ("deployments/write", "deployments/delete", "regenerateKey", "accounts/write")
| project TimeGenerated, Caller, OperationNameValue, ActivityStatusValue, _ResourceId
| order by TimeGenerated desc
Prevent it next time
- Keep a script that runs the deployment and quota queries above weekly and posts the output to your team channel, so you spot retiring versions and quota pressure early.
- Create the diagnostic setting with Azure Policy so every new account sends
AuditandRequestResponselogs to Log Analytics from day one. - Add metric alerts on
AzureOpenAIRequestsfiltered toStatusCode429 and 5xx, and on PTU utilisation above your chosen threshold. - Move apps to Entra ID and set
disableLocalAuth=trueonce the logs show no key-based callers remain. - Store the
curltest call in your runbook so the first responder can separate "Azure OpenAI is down" from "our app is broken" in under a minute.