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 — architecture diagrams

This page is the rendered home for the CSA Loom topology and deployment diagrams. The committed source for each lives next to this file as a .mmd (Mermaid) — the diff-friendly source of truth — and is mirrored here as a rendered Mermaid block. The hand-drawn hero is committed as topology.excalidraw (open it in excalidraw.com or the VS Code Excalidraw extension).

These diagrams are referenced from the Reference architecture and taught in Tutorial 09 — Tenant topology. The topology shape (one DMLZ hub + N DLZ spokes + one Console) is identical across every cloud boundary — only the per-node service substitution changes (see the per-boundary dispatch matrix).

Diagram Source Teaches
Tenant topology tenant-topology.mmd DMLZ hub + N DLZ spokes + single Console
First-run deploy flow deploy-flow-first-run.mmd Initial provision of Admin Plane + first DLZ
DLZ-attach deploy flow deploy-flow-dlz-attach.mmd Adding a DLZ to an existing Admin Plane
Domain / RBAC model domain-rbac.mmd Entra groups → per-engine roles → workspace UAMIs
Data flow (domain → catalog) data-flow-domain-to-catalog.mmd Domain item → DLZ resources → shared catalog/marketplace
Estate pipeline vs Loom estate-vs-loom.mmd How deploy.yml relates to main.bicep

Tenant topology — one DMLZ hub, N DLZ spokes, one Console

Source: platform/fiab/bicep/main.bicep (adminPlaneRg + module adminPlane; singleDlz in single-sub, dlz[*] over dlzSubscriptionIds in multi-sub).

flowchart TB
    classDef tenant fill:none,stroke:#5C2D91,stroke-width:2px,stroke-dasharray:6 4,color:#5C2D91
    classDef admin fill:#107C10,stroke:#fff,color:#fff,stroke-width:2px
    classDef landing fill:#D83B01,stroke:#fff,color:#fff,stroke-width:2px
    classDef console fill:#0078D4,stroke:#fff,color:#fff,stroke-width:2px

    subgraph Tenant ["Single Microsoft Entra ID tenant — identical Entra groups across every subscription"]
        direction TB
        subgraph Admin ["Admin Plane = Data Management Zone (DMLZ) — one subscription (rg-csa-loom-admin-LOCATION)"]
            direction TB
            Console["Loom Console (one, serves all DLZs)<br/>Container App (Commercial/GCC) or AKS (GCC-H/IL5)"]:::console
            MCP["Self-hosted Azure MCP server"]:::admin
            Hub["Hub VNet + Azure Firewall + Private DNS zones"]:::admin
            Cat["Shared catalog (catalogPrimary):<br/>Unity Catalog / Purview / Atlas-on-AKS"]:::admin
            ADX["Shared ADX cluster (one cluster, N databases)"]:::admin
            Market["API + data-product marketplace"]:::admin
        end
        subgraph DLZ1 ["DLZ — Domain A (rg-csa-loom-dlz-A-LOCATION)"]
            direction TB
            S1["Spoke VNet (peered to Hub)"]:::landing
            R1["ADLS medallion · Synapse Serverless · ADX DB · Power BI · Cosmos · Weave PG"]:::landing
        end
        subgraph DLZ2 ["DLZ — Domain B (rg-csa-loom-dlz-B-LOCATION)"]
            direction TB
            S2["Spoke VNet (peered to Hub)"]:::landing
            R2["...same shape as Domain A..."]:::landing
        end
        DLZN["DLZ — Domain N ... (one per dlzDomainNames entry)"]:::landing
    end
    class Tenant tenant

    Console -->|deploys + manages| DLZ1
    Console -->|deploys + manages| DLZ2
    Console -->|deploys + manages| DLZN
    Hub <-->|VNet peering| S1
    Hub <-->|VNet peering| S2
    R1 -->|domain DB attaches to| ADX
    R2 -->|domain DB attaches to| ADX
    R1 -.scanned by.-> Cat
    R2 -.scanned by.-> Cat
    R1 -->|publish data product| Market
    R2 -->|publish data product| Market

First-run deploy flow

Initial provision: azd up / Deploy-to-Azure button → main.bicep deploys the Admin Plane, then the first DLZ. The deploymentMode parameter (single-sub | multi-sub) is the topology knob.

sequenceDiagram
    autonumber
    actor Op as Operator (CIO / platform team)
    participant Boot as azd up / Deploy-to-Azure button
    participant Main as main.bicep (targetScope=subscription)
    participant AP as module adminPlane → rg-csa-loom-admin-LOCATION
    participant DLZ as module singleDlz / dlz[*]
    participant RBAC as setupOrchestrator*Rbac

    Op->>Boot: git clone + azd up  (or click the README button)
    Boot->>Main: az deployment sub create -p <boundary>.bicepparam<br/>param deploymentMode = 'single-sub' | 'multi-sub'
    Main->>AP: deploy Admin Plane (Hub VNet, Console, MCP, Copilot,<br/>shared catalog, shared ADX cluster)
    AP-->>Main: outputs (hubVnetId, lawId, consolePrincipalId, adxClusterPrincipalId)
    alt deploymentMode == 'single-sub'
        Main->>DLZ: deploy 1 DLZ (domainName='default') into rg-csa-loom-dlz-single-LOCATION
    else deploymentMode == 'multi-sub'
        loop for each (subId, name) in dlzSubscriptionIds / dlzDomainNames
            Main->>DLZ: deploy DLZ into subId / rg-csa-loom-dlz-<name>-LOCATION<br/>(spoke VNet peers to adminPlaneHubVnetId; ADX DB attaches to shared cluster)
        end
    end
    Main->>RBAC: grant Console UAMI Contributor on hub sub (+ each spoke sub if setupOrchestratorEnabled)
    Main-->>Boot: outputs (consoleUrl, mcpServerUrl, adminPlaneHubVnetId)
    Boot-->>Op: "Your CSA Loom Admin Plane + first DLZ are deployed."

DLZ-attach deploy flow

Adding a Data Landing Zone to an existing Admin Plane: the Console "Add Data Landing Zone" action registers the new domain in the DlzOnboardingRegistry, then re-invokes main.bicep with an expanded dlzSubscriptionIds / dlzDomainNames. The admin plane already exists, so the run adds one spoke, peers it to the hub, attaches its ADX database, and grants the spoke RBAC.

sequenceDiagram
    autonumber
    actor Admin as Loom admin
    participant UI as Console "Add Data Landing Zone" (Setup Wizard)
    participant Reg as DlzOnboardingRegistry (Cosmos)
    participant Orch as Setup Orchestrator (Console UAMI)
    participant Main as main.bicep (deploymentMode='multi-sub')
    participant Hub as Existing Admin Plane (DMLZ)
    participant Spoke as New DLZ spoke (rg-csa-loom-dlz-<name>-LOCATION)

    Admin->>UI: Add Data Landing Zone (pick subscription + domain name)
    UI->>Reg: register the new DLZ (domain → target subId)
    Reg-->>Orch: expanded dlzSubscriptionIds[] + dlzDomainNames[]
    Orch->>Main: re-run az deployment sub create (admin plane already present → no-op there)
    Main->>Spoke: deploy the new DLZ module only (new RG / sub)
    Main->>Hub: read adminPlane outputs (hubVnetId, lawId, shared ADX cluster)
    Spoke->>Hub: peer new spoke VNet to adminPlaneHubVnetId
    Spoke->>Hub: attach new ADX database to the shared cluster
    Main->>Spoke: setupOrchestratorSpokeRbac → Console UAMI Contributor on the new spoke sub
    Main-->>Orch: outputs (new DLZ RG, storage, Synapse)
    Orch-->>UI: DLZ ready
    UI-->>Admin: "Domain <name> added — its workspaces can now be created."

Domain / RBAC model

Human Entra groups (one set, tenant-wide) map to per-engine roles. Each PlanDomain becomes a DLZ resource group whose workspace items run as a per-workspace user-assigned managed identity (UAMI).

flowchart LR
    classDef grp fill:#5C2D91,stroke:#fff,color:#fff,stroke-width:2px
    classDef role fill:#107C10,stroke:#fff,color:#fff,stroke-width:2px
    classDef rg fill:#D83B01,stroke:#fff,color:#fff,stroke-width:2px
    classDef id fill:#0078D4,stroke:#fff,color:#fff,stroke-width:2px

    subgraph Entra ["Microsoft Entra ID (single tenant) — Console-managed groups"]
        direction TB
        GAdmin["Loom Admin group<br/>(adminEntraGroupId)"]:::grp
        GWs["Workspace member groups"]:::grp
        GStew["Data Steward group"]:::grp
    end
    subgraph Roles ["Per-engine authorization (boundary-dispatched)"]
        direction TB
        UC["Unity Catalog roles (Commercial)"]:::role
        Syn["Synapse SQL roles (Gov)"]:::role
        Hive["Hive metastore grants (Gov interim)"]:::role
        Blob["Storage Blob Data roles (container-scoped)"]:::role
    end
    subgraph Domain ["PlanDomain → DLZ"]
        direction TB
        RG["Resource group<br/>rg-csa-loom-dlz-DOMAIN-LOCATION"]:::rg
        WS["Loom workspaces (data products)"]:::rg
        UAMI["Workspace UAMI (per workspace)"]:::id
    end

    GAdmin -->|maps to| UC
    GAdmin -->|maps to| Syn
    GWs -->|maps to| UC
    GWs -->|maps to| Hive
    GStew -->|catalog curation| UC
    GStew -->|catalog curation| Syn
    RG --> WS
    WS --> UAMI
    UAMI -->|authenticates to OneLake / Synapse / Power BI| Blob
    UC -.governs.-> WS
    Syn -.governs.-> WS
    Hive -.governs.-> WS

Data flow — domain item → DLZ resources → shared catalog / marketplace

A domain item lands in its DLZ resources, then surfaces through the shared Admin-Plane catalog + ADX cluster + marketplace. Each DLZ attaches its own ADX database to the single shared cluster. Microsoft Fabric is optional / plan-only (no Fabric dependency on the default path).

flowchart LR
    classDef item fill:#5C2D91,stroke:#fff,color:#fff,stroke-width:2px
    classDef dlz fill:#D83B01,stroke:#fff,color:#fff,stroke-width:2px
    classDef shared fill:#107C10,stroke:#fff,color:#fff,stroke-width:2px
    classDef opt fill:#5D5A58,stroke:#fff,color:#fff,stroke-width:2px,stroke-dasharray:5 4

    Item["Domain item<br/>(lakehouse / warehouse / semantic model / pipeline)"]:::item
    subgraph DLZ ["Per-DLZ resources (in the domain's subscription)"]
        direction TB
        ADLS["ADLS Gen2 medallion<br/>Bronze → Silver → Gold"]:::dlz
        Syn["Synapse Serverless SQL<br/>(external tables over Gold)"]:::dlz
        ADXdb["ADX database (this domain)"]:::dlz
        PBI["Power BI Premium workspace"]:::dlz
    end
    subgraph Admin ["Shared Admin Plane (DMLZ) — one of each, for all DLZs"]
        direction TB
        ADXc["Shared ADX cluster<br/>(hosts every DLZ's database)"]:::shared
        Cat["Shared catalog (catalogPrimary)<br/>Unity Catalog / Purview / Atlas-on-AKS"]:::shared
        Market["Data-product + API marketplace"]:::shared
    end
    Fabric["Microsoft Fabric capacity (OPTIONAL, plan-only)<br/>only when fabricEnabled + LOOM_DEFAULT_FABRIC_WORKSPACE set"]:::opt

    Item -->|writes Delta| ADLS
    ADLS --> Syn
    ADLS --> PBI
    ADXdb -->|attaches to| ADXc
    Syn -.cataloged by.-> Cat
    ADLS -.cataloged by.-> Cat
    PBI -.cataloged by.-> Cat
    Cat -->|publish endorsed product| Market
    Market -.optional forward-migrate.-> Fabric

Estate pipeline (deploy.yml) vs the Loom platform (main.bicep)

The ESLZ estate pipeline vends subscriptions / networks; the Loom platform runs inside them. They compose for full federal estates but Loom does not require deploy.ymlazd / the Deploy button can target any existing subscriptions. See Relationship to the ALZ estate pipeline.

flowchart TB
    classDef estate fill:#5D5A58,stroke:#fff,color:#fff,stroke-width:2px
    classDef loom fill:#0078D4,stroke:#fff,color:#fff,stroke-width:2px
    classDef sub fill:#107C10,stroke:#fff,color:#fff,stroke-width:2px
    classDef dlz fill:#D83B01,stroke:#fff,color:#fff,stroke-width:2px

    subgraph Estate ["ESTATE pipeline — .github/workflows/deploy.yml (run rarely, by platform teams)"]
        direction TB
        ALZ["deploy-alz → deploy/bicep/landing-zone-alz<br/>Management + Connectivity platform subs"]:::estate
        DMLZj["deploy-dmlz → deploy/bicep/DMLZ"]:::estate
        DLZj["deploy-dlz → deploy/bicep/DLZ"]:::estate
    end
    subgraph Subs ["Azure subscriptions (the vended estate)"]
        direction TB
        Mgmt["Management + Connectivity (platform LZ)"]:::sub
        DMLZsub["DMLZ subscription"]:::sub
        DLZsub["DLZ subscription(s)"]:::dlz
    end
    subgraph Loom ["LOOM platform — platform/fiab/bicep/main.bicep (run repeatedly, by Console/azd)"]
        direction TB
        AP["module adminPlane → Admin Plane<br/>(Console, MCP, Copilot, shared catalog)"]:::loom
        LZmod["module landing-zone → per-DLZ data plane"]:::loom
    end

    ALZ --> Mgmt
    DMLZj --> DMLZsub
    DLZj --> DLZsub
    Mgmt -.platform LZ beneath both.-> DMLZsub
    Mgmt -.platform LZ beneath both.-> DLZsub
    DMLZsub ==>|main.bicep targets this sub| AP
    DLZsub ==>|dlzSubscriptionIds target these| LZmod
    AP -. "Loom does NOT require deploy.yml; azd can target any existing subs" .-> LZmod