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). IdempotentcreateIfNotExistsforworkspaces(PK/tenantId) +items(PK/workspaceId).lib/types/workspace.ts—Workspace,WorkspaceItemtypes.- BFF routes:
/api/workspacesGET/POST (list, create)/api/workspaces/[id]GET/PATCH/DELETE (cascade-deletes items)/api/workspaces/[id]/itemsGET/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 explicitManagedIdentityCredential({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 againstadb-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_HOSTNAMEset - 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/ServicePrincipalswith the UAMI's applicationId. Task #16 tracks this. - Commit: bundled into
fdba1b2c
APIM API/Product/Policy editors¶
lib/azure/apim-client.ts— ARM REST againstapim-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.0added, externalized in next.config - Commit: bundled into
fdba1b2c
Bicep gap fixes (Gap 1-4)¶
- Gap 1: per-app
imagetag viaappImageTagsobject (console, mcp, orchestrator, activator, mirroring, directLake) - Gap 2:
loom-consoleenv 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
externalflag — loom-console=true (Front Door), others=false - Gap 4: new
modules/admin-plane/adx-cluster.bicep(Dev SKUStandard_E2a_v4~$140/mo). DLZ DB now has a parent. - Plus: per-app
secretsarray support + MSAL secret rename toloom-msal-client-secret - Commits:
3d88d6e1,d1252d8d
Push-button deploy¶
platform/fiab/bicep/params/commercial-full.bicepparam— all flags ON, Loom Admins group716f5ec5-..., MSAL secret via env var- Attempt 1 (
loom-pushbutton-20260524-180441): partial fail at network module (DNS link conflict with v2.0 manually-createdlink-hub-vnet-csa-loom-hub-eastus2). Cancelled. Conflicting links deleted. - Attempt 2 (
loom-pushbutton-r2-...): RUNNING NOW
Open follow-ups (next session)¶
-
Databricks SCIM bootstrap (Task #16): a workspace admin must visit
https://adb-7405613013893759.19.azuredatabricks.netonce 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"}]}' -
APIM RBAC (depends on APIM provisioning completing): run
bash scripts/csa-loom/grant-apim-rbac.sh. The script self-gates on Succeeded state. -
v2.1 image deploy: GHA build is in flight (
26374356046). When done: -
Still-stubbed editors (visual only — to be wired in next iterations):
synapse-spark-pool,synapse-pipeline(Synapse Pipelines REST + Spark batch API)databricks-notebook,databricks-job,databricks-cluster(Databricks Jobs API + Clusters API)adf-pipeline,adf-dataset,adf-trigger(ADF REST — DLZ doesn't yet deploy an ADF; need module)usql-job(ADLA — legacy, leave stub)- All
phase2-misc-editors(sparkJobDef, environment, copy-job, dbt-job) - All
phase3-editors(warehouse, eventhouse, kql, semantic models, reports, dashboards) - All
phase4-editors(ml, graphql, plans, maps, agents) -
notebook-editor,mirrored-database-editor,data-pipeline-editor,dataflow-gen2-editor(Fabric editors — need Fabric REST not yet wired) -
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.
-
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.