Skip to content
CSA Loom — the Microsoft Fabric experience for Azure tenants where Fabric isn't yet available: lakehouses, warehouses, notebooks, semantic models, Activator rules, Data Agents, across Commercial, GCC, GCC-High, and DoD IL5

CSA Loom — v2.1 push-button + foundation persistence + multi-service editor wiring (2026-05-24)

Status (this session): infra deploy retry in flight (bqhksj7ns cancelled, beln3pmgo retrying after fixing DNS link conflict). v2.1 image build in flight (GHA run 26374356046). All 4 parallel editor agents complete.

Branch: access-patterns-vpn-agw-fd (pushed through 7a531f3a)

What shipped this session (real, not vaporware)

Foundation persistence (Cosmos)

  • lib/azure/cosmos-client.ts — singleton CosmosClient via UAMI (ChainedTokenCredential → ManagedIdentityCredential + DefaultAzureCredential). Idempotent createIfNotExists for workspaces (PK /tenantId) + items (PK /workspaceId).
  • lib/types/workspace.tsWorkspace, WorkspaceItem types.
  • BFF routes:
  • /api/workspaces GET/POST (list, create)
  • /api/workspaces/[id] GET/PATCH/DELETE (cascade-deletes items)
  • /api/workspaces/[id]/items GET/POST
  • /api/items/[type]/[id] GET/PATCH/DELETE (tenant-verified)
  • UI: app/workspaces/[id]/page.tsx (new) + lib/panes/workspaces.tsx (rewrite). Real React Query, "Create workspace" dialog with Name/Description/Capacity/Domain → POST → route. "+New item" Combobox over FABRIC_ITEM_TYPES → POST → route.
  • RBAC: Cosmos DB Built-in Data Contributor granted to Console UAMI.
  • Commit: 3c18c798

Synapse Dedicated + Serverless (already from earlier this session)

  • lib/azure/synapse-sql-client.ts (TDS + AAD) — fixed credential acquisition with explicit ManagedIdentityCredential({clientId: LOOM_UAMI_CLIENT_ID})
  • lib/azure/synapse-pool-arm.ts (ARM REST pause/resume)
  • BFF routes under /api/items/synapse-{dedicated,serverless}-sql-pool/[id]/{query,schema,state,resume}
  • lib/editors/synapse-sql-editors.tsx — live editors with real Run, results table, Resume-on-demand
  • Bicep: Dedicated pool loompool (DW100c LRS) + auto-pause Logic App + PEs + AAD admin + ARM Contributor
  • Commits: 966c1251, d1252d8d

Databricks SQL Warehouse editor

  • lib/azure/databricks-client.ts — AAD-auth REST against adb-7405613013893759.19.azuredatabricks.net. listWarehouses / getWarehouse / start / stop / executeStatement (polls SUCCEEDED).
  • BFF routes under /api/items/databricks-sql-warehouse/[id]/{warehouses,query,schema,state,start}
  • lib/editors/databricks-editors.tsx — DatabricksSqlWarehouseEditor: warehouse dropdown, state badge, Start/Stop with 5s polling, lazy 3-level Unity Catalog tree, real Run.
  • Container env: LOOM_DATABRICKS_HOSTNAME set
  • RBAC: ARM Contributor on workspace granted to Console UAMI
  • Known blocker: UAMI not yet registered as Databricks workspace SP via SCIM — REST calls 403 until first-time human admin logs in to the workspace and POSTs to /api/2.0/preview/scim/v2/ServicePrincipals with the UAMI's applicationId. Task #16 tracks this.
  • Commit: bundled into fdba1b2c

APIM API/Product/Policy editors

  • lib/azure/apim-client.ts — ARM REST against apim-csa-loom-eastus2 (api-version 2024-06-01-preview). list/get/upsert/delete for APIs/Products/Policies + Subscriptions. 404 returns null cleanly.
  • BFF routes under /api/items/apim-api/{list,[id],operations,spec}, /api/items/apim-product/{list,[id]}, /api/items/apim-policy/[id]
  • lib/editors/apim-editors.tsx — all 4 editors (Api/Product/Policy/DataProduct) rewritten: real fetch/save, Fluent UI v9, DOMParser XML validation for policies.
  • scripts/csa-loom/grant-apim-rbac.sh — runs after APIM provisions, grants "API Management Service Contributor" to UAMI (self-gates on provisioningState=Succeeded)
  • Commit: 6af32fd2

Lakehouse editor (ADLS Gen2)

  • lib/azure/adls-client.ts — DataLakeServiceClient via UAMI. listContainers (probes via exists), listPaths, getMetadata, uploadFile, deletePath, createDirectory.
  • BFF routes under /api/lakehouse/{containers,paths,preview,upload,path}
  • lib/editors/lakehouse-editor.tsx — full rewrite: real container tree with lazy per-prefix loading + spinners; Files/Preview/SQL tabs; Upload (file picker), New folder, Delete with confirm, Refresh.
  • RBAC: Storage Blob Data Contributor granted to Console UAMI
  • Deps: @azure/storage-file-datalake@^12.27.0 added, externalized in next.config
  • Commit: bundled into fdba1b2c

Bicep gap fixes (Gap 1-4)

  • Gap 1: per-app image tag via appImageTags object (console, mcp, orchestrator, activator, mirroring, directLake)
  • Gap 2: loom-console env vars wired in bicep — LOOM_SUBSCRIPTION_ID/DLZ_RG/SYNAPSE_/COSMOS_/BRONZE-SILVER-GOLD-LANDING_URL + UAMI client id + MSAL client id + AZURE_TENANT_ID + AZURE_CLOUD
  • Gap 3: per-app external flag — loom-console=true (Front Door), others=false
  • Gap 4: new modules/admin-plane/adx-cluster.bicep (Dev SKU Standard_E2a_v4 ~$140/mo). DLZ DB now has a parent.
  • Plus: per-app secrets array support + MSAL secret rename to loom-msal-client-secret
  • Commits: 3d88d6e1, d1252d8d

Push-button deploy

  • platform/fiab/bicep/params/commercial-full.bicepparam — all flags ON, Loom Admins group 716f5ec5-..., MSAL secret via env var
  • Attempt 1 (loom-pushbutton-20260524-180441): partial fail at network module (DNS link conflict with v2.0 manually-created link-hub-vnet-csa-loom-hub-eastus2). Cancelled. Conflicting links deleted.
  • Attempt 2 (loom-pushbutton-r2-...): RUNNING NOW

Open follow-ups (next session)

  1. Databricks SCIM bootstrap (Task #16): a workspace admin must visit https://adb-7405613013893759.19.azuredatabricks.net once to bootstrap, then POST UAMI as workspace SP:

    UAMI=c6272de5-3c4e-4b72-8b57-71b2e950209b
    TOKEN=$(az account get-access-token --resource 2ff814a6-3304-4ab8-85cb-cd0e6f879c1d --query accessToken -o tsv)
    curl -X POST "https://adb-7405613013893759.19.azuredatabricks.net/api/2.0/preview/scim/v2/ServicePrincipals" \
      -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/scim+json" \
      -d '{"schemas":["urn:ietf:params:scim:schemas:core:2.0:ServicePrincipal"],"applicationId":"'$UAMI'","displayName":"uami-loom-console-eastus2","entitlements":[{"value":"workspace-access"},{"value":"databricks-sql-access"}]}'
    

  2. APIM RBAC (depends on APIM provisioning completing): run bash scripts/csa-loom/grant-apim-rbac.sh. The script self-gates on Succeeded state.

  3. v2.1 image deploy: GHA build is in flight (26374356046). When done:

    az containerapp update -g rg-csa-loom-admin-eastus2 -n loom-console --image acrloomm56yejezt7bjo.azurecr.io/loom-console:v2.1 --set-env-vars LOOM_VERSION=v2.1 NEXT_PUBLIC_LOOM_VERSION=v2.1
    

  4. Still-stubbed editors (visual only — to be wired in next iterations):

  5. synapse-spark-pool, synapse-pipeline (Synapse Pipelines REST + Spark batch API)
  6. databricks-notebook, databricks-job, databricks-cluster (Databricks Jobs API + Clusters API)
  7. adf-pipeline, adf-dataset, adf-trigger (ADF REST — DLZ doesn't yet deploy an ADF; need module)
  8. usql-job (ADLA — legacy, leave stub)
  9. All phase2-misc-editors (sparkJobDef, environment, copy-job, dbt-job)
  10. All phase3-editors (warehouse, eventhouse, kql, semantic models, reports, dashboards)
  11. All phase4-editors (ml, graphql, plans, maps, agents)
  12. notebook-editor, mirrored-database-editor, data-pipeline-editor, dataflow-gen2-editor (Fabric editors — need Fabric REST not yet wired)

  13. Zero-trust audit: every BFF route validates session, uses UAMI for downstream auth (no user OBO yet), private endpoints in place for KV+ACR+Synapse+Cosmos+Storage. Still TODO: APIM PE, AI Foundry PE (bicep adds these), AI Search PE (bicep adds), AAD Conditional Access policy doc, security scan via Defender for Cloud.

  14. Auth OBO re-enable: current model uses single-identity (UAMI) for all downstream calls. To get per-user RLS in Synapse/Databricks, need to re-introduce homeAccountId in session cookie (carefully — this is what triggered the v1.13–v1.18 cookie saga). Defer until later.

Resume command for next session

You're picking up CSA Loom v2.1. Read docs/fiab/v2.1-handoff.md.
1. Verify infra deploy GREEN: az deployment sub show --name <name> --query properties.provisioningState
2. Deploy v2.1 image: az containerapp update ... --image ...:v2.1 --set-env-vars LOOM_VERSION=v2.1
3. Run Databricks SCIM bootstrap (Task #16)
4. Run APIM RBAC script (Task #14 follow-up)
5. UAT: visit /workspaces (create one), then /workspaces/<id> (+New item → synapse-serverless-sql-pool → real SELECT 1), then /items/lakehouse/<id> (browse bronze container), /items/databricks-sql-warehouse/<id> (after SCIM bootstrap), /items/apim-api/orders-api (real APIM REST).
6. Next slice: wire one of the still-stubbed editor families. Notebook → Databricks Notebook Jobs API is the next highest-leverage one.