Skip to content

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-Token requests a per-resource access token and unwraps the SecureString returned by Az 12+.
  • Invoke-KustoMgmt POSTs to <cluster>/v1/rest/mgmt with { db, csl }.
  • Provision-Schema wraps 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-RowCounts runs a union withsource=T … | summarize Rows=count() by T probe and prints per-table counts.
  • Schedule-Notebook POSTs a Cron schedule (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.