# Azure CLI Cheat Sheet — Monitoring & Governance

> **Tool:** Azure CLI (az)
> **Category:** Cloud CLIs
> **Verified against:** Azure CLI 2.87.0, flags verified via `az <cmd> --help`, 2026-08-21
> **Official docs:** https://learn.microsoft.com/cli/azure

Azure Monitor metrics and Log Analytics queries for observability, and Azure Policy for enforcing organization-wide governance rules.

## Listing available metrics for a resource

```bash
az monitor metrics list-definitions --resource <resource-id>
az monitor metrics list --resource <resource-id> --metrics "Percentage CPU" --interval 5m
```

`az monitor metrics list-definitions` tells you which metric names, aggregation types, and dimensions a given resource actually supports before you query it — resource types expose different metrics, so guessing a metric name is a common source of an empty result.

## Creating a metric-based alert

```bash
az monitor metrics alert create \
  --resource-group my-rg --name high-cpu-alert \
  --scopes /subscriptions/<sub-id>/resourceGroups/my-rg/providers/Microsoft.Compute/virtualMachines/my-vm \
  --condition "avg Percentage CPU > 80" \
  --window-size 5m --evaluation-frequency 1m \
  --action my-action-group
```

`--condition` uses a compact expression syntax (`{avg,min,max,total,count} METRIC {operator} THRESHOLD`) rather than a JSON body — `az monitor metrics alert condition create` can build more complex dynamic-threshold conditions if a static threshold isn't enough.

## Querying a Log Analytics workspace

```bash
az monitor log-analytics workspace create --resource-group my-rg --workspace-name my-workspace
az monitor log-analytics query \
  --workspace <workspace-customer-id> \
  --analytics-query "AzureActivity | summarize count() by bin(TimeGenerated, 1h)" \
  --timespan P1D
```

The `--workspace` value is the workspace's *customer ID* (a GUID), not its resource name — get it with `az monitor log-analytics workspace show --resource-group my-rg --workspace-name my-workspace --query customerId -o tsv`. `--analytics-query` takes a KQL (Kusto Query Language) query, the same language used in the Log Analytics portal blade.

## Azure Policy: definitions and assignments

```bash
az policy definition create \
  --name require-tag-environment \
  --rules @policy-rule.json --params @policy-params.json \
  --mode Indexed --display-name "Require an environment tag"
az policy definition list --query "[?policyType=='Custom']" --output table
az policy assignment create \
  --name enforce-env-tag --policy require-tag-environment \
  --scope /subscriptions/<sub-id>/resourceGroups/my-rg
```

A policy *definition* is just a rule (what to check, and the effect — `deny`, `audit`, `append`, etc.); it does nothing until a policy *assignment* attaches it to a scope (subscription, resource group, or management group). The same definition can be assigned at multiple scopes with different parameters.

## Checking policy compliance

```bash
az policy state list --resource-group my-rg --filter "complianceState eq 'NonCompliant'"
az policy state trigger-scan --resource-group my-rg --no-wait
```

Compliance state is evaluated on a periodic cycle (roughly every 24 hours) plus on resource create/update — `az policy state trigger-scan` forces an on-demand re-evaluation instead of waiting for the next cycle, useful right after creating or changing an assignment.
