Azure CLI Operations
Guidance for using the Azure Command-Line Interface effectively and safely.
Current version: Azure CLI 2.83.0 (released 2026-02-03). MFA is mandatory for user identity logins since October 2025.
Pre-Flight: Always Verify Context
Before executing ANY Azure CLI command, verify subscription and identity:
az account show --query "{Sub:name, ID:id, Tenant:tenantId}" --output table
Never assume which subscription is active. Environment variables, config defaults, and recent az account set calls can silently change the target.
Core Principles
- Verify before mutating — Always
az account showbefore write/delete operations - What-if first — Use
az deployment group what-ifbefore ARM/Bicep deployments; no native--dry-runfor individualazcommands - Query server-side — Use
--query(JMESPath) to reduce output; useaz graph query(KQL) for cross-subscription inventory - Use
tsvfor scripting —--output tsvgives clean values without quotes;--output nonewhen only exit code matters - Pin subscription explicitly — Always pass
--subscriptionin scripts; never rely on the default context - Use
--only-show-errors— Suppress warnings in scripts; let errors flow to stderr - Prefer managed identity —
az login --identityfor Azure-hosted workloads; service principal with certificate for CI/CD; federated credentials (OIDC) for GitHub Actions - Resource locks for safety —
az lock create --lock-type CanNotDeleteon production resource groups
Essential Command Patterns
Resource Discovery
# List all VMs with power state
az vm list --show-details \
--query "[].{Name:name,RG:resourceGroup,State:powerState,Size:hardwareProfile.vmSize}" \
--output table
# Find resources by tag across the subscription
az resource list \
--tag Environment=production \
--query "[].{Name:name,Type:type,RG:resourceGroup}" --output table
# Cross-subscription inventory via Resource Graph (KQL)
az graph query -q \
"Resources | where type =~ 'Microsoft.Compute/virtualMachines' | project name, location, resourceGroup | order by name"
Safe Mutation Pattern
# Step 1: Verify subscription
SUB=$(az account show --query name --output tsv)
echo "Subscription: $SUB"
# Step 2: What-if for Bicep/ARM deployments
az deployment group what-if \
--resource-group myRG \
--template-file main.bicep \
--parameters @params.json
# Step 3: Deploy with confirmation
az deployment group create \
--resource-group myRG \
--template-file main.bicep \
--confirm-with-what-if
Authentication
az login # interactive browser (WAM on Windows)
az login --use-device-code # headless/SSH
az login --identity # managed identity (Azure-hosted)
az login --service-principal \
--username "$AZURE_CLIENT_ID" \
--certificate /path/to/cert.pem \
--tenant "$AZURE_TENANT_ID" # certificate-based SP (preferred for CI/CD)
MFA is mandatory for user identities since October 2025. This does NOT affect service principals or managed identities. For full credential chain, federated credentials (OIDC), WAM, multi-tenant patterns, and token caching details, see references/advanced-patterns.md#authentication-and-credential-chain.
Storage Operations
# Upload blob (always use --auth-mode login over key auth)
az storage blob upload \
--account-name mystorageacct \
--container-name mycontainer \
--file ./local.txt --name remote.txt \
--auth-mode login
# Sync directory
az storage azcopy blob sync \
--container mycontainer \
--account-name mystorageacct \
--source "./local-dir"
Waiting for Resources
az vm wait -g myRG -n myVM --created
az deployment group wait -g myRG -n my-deployment --created
az group wait -n myRG --deleted
az wait polls with --interval (default 30s) and --timeout (default 3600s). Use --custom for JMESPath conditions. For parallel wait patterns and --ids, see references/advanced-patterns.md#az-wait-patterns.
Output and Filtering
--query (JMESPath)
All Azure CLI commands support --query. Queries execute client-side on the JSON response.
# Filter + project
az vm list --show-details \
--query "[?powerState=='VM running'].{Name:name,IP:publicIps}" \
--output table
# Scalar capture (tsv strips quotes)
VM_ID=$(az vm show -g myRG -n myVM --query id --output tsv)
# Built-in functions
az vm list --query "sort_by([], &name)[0:5].name" --output tsv
For JMESPath gotchas, filter predicates, and pipe expressions, see references/advanced-patterns.md#jmespath-query-patterns.
Output Format Selection
- Shell scripts —
tsv—--output tsv - Debugging —
json/jsonc—--output json - Reports —
table—--output table - Documentation —
yaml—--output yaml - Fire-and-forget —
none—--output none
Script Safety Checklist
Verify before shipping:
set -euo pipefailat script top--only-show-errorson all commands (oraz config set core.only_show_errors=true)--subscriptionexplicit on every command (never rely on default)az account showat script start to verify context- No
--yes --no-waiton destructive commands in production (swallows errors) - Resource locks (
CanNotDelete) on production resource groups - Sensitive output (secrets, tokens) never piped to stdout unredacted
- No bare
az role assignment deletewithout--assigneeand--role(may match unintended assignments)
Common Gotchas
- MFA blocks automation: User identity
az login -u/-pfails with MFA since October 2025. Migrate to service principal or managed identity. az vm deleteorphans resources: Disks, NICs, public IPs, and NSGs are NOT deleted. Clean up manually or delete the resource group.az vm stopstill bills: Useaz vm deallocateto actually stop billing for compute.--auth-mode loginfor storage: Default is key auth which bypasses Entra RBAC. Always pass--auth-mode login.- Basic LB retired: Basic Load Balancer and default outbound access retired 2025-09-30. Use Standard LB.
- Key Vault purge protection is irreversible: Once enabled, cannot be disabled. Don't enable on dev/test vaults.
- Exit code 3 = not found: Azure CLI returns 3 (not 1) when a resource doesn't exist. Handle in
set -escripts. az role assignment deletewithout--assignee: May match and delete more assignments than intended at the given scope. Always specify--assigneeand--role.- Azure Linux 2.0 EOL: Node images removed March 31, 2026. Migrate AKS node pools to AzureLinux3 or Ubuntu.
- Subscription selector prompt: CLI 2.61.0+ shows interactive subscription picker after
az login. Disable withaz config set core.login_experience_v2=off. - No
az costcommand: Cost Management has no CLI command group. Useaz restagainst the CostManagement query API. See references/advanced-patterns.md#cost-management-queries-via-az-rest. - Some resource types lack CLI commands: CodeSigning, Purview (preview extension), Portal Dashboards have no dedicated
azcommand group. Useaz resource show/list/deleteoraz rest. See references/advanced-patterns.md#resources-without-dedicated-cli-commands. - Preview extensions auto-install with warnings: Extensions like
purviewonly have preview releases and auto-install with noisy warnings. Suppress with--only-show-errors.
Dangerous Commands Reference
Before running any destructive Azure CLI command, consult the safety reference for tiered risk commands (irreversible data loss, service disruption, cost explosion), safer alternatives, resource locks, and the --yes/--no-wait danger matrix: references/dangerous-commands.md.
Advanced Patterns Reference
For JMESPath queries, output formats, pagination, az wait, az rest, CLI extensions, az configure, environment variables, az graph (KQL), scripting best practices, exit codes, az bicep, multi-subscription loops, authentication and credential chain: references/advanced-patterns.md.
Service Patterns Reference
For Virtual Machines, Storage Accounts, Virtual Networks and NSGs, Key Vault, App Service, AKS, and RBAC role assignments: references/service-patterns.md.