--- name: eventhouse-consumption-cli description: > Run KQL queries against Fabric Eventhouse for real-time intelligence and time-series analytics using `az rest` against the Kusto REST API. Covers KQL operators (where, summarize, join, render), Eventhouse schema discovery (.show tables), time-series patterns with bin(), and ingestion monitoring. Use when the user wants to: 1. Run read-only KQL queries against an Eventhouse or KQL Database 2. Discover Eventhouse table schema and metadata 3. Analyse real-time or time-series data with KQL operators 4. Monitor ingestion health and active KQL queries 5. Export KQL results to JSON Triggers: "kql query", "kusto query", "eventhouse query", "kql database", "real-time intelligence", "time-series kql", "query eventhouse", "explore eventhouse", "show tables kql" --- > **Update Check — ONCE PER SESSION (mandatory)** > The first time this skill is used in a session, run the **check-updates** skill before proceeding. > - **GitHub Copilot CLI / VS Code**: invoke the `check-updates` skill. > - **Claude Code / Cowork / Cursor / Windsurf / Codex**: compare local vs remote package.json version. > - Skip if the check was already performed earlier in this session. > **CRITICAL NOTES** > 1. To find the workspace details (including its ID) from workspace name: list all workspaces and, then, use JMESPath filtering > 2. To find the item details (including its ID) from workspace ID, item type, and item name: list all items of that type in that workspace and, then, use JMESPath filtering # eventhouse-consumption-cli — Read-Only KQL Queries via CLI ## Table of Contents | Task | Reference | Notes | |---|---|---| | Finding Workspaces and Items in Fabric | [COMMON-CLI.md § Finding Workspaces and Items in Fabric](../../common/COMMON-CLI.md#finding-workspaces-and-items-in-fabric) | **Mandatory** — *READ link first* [needed for finding workspace id by its name or item id by its name, item type, and workspace id] | | Fabric Topology & Key Concepts | [COMMON-CORE.md § Fabric Topology & Key Concepts](../../common/COMMON-CORE.md#fabric-topology--key-concepts) | | | Environment URLs | [COMMON-CORE.md § Environment URLs](../../common/COMMON-CORE.md#environment-urls) | KQL Cluster URI is per-item | | Authentication & Token Acquisition | [COMMON-CORE.md § Authentication & Token Acquisition](../../common/COMMON-CORE.md#authentication--token-acquisition) | Wrong audience = 401; read before any auth issue | | Core Control-Plane REST APIs | [COMMON-CORE.md § Core Control-Plane REST APIs](../../common/COMMON-CORE.md#core-control-plane-rest-apis) | | | Pagination | [COMMON-CORE.md § Pagination](../../common/COMMON-CORE.md#pagination) | | | Long-Running Operations (LRO) | [COMMON-CORE.md § Long-Running Operations (LRO)](../../common/COMMON-CORE.md#long-running-operations-lro) | | | Rate Limiting & Throttling | [COMMON-CORE.md § Rate Limiting & Throttling](../../common/COMMON-CORE.md#rate-limiting--throttling) | | | OneLake Data Access | [COMMON-CORE.md § OneLake Data Access](../../common/COMMON-CORE.md#onelake-data-access) | Requires `storage.azure.com` token, not Fabric token | | Job Execution | [COMMON-CORE.md § Job Execution](../../common/COMMON-CORE.md#job-execution) | | | Capacity Management | [COMMON-CORE.md § Capacity Management](../../common/COMMON-CORE.md#capacity-management) | | | Gotchas & Troubleshooting | [COMMON-CORE.md § Gotchas & Troubleshooting](../../common/COMMON-CORE.md#gotchas--troubleshooting) | | | Best Practices | [COMMON-CORE.md § Best Practices](../../common/COMMON-CORE.md#best-practices) | | | Tool Selection Rationale | [COMMON-CLI.md § Tool Selection Rationale](../../common/COMMON-CLI.md#tool-selection-rationale) | | | Authentication Recipes | [COMMON-CLI.md § Authentication Recipes](../../common/COMMON-CLI.md#authentication-recipes) | `az login` flows and token acquisition | | Fabric Control-Plane API via `az rest` | [COMMON-CLI.md § Fabric Control-Plane API via az rest](../../common/COMMON-CLI.md#fabric-control-plane-api-via-az-rest) | **Always pass `--resource https://api.fabric.microsoft.com`** or `az rest` fails | | Pagination Pattern | [COMMON-CLI.md § Pagination Pattern](../../common/COMMON-CLI.md#pagination-pattern) | | | Long-Running Operations (LRO) Pattern | [COMMON-CLI.md § Long-Running Operations (LRO) Pattern](../../common/COMMON-CLI.md#long-running-operations-lro-pattern) | | | OneLake Data Access via `curl` | [COMMON-CLI.md § OneLake Data Access via curl](../../common/COMMON-CLI.md#onelake-data-access-via-curl) | Use `curl` not `az rest` (different token audience) | | Job Execution (CLI) | [COMMON-CLI.md § Job Execution](../../common/COMMON-CLI.md#job-execution) | | | OneLake Shortcuts | [COMMON-CLI.md § OneLake Shortcuts](../../common/COMMON-CLI.md#onelake-shortcuts) | | | Capacity Management (CLI) | [COMMON-CLI.md § Capacity Management](../../common/COMMON-CLI.md#capacity-management) | | | Composite Recipes | [COMMON-CLI.md § Composite Recipes](../../common/COMMON-CLI.md#composite-recipes) | | | Gotchas & Troubleshooting (CLI-Specific) | [COMMON-CLI.md § Gotchas & Troubleshooting (CLI-Specific)](../../common/COMMON-CLI.md#gotchas--troubleshooting-cli-specific) | `az rest` audience, shell escaping, token expiry | | Quick Reference: `az rest` Template | [COMMON-CLI.md § Quick Reference: az rest Template](../../common/COMMON-CLI.md#quick-reference-az-rest-template) | | | Quick Reference: Token Audience / CLI Tool Matrix | [COMMON-CLI.md § Quick Reference: Token Audience ↔ CLI Tool Matrix](../../common/COMMON-CLI.md#quick-reference-token-audience--cli-tool-matrix) | Which `--resource` + tool for each service | | Connection Fundamentals | [EVENTHOUSE-CONSUMPTION-CORE.md § Connection Fundamentals](../../common/EVENTHOUSE-CONSUMPTION-CORE.md#connection-fundamentals) | Cluster URI discovery, `az rest`, REST API | | Schema Discovery and Security | [EVENTHOUSE-CONSUMPTION-CORE.md § Schema Discovery and Security](../../common/EVENTHOUSE-CONSUMPTION-CORE.md#schema-discovery-and-security) | Schema Discovery, Security — workspace roles + KQL DB roles | | Monitoring and Diagnostics | [EVENTHOUSE-CONSUMPTION-CORE.md § Monitoring and Diagnostics](../../common/EVENTHOUSE-CONSUMPTION-CORE.md#monitoring-and-diagnostics) | | | Performance Best Practices | [EVENTHOUSE-CONSUMPTION-CORE.md § Performance Best Practices](../../common/EVENTHOUSE-CONSUMPTION-CORE.md#performance-best-practices) | **Read before writing KQL** — time filters, `has` vs `contains` | | Common Consumption Patterns | [EVENTHOUSE-CONSUMPTION-CORE.md § Common Consumption Patterns](../../common/EVENTHOUSE-CONSUMPTION-CORE.md#common-consumption-patterns) | Time-series, Top-N, percentile, dynamic fields | | Gotchas, Troubleshooting, and Quick Reference | [EVENTHOUSE-CONSUMPTION-CORE.md § Gotchas, Troubleshooting, and Quick Reference](../../common/EVENTHOUSE-CONSUMPTION-CORE.md#gotchas-troubleshooting-and-quick-reference) | Gotchas and Troubleshooting (12 issues), Quick Reference: Consumption Capabilities by Scenario | | Table and Column Discovery | [discovery-queries.md § Table and Column Discovery](references/discovery-queries.md#table-and-column-discovery) | Table Discovery, Column Statistics | | Function and View Discovery | [discovery-queries.md § Function and View Discovery](references/discovery-queries.md#function-and-view-discovery) | Function Discovery, Materialized View Discovery | | Policy Discovery | [discovery-queries.md § Policy Discovery](references/discovery-queries.md#policy-discovery) | | | External Tables and Ingestion Mappings | [discovery-queries.md § External Tables and Ingestion Mappings](references/discovery-queries.md#external-tables-and-ingestion-mappings) | External Table Discovery, Ingestion Mapping Discovery | | Security Discovery | [discovery-queries.md § Security Discovery](references/discovery-queries.md#security-discovery) | | | Database Overview Script | [discovery-queries.md § Database Overview Script](references/discovery-queries.md#database-overview-script) | | | Tool Stack | [SKILL.md § Tool Stack](#tool-stack) | | | Connection | [SKILL.md § Connection](#connection) | eventhouse-specific `az rest` connection steps | | Agentic Exploration ("Chat With My Data") | [SKILL.md § Agentic Exploration](#agentic-exploration) | **Start here** for data exploration | | Running Queries | [SKILL.md § Running Queries](#running-queries) | `az rest`, output formatting, export | | Monitoring | [SKILL.md § Monitoring](#monitoring) | | | Must / Prefer / Avoid / Troubleshooting | [SKILL.md § Must / Prefer / Avoid / Troubleshooting](#must--prefer--avoid--troubleshooting) | **MUST DO / AVOID / PREFER** checklists | | Examples | [SKILL.md § Examples](#examples) | | | Agent Integration Notes | [SKILL.md § Agent Integration Notes](#agent-integration-notes) | | --- ## Tool Stack | Tool | Purpose | Install | |---|---|---| | **az cli** | KQL queries and management commands via Kusto REST API; Fabric control-plane discovery | `winget install Microsoft.AzureCLI` | | **jq** | JSON processing and output formatting | `winget install jqlang.jq` | ## Connection ### Step 1 — Discover KQL Database Query URI ```bash # Get workspace ID (if not known) WS_ID=$(az rest --method GET \ --url "https://api.fabric.microsoft.com/v1/workspaces" \ --resource "https://api.fabric.microsoft.com" \ | jq -r '.value[] | select(.displayName=="MyWorkspace") | .id') # List KQL Databases and get connection properties az rest --method GET \ --url "https://api.fabric.microsoft.com/v1/workspaces/${WS_ID}/kqlDatabases" \ --resource "https://api.fabric.microsoft.com" \ | jq '.value[] | {name: .displayName, id: .id, queryUri: .properties.queryServiceUri, dbName: .properties.databaseName}' ``` ### Step 2 — Set Connection Variables ```bash CLUSTER_URI="https://.kusto.fabric.microsoft.com" DB_NAME="MyKqlDatabase" ``` ### Step 3 — Verify Connection > **Important — body file pattern**: KQL queries contain `|` (pipe) characters which break shell > escaping in both bash and PowerShell. **Always write the JSON body to a temp file** and reference > it with `--body @`. This is the recommended approach for all `az rest` KQL calls. > On PowerShell, use `@{db="X";csl="..."} | ConvertTo-Json -Compress | Out-File $env:TEMP\kql_body.json -Encoding utf8NoBOM` then `--body "@$env:TEMP\kql_body.json"`. ```bash # Write body to temp file (avoids pipe escaping issues) cat > /tmp/kql_body.json << 'EOF' {"db":"MyKqlDatabase","csl":"print Message = 'Connected successfully', Cluster = current_cluster_endpoint(), Timestamp = now()"} EOF az rest --method POST \ --url "${CLUSTER_URI}/v1/rest/query" \ --resource "https://kusto.kusto.windows.net" \ --headers "Content-Type=application/json" \ --body @/tmp/kql_body.json \ | jq '.Tables[0].Rows' ``` --- ## Agentic Exploration ### "Chat With My Data" — Discovery Sequence When the user asks to explore or query an Eventhouse without specifying tables: ```kql Step 1 → .show tables // discover tables Step 2 → .show table schema as json // understand columns + types Step 3 →
| take 10 // see sample data Step 4 →
| summarize count() by bin(Timestamp, 1h) | render timechart // shape of data Step 5 → Formulate targeted query based on user's question ``` ### Schema-Aware Query Generation After schema discovery, generate queries using actual column names and types: ```kql // Example: user asks "show me errors in the last hour" // After discovering table "AppEvents" with columns: Timestamp, Level, Message, Source AppEvents | where Timestamp > ago(1h) | where Level == "Error" | summarize ErrorCount = count() by Source, bin(Timestamp, 5m) | order by ErrorCount desc ``` --- ## Running Queries ### Via `az rest` > **Always use the temp-file pattern** for `--body` — KQL pipes (`|`) break inline shell escaping. ```bash # Run a KQL query cat > /tmp/kql_body.json << 'EOF' {"db":"MyDB","csl":"Events | where Timestamp > ago(1h) | count"} EOF az rest --method POST \ --url "${CLUSTER_URI}/v1/rest/query" \ --resource "https://kusto.kusto.windows.net" \ --headers "Content-Type=application/json" \ --body @/tmp/kql_body.json \ | jq '.Tables[0].Rows' ``` ### Output Formatting ```bash # Pretty-print results as a table with jq cat > /tmp/kql_body.json << 'EOF' {"db":"MyDB","csl":".show tables"} EOF az rest --method POST \ --url "${CLUSTER_URI}/v1/rest/query" \ --resource "https://kusto.kusto.windows.net" \ --headers "Content-Type=application/json" \ --body @/tmp/kql_body.json \ | jq '.Tables[0] | [.Columns[].ColumnName] as $cols | .Rows[] | [$cols, .] | transpose | map({(.[0]): .[1]}) | add' # Save results to file cat > /tmp/kql_body.json << 'EOF' {"db":"MyDB","csl":"Events | where Timestamp > ago(1h) | summarize count() by EventType"} EOF az rest --method POST \ --url "${CLUSTER_URI}/v1/rest/query" \ --resource "https://kusto.kusto.windows.net" \ --headers "Content-Type=application/json" \ --body @/tmp/kql_body.json \ --output-file results.json ``` --- ## Monitoring ```kql // Active queries .show queries // Recent commands (last hour) .show commands | where StartedOn > ago(1h) | project StartedOn, CommandType, Text = substring(Text, 0, 80), Duration, State | order by StartedOn desc // Ingestion failures (for context when data seems stale) .show ingestion failures | where FailedOn > ago(24h) | summarize count() by ErrorCode | top 5 by count_ ``` --- ## Must / Prefer / Avoid / Troubleshooting ### Must - **Always include time filters** — `where Timestamp > ago(...)` must be present on time-series tables. - **Discover schema before querying** — run `.show tables` and `.show table T schema as json` first. - **Use `has` for term search** — indexed and fast; only fall back to `contains` for substring needs. - **Verify cluster URI** — KQL Database URIs are per-item; always resolve via Fabric REST API. ### Prefer - **`az rest`** for CLI query sessions; **Fabric KQL MCP server** for agent-integrated workflows. - **`project` early** to drop unneeded columns before aggregation. - **`materialize()`** when a sub-expression is used multiple times. - **`take 100`** for initial exploration; avoid full table scans. - **`render timechart`** for time-series; `render piechart` for distribution. ### Avoid - **`contains`** on large tables — full scan, not indexed. Use `has` or `has_cs`. - **`join`** without filtering both sides first — causes memory explosion. - **`SELECT *`** equivalent (`project` all columns) on wide tables. - **Missing `bin()`** in time-series `summarize` — produces one row per unique timestamp. - **Hardcoded cluster URIs** — always resolve from Fabric REST API or environment variables. ### Troubleshooting | Symptom | Fix | |---|---| | `az rest` auth fails | Run `az login` first; ensure `--resource "https://kusto.kusto.windows.net"` is set | | Empty results on valid table | Check database context; may need `database("name").table` | | Query timeout | Add tighter time filter; check `.show queries` for competing queries | | `Forbidden (403)` | Request `viewer` role on the KQL Database | | Results truncated | Default limit is 500K rows; add `set truncationmaxrecords = N;` before query | | KQL pipe `\|` breaks PowerShell or bash | **Never inline KQL in `--body`**. Write JSON to a temp file and use `--body @file.json` (see [Running Queries](#running-queries)) | --- ## Examples ### Example 1: Discover and Query ```bash # 1. Set connection variables (after discovering URI via Step 1) CLUSTER_URI="https://.kusto.fabric.microsoft.com" DB_NAME="SalesDB" # 2. Discover tables cat > /tmp/kql_body.json << EOF {"db":"${DB_NAME}","csl":".show tables"} EOF az rest --method POST \ --url "${CLUSTER_URI}/v1/rest/query" \ --resource "https://kusto.kusto.windows.net" \ --headers "Content-Type=application/json" \ --body @/tmp/kql_body.json \ | jq '.Tables[0].Rows' # 3. Explore schema cat > /tmp/kql_body.json << EOF {"db":"${DB_NAME}","csl":".show table Orders schema as json"} EOF az rest --method POST \ --url "${CLUSTER_URI}/v1/rest/query" \ --resource "https://kusto.kusto.windows.net" \ --headers "Content-Type=application/json" \ --body @/tmp/kql_body.json \ | jq '.Tables[0].Rows' # 4. Sample data cat > /tmp/kql_body.json << EOF {"db":"${DB_NAME}","csl":"Orders | take 10"} EOF az rest --method POST \ --url "${CLUSTER_URI}/v1/rest/query" \ --resource "https://kusto.kusto.windows.net" \ --headers "Content-Type=application/json" \ --body @/tmp/kql_body.json \ | jq '.Tables[0].Rows' ``` ```kql // 5. Analytical query (via az rest --body @file) Orders | where OrderDate > ago(30d) | summarize TotalOrders = count(), TotalRevenue = sum(Amount) by bin(OrderDate, 1d) | render timechart ``` ### Example 2: Cross-Database Query ```kql // Query across KQL databases in the same Eventhouse let orders = database("SalesDB").Orders | where OrderDate > ago(7d); let products = database("CatalogDB").Products; orders | join kind=inner (products) on ProductId | summarize Revenue = sum(Amount) by ProductName | top 10 by Revenue desc ``` ### Example 3: Export Results to File ```bash # Run query and save results to JSON cat > /tmp/kql_body.json << 'EOF' {"db":"MyDB","csl":"Events | where Timestamp > ago(1d) | summarize count() by EventType"} EOF az rest --method POST \ --url "${CLUSTER_URI}/v1/rest/query" \ --resource "https://kusto.kusto.windows.net" \ --headers "Content-Type=application/json" \ --body @/tmp/kql_body.json \ --output-file results.json # Convert to CSV with jq cat results.json \ | jq -r '.Tables[0] | (.Columns | map(.ColumnName)), (.Rows[]) | @csv' > results.csv ``` --- ## Agent Integration Notes - This skill is **read-only** — it does not create, alter, or drop database objects. - For authoring operations (table management, ingestion, policies), delegate to **eventhouse-authoring-cli**. - For cross-workload orchestration (Spark + SQL + KQL), delegate to the **FabricDataEngineer** agent. - The **Fabric KQL MCP server** (`fabric-kql` in `mcp-setup/mcp-config-template.json`) can be used as an alternative to `az rest` for agent-integrated query execution.