Skip to content
CAMPUX Cloud Bootcamp Lab · Observability ← All labs
Hands-On Lab · Intermediate
~35 min · Azure charges may apply · Cloud Shell
CLI & KQL · torn down at the end
Observability · Monitoring

See what your cloud is doing, in KQL.

Route future subscription management events into a Log Analytics workspace, query them with KQL, and configure an alert rule and email action group. Verify the data arrives; creating the alert rule alone does not prove a notification was delivered.

Fig. 1 · From Activity log to a KQL alert
Resource group · campux-lab-obs-rgstreamquery → alertActivity lograw eventsLog Analyticsquery in KQLAlert rulefires on a matchlogs you cannot query are just storage
Start

Run one step, then check it

Read the step before copying. Bash commands belong in Git Bash on Windows or Azure Cloud Shell set to Bash; PowerShell uses different syntax. A JSON, YAML, Bicep or application-code example belongs in the named file, not directly at the terminal prompt. Follow any shell-specific instructions below.

Keep the same terminal open so earlier variables remain available. Copy one complete command block without the terminal's $ prompt. A backslash continues a command onto the next line. If Bash shows a lone >, it is waiting for the rest of a quote, command or file block. Finish the complete block; if the paste was incomplete, press Ctrl+C and copy it again. Do not paste the next step into an unfinished command.

A line such as RG="training-group" stores a value without printing it. Output captured with $(...) is also stored; a blank screen alone does not prove failure. A sleep deliberately waits, and -o none suppresses normal output. Wait for your normal prompt, then run the step's checkpoint. After an error, stop and keep the exact command and message; later steps cannot repair a missing resource. Never share passwords, access tokens or secret values in a screenshot.

Why

Logs you cannot query are just storage

Azure records the subscription Activity Log automatically. It covers management events such as creating or deleting a resource group, not every operation inside a resource. This lab routes future Activity Log entries to Log Analytics, where KQL can query them. You will also configure a scheduled rule that looks for failed management operations. The rule and its email action need a separate delivery test before a team can depend on them.

By the end you will have a workspace collecting real data, a KQL query that reads back your own operations, and a scheduled-query alert rule with an action group behind it — a loop you can point at any workload later.

Monitoring is a question you have written down in advance.

Before you begin — one-time setup

You need an Azure subscription, Azure CLI access or Cloud Shell (Bash), and permission to create a workspace, action group, alert rule, and subscription-scoped diagnostic setting. Check your role at the subscription scope before starting; resource-group access alone may not be enough. Review current alert pricing because a free account does not guarantee a zero-cost run.

The companion repository

This page is the full lab; companion Bicep is in github.com/kloudcaptain/campux-labs. Azure includes the subscription Activity Log, but a scheduled log alert and action-group notifications can incur charges. Check current Azure Monitor pricing and set a small budget before creating the rule.

Setup

A workspace to collect into

Create a resource group and a Log Analytics workspace. The workspace is the pool everything queryable lands in.

Commands inside $(...) save their output in a variable. Check the printed identifier or the checkpoint before continuing.

# Windows/Git Bash: stop it mangling /subscriptions/... arguments (harmless on macOS/Linux)
export MSYS_NO_PATHCONV=1

RG="campux-lab-obs-rg"
az group create -n "$RG" -l eastus

az monitor log-analytics workspace create -g "$RG" -n campux-logs

WS_ID=$(az monitor log-analytics workspace show -g "$RG" -n campux-logs --query id -o tsv)
WS_GUID=$(az monitor log-analytics workspace show -g "$RG" -n campux-logs --query customerId -o tsv)
echo "workspace resource id: $WS_ID"
Checkpoint The workspace exists and you have captured two identifiers — the full resource id (used to wire data in) and the customerId GUID (used to query). Empty for now; the next step gives it something to collect.
Step 1

Stream the Activity log in

A diagnostic setting at the subscription level routes the Activity log into your workspace. From now on, every management operation you run is recorded there.

Commands inside $(...) save their output in a variable. Check the printed identifier or the checkpoint before continuing.

SUB_ID=$(az account show --query id -o tsv)

az monitor diagnostic-settings subscription create \
  --name campux-activity-to-logs \
  --location eastus \
  --subscription "$SUB_ID" \
  --workspace "$WS_ID" \
  --logs '[{"category":"Administrative","enabled":true},{"category":"Alert","enabled":true}]'

Now generate a little activity to be sure there is something to find — create and delete a throwaway resource group:

az group create -n campux-obs-ping -l eastus && az group delete -n campux-obs-ping --yes
Checkpoint The diagnostic setting is enabled. The Activity Log existed before this step; this setting routes future entries to the workspace. Allow for ingestion delay before expecting the new resource-group operations in AzureActivity.
Step 2

Ask a question in KQL

Query the AzureActivity table straight from the CLI. Read this KQL as a pipeline: take the table, keep the last hour, summarise a count per operation, sort by the busiest.

az monitor log-analytics query -w "$WS_GUID" --analytics-query '
AzureActivity
| where TimeGenerated > ago(1h)
| summarize count() by OperationNameValue, ActivityStatusValue
| sort by count_ desc
' -o table
Checkpoint Once ingestion catches up, the query groups your recent management operations. If it is empty, widen the time window, inspect the diagnostic setting, and allow for collection delay. Do not assume a fixed arrival time.
Step 3

Turn a query into an alert

An alert is a KQL query Azure runs on a schedule, firing when it returns too many rows. First an action group — who gets told — then the rule itself, watching for failed operations.

Commands inside $(...) save their output in a variable. Check the printed identifier or the checkpoint before continuing.

# Enter an address you can check; the prompt avoids a fake recipient.
read -r -p "Lab alert email: " ALERT_EMAIL
test -n "$ALERT_EMAIL"
az monitor action-group create -g "$RG" -n campux-oncall \
  --short-name cxoncall \
  --action email me "$ALERT_EMAIL"

AG_ID=$(az monitor action-group show -g "$RG" -n campux-oncall --query id -o tsv)

# fire if any Activity-log operation failed in the last 5 minutes
az monitor scheduled-query create -g "$RG" -n campux-failed-ops \
  --scopes "$WS_ID" \
  --condition "count 'failedOps' > 0" \
  --condition-query failedOps='AzureActivity | where ActivityStatusValue == "Failed"' \
  --evaluation-frequency 5m --window-size 5m \
  --severity 2 --action-groups "$AG_ID" \
  --description "A management operation failed"
Checkpoint Confirm the rule exists and is enabled:
az monitor scheduled-query show -g "$RG" -n campux-failed-ops \
  --query "{name:name, enabled:enabled, freq:evaluationFrequency}" -o table
The rule is configured, but this check does not prove that a failed operation occurred or that an email arrived. In production, verify the rule's evaluation and test the action group before trusting the notification path.
Down

Tear it down

Delete the resource group, and remove the subscription-level diagnostic setting separately — it lives on the subscription, not in the group.

az monitor diagnostic-settings subscription delete \
  --name campux-activity-to-logs --subscription "$SUB_ID" --yes
az group delete -n campux-lab-obs-rg --yes
az group exists -n campux-lab-obs-rg      # -> false
Checkpoint Confirm the group no longer exists and list subscription diagnostic settings to verify this lab's route was removed. Check any other resources you created separately.
End

What you can now honestly claim

You created a Log Analytics workspace, routed real telemetry into it, queried that telemetry in KQL, and built a scheduled-query alert wired to an action group. That is "observability with Azure Monitor, Log Analytics, and thoughtful alerting" — a line lifted almost word-for-word from the job descriptions — done rather than described. KQL in particular is a transferable superpower: the same language queries Application Insights, Microsoft Sentinel, and Azure Resource Graph, so the query muscle you just built pays off far beyond this one workspace.

Footnotes
  1. The subscription Activity Log is collected automatically and retained for 90 days. Sending it to a workspace can avoid a direct ingestion charge for that data, but scheduled log alerts and notification features may be billed. Check current pricing for your configuration.
  2. There is a short delay — usually a few minutes — between an operation happening and it being queryable, because logs are batched on their way in. If a query returns nothing, that latency is the first thing to suspect, not a broken pipeline.