Azure Monitor raccoglie telemetria in un Log Analytics workspace, dove i dati risiedono in tabelle interrogabili con KQL (Kusto Query Language). Per l’amministratore Azure, KQL non è opzionale: è lo strumento con cui si diagnosticano problemi, si costruiscono dashboard e — soprattutto — si definiscono i log alert. Capire come una query si trasforma in una regola di allerta è uno dei nodi centrali di AZ-104.
Il modello dei dati e le tabelle comuni
Ogni sorgente scrive in una tabella con schema noto. Le tre che ricorrono all’esame:
- Heartbeat — un record periodico (ogni minuto) inviato dagli agenti (Azure Monitor Agent). Serve a verificare quali VM sono “vive”: l’assenza di heartbeat è il segnale classico di una macchina down.
- AzureActivity — l’Activity Log a livello di control plane: chi ha creato, modificato o eliminato una risorsa, con
Caller,OperationNameValueeActivityStatusValue. È la tabella per audit e governance. - Perf — contatori di performance guest (CPU, memoria, disco) raccolti tramite Data Collection Rules (DCR). I dati vivono nelle colonne
ObjectName,CounterName,CounterValue,InstanceName.
Gli operatori fondamentali
Una query KQL è una pipeline: la tabella iniziale scorre attraverso operatori separati da |, ciascuno che trasforma il set di righe.
where— filtra le righe. Va messo il prima possibile, specialmente sul tempo (TimeGenerated), per ridurre il volume scandito.project— seleziona/rinomina le colonne da mantenere (project-awayper escludere).summarize— aggrega: applica funzioni comeavg(),count(),max()raggruppando perby.join— combina due tabelle su una chiave comune (es. correlareHeartbeatePerfsullo stessoComputer).render— istruisce il portale a disegnare il risultato cometimechart,barchart, ecc. È puramente di presentazione.
Costruire trend e aggregazioni
Per un trend temporale la chiave è bin(), che arrotonda TimeGenerated a intervalli fissi creando “secchi” (bucket) per l’asse X:
Perf
| where TimeGenerated > ago(24h)
| where CounterName == "% Processor Time"
| summarize avg(CounterValue) by Computer, bin(TimeGenerated, 15m)
| render timechart
Qui where restringe a 24 ore e a un contatore specifico, summarize ... by bin() produce la media CPU per macchina ogni 15 minuti, render la visualizza. Questo è il pattern-tipo che l’esame propone come “grafico di andamento”.
Dalla query al log alert
Il passaggio critico: una log alert rule in Azure Monitor incapsula una query KQL eseguita a intervalli regolari. La configurazione ruota attorno a tre elementi:
- Measure — la colonna numerica valutata (spesso il risultato di un
summarize count()oavg()). - Aggregation granularity — la finestra temporale su cui la query aggrega.
- Threshold + operatore — la condizione (es.
count > 5) che, se soddisfatta, fa scattare l’alert collegato a un Action Group.
Esempio per rilevare VM che smettono di rispondere:
Heartbeat
| summarize LastSeen = max(TimeGenerated) by Computer
| where LastSeen < ago(5m)
La regola conta le righe risultanti; se il conteggio è maggiore di zero, esiste almeno una macchina silente da oltre 5 minuti. Nota: la regola definisce da sé la frequenza di valutazione, quindi nella query di allerta si evita di duplicare filtri temporali già gestiti dalla finestra della regola.
Trappole tipiche d’esame
- Scenario: una query filtra
where Computer == "web01"ma non restituisce righe, benché la VM esista. → Risposta: KQL è case-sensitive sui valori delle stringhe; se il record contieneWEB01, il confronto fallisce. Usa=~(uguaglianza case-insensitive) otolower(). - Scenario: una query su un intervallo ampio è lentissima e costosa. → Risposta: manca un filtro
where TimeGenerated > ago(...)in cima alla pipeline; senza restringere il range temporale si scansiona l’intera tabella. Filtra sempre il tempo per primo. - Scenario: serve individuare VM che non inviano più dati. → Risposta: interroga Heartbeat con
summarize max(TimeGenerated) by Computer, nonPerf; l’assenza di heartbeat è il segnale canonico di macchina down. - Scenario: serve un grafico di andamento della CPU nel tempo. → Risposta: aggrega con
summarize ... by bin(TimeGenerated, ...)e chiudi conrender timechart; senzabin()non ottieni serie temporali. - Scenario: occorre tracciare chi ha eliminato un resource group. → Risposta: la tabella è AzureActivity (control plane), filtrando su
OperationNameValueeCaller, non le tabelle di performance del guest.