4. Getting Started
The smallest thing that proves Azure Monitor works, end to end: create a workspace, create a
resource that produces logs, bolt a diagnostic setting onto it, query the result in KQL, and fire one
alert. Three ways — Portal, az, and Terraform.
This page is deliberately throwaway. Hard-coded names, no variables, no remote state, no pipeline. Everything production-shaped lives in Deployment.
We will use a Key Vault as the thing being monitored, because its AuditEvent category produces a
log row the moment you touch a secret — so you get feedback in one command rather than waiting for
traffic.
[Image Prompt: 2D minimalistic diagram comparing three provisioning paths — Azure Portal, Azure CLI, and Terraform — converging on the same set of resources: a Log Analytics workspace, a monitored Key Vault, and the diagnostic setting connecting them, flat design, clean vector art style, white background]
1. Azure Portal
Short click-paths only; portal navigation changes often, so these name destination blades rather than exact button chains.
- Create the workspace — search Log Analytics workspaces → Create → new resource group
rg-monitor-demo, regionuksouth, namelog-monitor-demo. Accept the defaults; the pricing tier defaults to pay-as-you-go, which is what you want for a demo. - Create the monitored resource — search Key Vault → Create → same resource group, name
kv-monitor-demo-<something-unique>. On the Access configuration step choose Azure RBAC. - Bolt on the diagnostic setting — open the vault → Monitoring → Diagnostic settings → Add
diagnostic setting → tick the Audit category → destination Send to Log Analytics
workspace, choose
log-monitor-demo, and pick Resource specific as the destination table. Name itdiag-to-lawand save. This blade is the whole lesson of this page. - Generate a log row — in the vault, Objects → Secrets → Generate/Import, create a secret
called
demo. (You need the Key Vault Secrets Officer role on the vault to do this; grant it to yourself under Access control (IAM) if the blade refuses.) - Query it — open the workspace → Logs → run the query below. Expect nothing for the first few minutes; ingestion latency is real.
AZKVAuditLogs
| where TimeGenerated > ago(1h)
| project TimeGenerated, OperationName, ResultSignature, CallerIPAddress, identity_claim_upn_s
| order by TimeGenerated desc
- Create one alert — from the workspace's Logs blade, New alert rule on that query, or from Monitor → Alerts → Create. Condition: the query returns more than 0 rows in a 5-minute window. Action group: create one with your email. Severity 3.
2. Azure CLI
Copy-pasteable, and the version you should actually use to learn — the portal hides the resource shapes.
# --- Variables (throwaway) ---
RG=rg-monitor-demo
LOC=uksouth
LAW=log-monitor-demo
KV=kv-mon-demo-$RANDOM
# --- 1. Throwaway resource group ---
az group create -n $RG -l $LOC
# --- 2. The workspace ---
az monitor log-analytics workspace create \
-g $RG -n $LAW -l $LOC \
--retention-time 30
LAW_ID=$(az monitor log-analytics workspace show -g $RG -n $LAW --query id -o tsv)
# --- 3. Something worth monitoring ---
az keyvault create -g $RG -n $KV -l $LOC --enable-rbac-authorization true
KV_ID=$(az keyvault show -g $RG -n $KV --query id -o tsv)
# --- 4. The diagnostic setting: the step nothing works without ---
az monitor diagnostic-settings create \
--name diag-to-law \
--resource "$KV_ID" \
--workspace "$LAW_ID" \
--export-to-resource-specific true \
--logs '[{"category":"AuditEvent","enabled":true}]' \
--metrics '[{"category":"AllMetrics","enabled":true}]'
# --- 5. Give yourself data-plane access, then generate a row ---
ME=$(az ad signed-in-user show --query id -o tsv)
az role assignment create --assignee "$ME" \
--role "Key Vault Secrets Officer" --scope "$KV_ID"
sleep 30 # RBAC propagation
az keyvault secret set --vault-name $KV -n demo --value hello -o none
# --- 6. Query it (wait a few minutes for ingestion) ---
WS_GUID=$(az monitor log-analytics workspace show -g $RG -n $LAW --query customerId -o tsv)
az monitor log-analytics query \
--workspace "$WS_GUID" \
--analytics-query "AZKVAuditLogs | where TimeGenerated > ago(1h) | project TimeGenerated, OperationName, ResultSignature | order by TimeGenerated desc | take 20" \
-o table
# --- 7. A metric alert on the vault, wired to an action group ---
az monitor action-group create \
-g $RG -n ag-demo --short-name agdemo \
--action email me you@example.com
az monitor metrics alert create \
-g $RG -n alert-kv-traffic \
--scopes "$KV_ID" \
--condition "total ServiceApiHit > 5" \
--window-size 5m --evaluation-frequency 1m \
--severity 3 \
--action ag-demo \
--description "Demo: vault is being used more than usual"
Two things worth noticing in that script. First, --export-to-resource-specific true is what puts
the rows in AZKVAuditLogs rather than the legacy wide AzureDiagnostics table — get this right at
creation, because changing it later means your queries have to look in two places for historical
data. Second, the role assignment on step 5 is a data-plane grant; being the subscription owner
does not by itself let you write a secret, which is the same control/data split described in
Architecture.
PowerShell equivalent
Azure Monitor is heavily used by Windows- and VM-centric teams, so the Az equivalents are worth
having:
New-AzResourceGroup -Name rg-monitor-demo -Location uksouth
$law = New-AzOperationalInsightsWorkspace -ResourceGroupName rg-monitor-demo `
-Name log-monitor-demo -Location uksouth -RetentionInDays 30
$kv = New-AzKeyVault -ResourceGroupName rg-monitor-demo -VaultName "kv-mon-demo-$(Get-Random)" `
-Location uksouth -EnableRbacAuthorization
$log = New-AzDiagnosticSettingLogSettingsObject -Category AuditEvent -Enabled $true
New-AzDiagnosticSetting -Name diag-to-law -ResourceId $kv.ResourceId `
-WorkspaceId $law.ResourceId -Log $log -LogAnalyticsDestinationType Dedicated
3. Terraform — minimal
The same thing declaratively. Hard-coded, single file, local state — the parameterised module lives in Deployment.
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
}
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "demo" {
name = "rg-monitor-demo"
location = "uksouth"
}
resource "azurerm_log_analytics_workspace" "demo" {
name = "log-monitor-demo"
resource_group_name = azurerm_resource_group.demo.name
location = azurerm_resource_group.demo.location
sku = "PerGB2018" # pay-as-you-go
retention_in_days = 30
}
data "azurerm_client_config" "current" {}
resource "azurerm_key_vault" "demo" {
name = "kv-mon-demo-91427"
resource_group_name = azurerm_resource_group.demo.name
location = azurerm_resource_group.demo.location
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = "standard"
enable_rbac_authorization = true
}
# The whole point of the page
resource "azurerm_monitor_diagnostic_setting" "kv" {
name = "diag-to-law"
target_resource_id = azurerm_key_vault.demo.id
log_analytics_workspace_id = azurerm_log_analytics_workspace.demo.id
log_analytics_destination_type = "Dedicated" # resource-specific tables
enabled_log {
category = "AuditEvent"
}
metric {
category = "AllMetrics"
}
}
output "workspace_id" {
value = azurerm_log_analytics_workspace.demo.workspace_id
}
terraform init
terraform plan
terraform apply
Three details in that snippet that trip people up:
log_analytics_destination_type = "Dedicated"is Terraform's spelling of "resource-specific tables". The default is the legacyAzureDiagnosticsbehaviour.enabled_logversus the oldlogblock. The provider deprecated the older block; if you are reading a tutorial withlog { category = ... enabled = true }, it predates the change.- The diagnostic setting's
target_resource_idis the monitored resource, not the workspace. That is what makes it an extension resource, and it means Terraform must be able to write to the monitored resource's scope — a permissions surprise when the monitored resource lives in another team's resource group.
Prove it worked
# Is the setting actually there?
az monitor diagnostic-settings list --resource "$KV_ID" -o table
# Is anything landing? (this is the query to run before you panic)
az monitor log-analytics query --workspace "$WS_GUID" --analytics-query \
"union withsource=T * | where TimeGenerated > ago(1h) | summarize count() by T" -o table
If the second query returns nothing after ten minutes, in order: check the diagnostic setting exists, check the category is enabled, check you actually generated activity, and only then suspect ingestion latency.
Teardown
Always. Half of all surprise cloud bills come from forgotten demo resources, and a workspace left collecting is one of the more expensive things to forget.
az group delete -n rg-monitor-demo --yes --no-wait
Deleting the resource group is the cleanest teardown Azure gives you, and it is a genuine advantage over per-resource cleanup in AWS. Two caveats specific to this page:
- The Key Vault is soft-deleted, not gone. Its name stays reserved for the retention period, and
recreating it with the same name will fail until you purge it (
az keyvault purge -n $KV) — and if purge protection were enabled, you could not purge it at all. - The role assignment you created is scoped to the vault and disappears with it; role assignments scoped above the resource group would not.
Next: Deployment →
← Back to the Azure Monitor overview · ← Previous: Architecture