Deploy¶
End-to-end provisioning order. Do these once, in sequence. Step 1 (tenant + identity) has its own detailed page; this page is the runbook that ties the components together.
flowchart LR
s1[1 · Tenant + identity] --> s2[2 · Eventhouse schema]
s2 --> s3[3 · Collector notebook]
s3 --> s4[4 · WinForms app]
s2 --> s5[5 · Dashboard]
1. Tenant + identity¶
Enable the required tenant settings, register the collector app (and the review public-client app), grant permissions, and provision the observability Eventhouse. Full walkthrough: Tenant & identity setup.
2. Eventhouse schema¶
Run fabric/kql/01_tables.kql then 02_policies.kql in the KQL database query editor —
or apply both automatically with the deploy script:
./deploy/Deploy-FdaObservability.ps1 `
-ClusterQueryUri https://<cluster>.<region>.kusto.fabric.microsoft.com `
-ProvisionSchema
3. Collector¶
Import fabric/notebooks/FDA_Collector.py into a Fabric notebook, set the parameters
cell, run once interactively, then schedule (e.g. every 15–60 min). You can schedule from the deploy script:
./deploy/Deploy-FdaObservability.ps1 `
-ClusterQueryUri https://<cluster>.<region>.kusto.fabric.microsoft.com `
-WorkspaceId <ws-guid> -NotebookId <nb-guid> -IntervalMinutes 15 -ScheduleNotebook
4. WinForms app¶
Open src/FdaObservability.sln, build, run, and complete the in-app Configuration
dialog (identity / Eventhouse / capacity / workspace / agent). Then review.
5. Dashboard¶
Import fabric/dashboards/FDA_Observability_RTI_Dashboard.json and set its data source
to the Eventhouse.
The deploy script¶
deploy/Deploy-FdaObservability.ps1
performs two idempotent actions, selectable via switches. With neither switch, it does both. A connectivity
check always runs first.
| Switch | Action |
|---|---|
-ProvisionSchema |
Runs 01_tables.kql then 02_policies.kql against the Eventhouse KQL DB via the Kusto management REST endpoint (.execute database script with ContinueOnErrors=true), then prints table row counts. |
-ScheduleNotebook |
Creates/updates a Fabric Cron job schedule on the collector notebook via the Fabric REST API. |
Parameters¶
| Parameter | Required | Default | Description |
|---|---|---|---|
-ClusterQueryUri |
✅ | — | Eventhouse Query URI, https://<cluster>.<region>.kusto.fabric.microsoft.com |
-Database |
FDA_Observability_EH |
KQL database name | |
-KqlDir |
..\fabric\kql |
Folder containing the .kql files |
|
-WorkspaceId |
for schedule | — | Fabric workspace GUID hosting the collector notebook |
-NotebookId |
for schedule | — | Collector notebook item GUID |
-IntervalMinutes |
30 |
Collector schedule cadence | |
-FabricApiBase |
https://api.fabric.microsoft.com/v1 |
Fabric REST base | |
-TimeZone |
UTC |
Schedule time zone (localTimeZoneId) |
|
-ProvisionSchema |
— | Switch — provision schema only | |
-ScheduleNotebook |
— | Switch — schedule notebook only |
Auth & prerequisites¶
- PowerShell 7+ and Az.Accounts (
Install-Module Az.Accounts -Scope CurrentUser). - Uses your interactive Azure login; tokens are requested per resource (the Kusto cluster and the Fabric API). No secrets are stored.
- The signed-in identity needs Database Admin/Ingestor on the KQL DB and Member/Admin on the workspace to create a schedule.
How it works¶
Get-Tokenrequests a per-resource access token and unwraps theSecureStringreturned by Az 12+.Invoke-KustoMgmtPOSTs to<cluster>/v1/rest/mgmtwith{ db, csl }.Provision-Schemawraps each file as.execute database script with (ContinueOnErrors=true) <| <file>so pre-existing objects don't abort the run; it reports any commands that flagged errors (usually pre-existing objects).Show-RowCountsruns aunion withsource=T … | summarize Rows=count() by Tprobe and prints per-table counts.Schedule-NotebookPOSTs aCronschedule (interval = IntervalMinutes, 3-year end date) to…/items/<NotebookId>/jobs/RunNotebook/schedules; if one already exists it lists the existing schedules instead of failing.
Idempotent by design
.create-merge tables, ContinueOnErrors=true, and the "already exists → list instead of fail" schedule path
mean you can re-run the script safely after configuration changes.
Verify¶
After the schema and a collector run, in the KQL query editor:
FdaInteractions | take 50
FdaInteractions | summarize count() by MatchConfidence, bin(Timestamp, 1h)
If FdaInteractions has rows with GeneratedDax populated and MatchConfidence != "Unmatched", the pipeline
works. More checks in Troubleshooting and Analyst queries.