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

Loom first deploy — operator runbook

Start with Prerequisites and first deploy. That is the authoritative page — prerequisites → deploy → verify, greenfield and brownfield, all four clouds. This runbook is the operator-side detail it links to, including the tenant-state decisions in phase 2.

Step-by-step for an operator standing up CSA Loom in a new subscription for the first time. Assumes you have:

  • Owner on the target subscription (or Contributor + User Access Administrator)
  • Ability to create an Entra group + service principal in the tenant
  • Decision authority on shared services (Purview, AI Foundry Hub)

Total wall time: roughly 60-90 minutes including provision + verification.

Phase 1 — secrets bootstrap (10 min)

Follow Secrets bootstrap per boundary. You need the deploy SP + admin group object ID + 5 GitHub secrets per boundary.

Verify by dispatching the workflow in whatif-only mode (no spend):

# `region` is REQUIRED and has no default — GitHub REJECTS a dispatch that
# omits it. An earlier revision of this runbook gave this command without it,
# which cannot run.
gh workflow run deploy-fiab-commercial.yml -f run_mode=whatif-only -f region=<region>
gh run watch $(gh run list --workflow deploy-fiab-commercial.yml --limit 1 --json databaseId --jq '.[0].databaseId') --exit-status

Expected: green run in under 1 minute. If red, see Deploy failure runbook.

Phase 2 — tenant-state decisions (5 min, async-bounded)

Walk these checklists BEFORE any real provisioning:

Purview

See Purview tenant-existing-account reuse. The default is per boundary, not global: true in commercial.bicepparam, commercial-full.bicepparam (via LOOM_PURVIEW_ENABLED), gcc.bicepparam and tenant-dmlz.bicepparam; false in gcc-high.bicepparam (reuse the tenant Enterprise Purview) and il5.bicepparam (Atlas on AKS instead). Decide:

  • Greenfield tenant → leave true (or flip it on for GCC-High)
  • Existing Purview → adopt it: set EXISTING_PURVIEW (+ _RG, _SUB). That is the whole step — provisionPurview is derived as purviewEnabled && adoptMode(adopt,'purview') == 'create', so an adopt decision suppresses creation on its own and no enable-flag override is needed. (It used to: EXISTING_PURVIEW alone bound the Console and still deployed a second account, failing EnterpriseTenantAlreadyExists.) See Brownfield deployment
  • Region has no Purview (e.g. centralus) → purviewEnabled=false; the Loom catalog falls back to its Azure-native backend

AI Foundry Hub

Loom creates one Hub per Admin Plane. If your tenant already has a designated AI Foundry Hub:

  • Reuse → set foundryPortalEnabled = false (uses Azure ML classic Workspace instead; Loom Data Agents still work)
  • Create new → keep default

Capacity SKU

Drives Databricks cluster size + ADX SKU + Power BI capacity. F8 is the recommended starting point for production; F2/F4 for dev/test.

param capacitySku = 'F8'   // F2 / F4 / F8 / F32 / F64 / F128 / F512

Multi-sub vs single-sub

  • single-sub (default) — Admin Plane + DLZ in same sub. Right for most starts.
  • multi-sub — One Admin Plane sub + one sub per DLZ. Required when DLZs have different audit boundaries (e.g., one DLZ for finance under SOX, another for HR under different controls).
param deploymentMode = 'single-sub'   // or 'multi-sub'

For multi-sub, pre-create the DLZ RGs in the target subs:

scripts/csa-loom/bootstrap-dlz-rgs.sh eastus2 \
  "<sub-id-1>,<sub-id-2>" \
  "finance,procurement"

Phase 3 — full provision (30-60 min)

What a full dispatch does now. keep_resources defaults to true (#3028) — a full run RECONCILES the estate and tears nothing down; teardown additionally requires confirm_teardown_rg to exactly equal the resolved admin resource group. deploy_apps_enabled defaults to true (#3332) — the Container Apps are created/updated and every LOOM_* env var is re-rendered on them, so a full run actually produces a Console you can open.

For a real from-scratch install, follow Greenfield deployment — the three-phase az deployment sub create path. On a brand-new subscription the ACR is created EMPTY, so the app phase has to come after the image build; dispatch this workflow with -f deploy_apps_enabled=false for phase 1 if you want the workflow to drive that path.

Once decisions are locked, dispatch the full deploy:

gh workflow run deploy-fiab-commercial \
  -f run_mode=full \
  -f region=<the estate's region>

# Watch — provisioning takes ~25-45 minutes for first run
RUN_ID=$(gh run list --workflow deploy-fiab-commercial --limit 1 --json databaseId --jq '.[0].databaseId')
gh run watch $RUN_ID --exit-status

deploy_apps_enabled=true (the default) only works when the app images already exist in the deployment's ACR. On a from-scratch subscription the ACR is created empty — run the image phase first (Greenfield, phase 2), or dispatch phase 1 with -f deploy_apps_enabled=false. The run's image preflight checks the registry before anything reaches ARM rather than failing mid-deploy on MANIFEST_UNKNOWN.

If the target subscription already holds a Loom hub, add -f allow_existing_hub=true to reconcile it rather than being rejected by the topology guard.

What this provisions: - Admin Plane RG with all 12 sub-modules (network, identity, KV, monitoring, ACR, container platform, AI Search, AI Foundry Hub, APIM, catalog, AI defense, app deployments) - Single-sub DLZ RG with all 7 sub-modules (network, storage, Databricks, Synapse, Event Hubs, ADX database, Cosmos) - Container Apps for every app — unless you set deploy_apps_enabled=false - Smoke test executes against the live apps - No teardown — unless you set keep_resources=false AND type confirm_teardown_rg

Phase 4 — verification (15 min)

After provisioning succeeds:

Open Loom Console

# Get the Console URL from azd output
CONSOLE_URL=$(az deployment sub show \
  --name csa-loom-ci-$RUN_ID \
  --query "properties.outputs.consoleUrl.value" -o tsv)
echo "$CONSOLE_URL"

Walk through every pane: - [ ] Workspaces — at least the default workspace shows - [ ] Lakehouse — bronze/silver/gold containers visible - [ ] Warehouse — sample T-SQL query returns - [ ] Notebook — empty notebook opens; new cell saves - [ ] Semantic Model — refresh policy table loads - [ ] Activator — example rule list loads - [ ] Data Agent — chat returns a clarifying question to a basic prompt - [ ] Setup Wizard — opens to intro step

Check telemetry flowing

LAW_NAME="law-csa-loom-eastus2"
RG="rg-csa-loom-admin-eastus2"

az monitor log-analytics query \
  --workspace "$(az monitor log-analytics workspace show --resource-group $RG --workspace-name $LAW_NAME --query customerId -o tsv)" \
  --analytics-query "AppRequests | where TimeGenerated > ago(15m) | summarize count() by AppRoleName" \
  -o table

Expected: rows for every deployed service (loom-console, loom-orchestrator, loom-mcp, etc.).

Check audit logs landing

Wait 10 minutes after first user action, then query per Synapse audit query pack and Loom LAW monitoring + alert pack.

Phase 5 — operational handoff

After verification:

  • Add the admin group to PIM-for-Groups for JIT Contributor elevation (used by Loom Setup Wizard for DLZ creation)
  • Configure the Teams webhook secret (ops-teams-webhook in Key Vault) for AI defense playbook + Activator dispatch
  • Schedule cost reports per LAW monitoring Cost monitoring section
  • Run the workshop in 5-day Federal CoE with operators

Backout

If the first deploy is going badly and you want to start over:

# Teardown script (or wait for the workflow's automatic teardown)
RG="rg-csa-loom-admin-eastus2" \
  bash .github/scripts/fiab-teardown.sh

# Verify all Loom RGs are gone
az group list --query "[?starts_with(name, 'rg-csa-loom-')].name" -o tsv

The teardown script purges Key Vaults + Managed HSMs before RG delete, so subsequent runs don't collide on soft-deleted names.