In nahezu jedem unserer Azure-Kurse kommen wir mit dem Azure Monitor, speziell den Dienst Log Analytics in Berührung. Allein schon beim Thema Überwachung der Azure-Plattform sowie Ihrer Anwendungen und oder Nutzungsmuster/Telemetrien kommen Sie um Microsofts Abfragesprache „Kusto Query Langage“ (KQL) kaum herum, weil diese tief mit Microsofts-Cloud-Plattformen (Azure, Graph-API) integriert ist. Das gilt nicht nur für Azure Monitor und Log Analytics (OMS, SCROM), sondern z. B. auch für Azure Data Explorer, Azure Synapse Analytics oder Azure Resource Graph. Ein paar Grundkenntnisse können also nicht schaden.

Vorab: ich bin selbst auch keine Kommandozeilen-Virtuose. Mit KQL geht es mir ähnlich, wie mit TSQL oder Powershell. Man muss nicht hunderte Kommandos im Kopf haben. Ein grundlegendes Verständnis der formalen Syntax und Funktionsweise, das Wissen um die unterschiedlichen Einsatzbereiche im Kontext der jeweiligen Dienste sowie bei Bedarf „Kollegen“ wie den GitHub-Copiloten, einige nützliche VS-Code-Extentions und den Zugang zur Kommandoreferenz in der Hinterhand, sind für das „Leben in freier Wildbahn“ nützlich. Zudem habe ich mir ein Repertoire für mich häufig verwendeter Abfragen in einem privaten GitHub-Repository zurecht gelegt.

Grundsätzlich handelt es sich bei KQL um eine leistungsfähige Sprache zum Abfragen strukturierter, halbstrukturierter und unstrukturierter Daten. KQL ist ausdrucksstark und Sie können die Abfrageabsicht recht einfach aus den Kommandos „herauslesen“. KQL  eignet sich ist gut zum Abfragen von Telemetrie, Metriken und Protokollen. Die Sprache bietet  umfassende Unterstützung für …

  • Textsuche und Analyse
  • Zeitreihenoperatoren und -funktionen
  • Analyse und Aggregationen
  • Geospatial
  • Vektorgleichheitssuchen

… und viele andere Sprachkonstrukte aus der Datenanalyse. Abfragen verwenden Schemaentitäten, die wie bei SQL in eine Hierarchie organisiert sind, namentlich Datenbanken, Tabellen und Spalten. KQL kennt drei Arten von Benutzerabfrageanweisungen, nämlich die tabellarische Ausdrucksanweisung, die  let-Anweisung und die set-Anweisung, wobei die erstgenannte die am häufigsten verwendete Abfrageausweisung ist, bei der sowohl die Eingabe als auch die Ausgabe aus Tabellen oder tabellarischen Datasets besteht.

Eine let Anweisung hingegen legt einen Variablennamen auf einen Ausdruck oder eine Funktion fest oder erstellt eine Ansicht (View). Die set Anweisung schließlich legt eine Anforderungseigenschaft für die Dauer der Abfrage fest. Schließlich gibt es noch Verwaltungsbefehle. Diese sind selbst keine Kusto-Abfragen, sondern drücken Anforderungen an Kusto aus, um Daten oder Metadaten zu verarbeiten oder zu ändern.

Ich hatte mich in diesem Blog schon einmal grundlegend mit den unterschiedlichen Kontexten, in denen sich die KQL gewinnbringend einsetzen lässt beschäftigt. Darauf können wir also hier aufbauen. Noch Mal zur Erinnerung: Folgende Dienste aus der Microsoft-Cloud verwenden KQL, bzw. lassen sich mit KQL abfragen. Ich habe auch das jeweilige Compute-/Speicher-Modell, die Art der Daten und die verfügbaren Abfrage-Werkzeuge mit aufgeführt:

Dienste, die KQL verwenden

  1. Azure Log Analytics / Azure Monitor
    • Compute/Speichermodell: Log Analytics Workspaces und Application Insights.
    • Art der Daten: Leistungsmetriken, Aktivitätsprotokolle, Diagnosedaten.
    • Tools: Azure Portal, Kusto Explorer, Azure Data Explorer Web UI, PowerShell, REST API 
  1. Azure Data Explorer
    • Compute/Speichermodell: Azure Data Explorer Cluster.
    • Art der Daten: Zeitreihendaten, strukturierte und unstrukturierte Daten.
    • Tools: Kusto Explorer, Azure Data Explorer Web UI, Jupyter Kqlmagic, Flow, PowerQuery 
  2. Azure Synapse Analytics
    • Compute/Speichermodell: Synapse SQL Pools, Spark Pools.
    • Art der Daten: Big Data, Data Warehousing, Echtzeit-Analysen.
    • Tools: Synapse Studio, Kusto Explorer, Azure Data Explorer Web UI 
  3. Azure Resource Graph
    • Compute/Speichermodell: Azure Resource Graph.
    • Art der Daten: Metadaten und Eigenschaften von Azure-Ressourcen.
    • Tools: Azure Portal, Kusto Explorer, Azure Data Explorer Web UI 

Zunächst noch ein paar Worte zu den Abfragewerkzeugen. Beim „Kusto Explorer“ handelt es sich um ein herunterladbares Desktop-Tool zur Abfrage und Analyse von Daten in Azure Data Explorer. Mit der „Azure Data Explorer Web UI“ steht aber auch eine  webbasierte Benutzeroberfläche für die Abfrage und Analyse von Daten zur Verfügung. Außerdem finden Sie einen grafischen KQL-Abfage-Editor im Azure-Portal im Azure Monitor, in einem Log Analytics Workspace sowie im Azure Ressource Graph Explorer.

Natürlich können Sie auch die PowerShell für skriptbasierte Abfragen nutzen oder KQL via REST API für programmgesteuerte Abfragen und Integrationen nutzen. Erwähnenswert ist zudem Jupyter Kqlmagic, eine Integration von KQL in Jupyter Notebooks für interaktive Datenanalyse. Schließlich können Sie KQL mittels PowerQuery für Datenabfragen und Transformationen in Excel und Power BI verwenden und mit „Flow“ visuelle Workflows zur Datenverarbeitung nutzen.

Damit Sie etwas abfragen können brauchen Sie Daten und zwar möglichst viele über einen langen Zeitraum. Um hierzu nicht in einem langwierigen Prozess Ressourcen und/oder Datenquellen bereitstellen zu müssen, können Sie sich bei Microsoft verschiedener Demo-Umgebungen bedienen. Details dazu finden Sie im oben verlinkten Beitrag. In diesem Artikel hier verwendet ich die Log-Analytics-Demo-Umgebung von Microsoft, zu finden unter https://aka.ms/lademo. Dabei handelt es sich nicht nur um eine entsprechend große Ressourcen-Basis in Azure, hier ist u. a. auch der Sentinel mit zahlreichen Datenkonnektoren angebunden.

Erkunden von KQL im Azure Monitor in Microsoft LA-Demoumgebung

Lassen Sie uns nun systematisch verschiedenen Arten von Abfragemöglichkeiten erkunden. Sie müssen in Azure-Monitor-Logs mit dem Menü rechts oben vom „Einfachen Modus“ in den „KQL-Modus“ wechseln.

Sie müssen erst in den KQL-Modus wechseln.
Sie müssen erst in den KQL-Modus wechseln.

Rufen Sie z. B. zunächst die komplette Tabelle „SecurityEvent“ ab. Beachten Sie dabei, dass Sie die Autovervollständigung nutzen können.

Der KQL-Abfrage-Editor bietet eine nützliche Autovervollständigung.
Der KQL-Abfrage-Editor bietet eine nützliche Autovervollständigung.

Lassen Sie weitere von der Autovervollständigung angebotene Operatoren einfach weg und führen Sie die Abfrage mit „Ausführen“ aus, setzen aber vorher den Filter für den Zeitbereich mit Hilfe von „Benutzerdefiniert“ auf etwa 3 Monate in die Vergangenheit, um relevante Daten zu erhalten. Zum Zeitpunkt der Erstellung dieses Beitrags war das bei mir der 01.04.2025. Ich bekomme dann ca. 1000 Ergebnisse.

Um Daten aus der Demo-Umgebung zu erhalten, müssen Sie evtl. mit dem Zeitbereich experimentieren.
Um Daten aus der Demo-Umgebung zu erhalten, müssen Sie evtl. mit dem Zeitbereich experimentieren.

Bei den Ergebnissen auf Seite 1 handelt es sich z, B, um den Windows-Computer „RETAILVM1“ und die Event-Quelle „Microsoft-Windows-Security-Auditing“. Wir werden jetzt systematisch eine ganze Reihe verschiedener Abfrageanweisungen ausprobieren, mit dem Ziel, die verschiedene Abfrage-Konstrukte, Möglichkeiten und Anwendungsbereiche zu erkunden.

Mit dem „search“-Operator lassen sich alle Spalten in der angegeben Tabelle nach bestimmten Werten durchsuchen. Verwenden jedoch den search-Operator ohne Angabe bestimmte Tabellen oder qualifizierende Klauseln ist er weniger effizient als tabellenspezifische und spaltenspezifische Textfilterung, wie z. B.:

search in (SecurityEvent,App*) „new“

Achten Sie auf die Spalte „Properties_dynamic“.

Der search-Operator durchsuch angegebene Tabellen nach den gewünschten Werten.
Der search-Operator durchsuch angegebene Tabellen nach den gewünschten Werten.

Sie können für jeden „Treffer“ (Zeile) links vorne auf das „>“-Zeichen klicken, um alle „Attribute“ des jeweiligen Datensatzes im Detail zu erkunden.

Die Detailansicht eines "Treffers".
Die Detailansicht eines „Treffers“.

Erkunden Sie nun den Operator „where“; die Ähnlichkeiten mit SQL sollten dabei nicht verborgen bleiben. Sie können übrigens den zu untersuchenden Zeitbereich nicht nur vorab in der GUI definieren, sondern auch in der Abfrage selbst. Das sieht dann so aus:

SecurityEvent 
| where TimeGenerated > ago(63d)

Der Filter in der Weboberfläche ändert sich dann automatisch auf „In Abfrage festlegen“:

Zeitbereiche lassen sich auch in der Abfrage selbst ausdrücken.
Zeitbereiche lassen sich auch in der Abfrage selbst ausdrücken.

Ich werde ab setzt aber nicht mehr jede Ausgabe bebildern; denn Sie können die folgenden Beispiele in der Demo-Umgebung einfach selbst ausprobieren:

Sie können z. B. wie folgt nach einer spezifische Event-ID suchen …

SecurityEvent 
| where TimeGenerated > ago(65d) and EventID == 4688

… oder zusätzlich nach einem spezifischen betroffenen Konto-Typ:

SecurityEvent 
| where TimeGenerated > ago(65d)
| where EventID == 4688 
| where AccountType =~ „user“

Die gesuchten Event-IDs dürfen auch gerne in einem spezifischen Bereich liegen:

SecurityEvent 
| where TimeGenerated > ago(65d) and EventID in (4624, 4625)

Wie eingangs erläutert könnten Sie zusätzlich mit einer let-Anweisung Variablen deklarieren:

let timeOffset = 65d;
let discardEventId = 4688;
SecurityEvent
 | where TimeGenerated > ago(timeOffset*2) and TimeGenerated < ago(timeOffset)
 | where EventID != discardEventId

Sie können in einer let-Anweisung auch dynamische Listen zur Variablendeklaration verwenden.  Oder Sie verwenden die let-Anweisung für das Deklarieren einer dynamische Tabelle. Eine dynamische Tabelle ist eine  Tabelle, die erst innerhalb einer Abfrage erstellt wird, aber nicht als separate Tabelle im Log Analytics gespeichert wird. Nehmen wir an, Sie weisen der Variablen „LowActivityAccounts“ mit Hilfe eine let-Anweisung eine Tabelle dynamisch zu, die ihrerseits erst aus den Ergebnissen der Abfrage …

SecurityEvent | summarize cnt = count() by Account | where cnt < 1000 

… entsteht, nicht aber in Log Analytics gespeichert wird. Die Die dynamische Tabelle kann dann in der Abfrage verwendet werden:

let LowActivityAccounts =
    SecurityEvent
    | summarize cnt = count() by Account
    | where cnt < 1000;
LowActivityAccounts | where Account contains „sql“

Dynamische Tabellen sind nützlich, wenn Sie eine Tabelle erstellen möchten, die nur für eine bestimmte Abfrage benötigt wird und nicht als separate Tabelle gespeichert werden soll.

Ferner könnten Sie mir einer extend-Anweisung eine „berechnete“ Spalte erstellen und sie zur Ergebnismenge hinzufügen.

SecurityEvent 
| where TimeGenerated > ago(65d)
| where ProcessName != „“ and Process != „“
| extend StartDir =  substring(ProcessName,0, string_size(ProcessName)-string_size(Process))

Sie haben hiermit schon einmal einen ersten Ausdruck einiger fundamentaler Operationen erhalten. Die Beispiele habe ich mir (größtenteils) nicht selbst ausgedacht, sondern mich eines öffentlichen Microsoft-Repositories auf GitHub für den Kurs „SC200 – Microsoft Security Operations Analyst“ bedient, den Sie übrigens ebenfalls bei uns buchen können.

Weitere Beispiele werden wir in folgenden Beiträgen erkunden!

 

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Seite verwendet Akismet, um Spam zu reduzieren. Erfahre, wie deine Kommentardaten verarbeitet werden..