Tutorial 09 — Tenant topology (DMLZ hub + N DLZ spokes + one Console)¶
Comparative positioning note
This document is written from the perspective of Microsoft Azure, Cloud Scale Analytics, and CSA Loom. Any description of third-party or competing products, services, pricing, or capabilities is derived from publicly available documentation and sources believed accurate at the time of writing, and is provided for general comparison only. We do not claim expertise in, or authority over, any non-Microsoft product or service; the respective vendor's official documentation is the authoritative source for their offerings, which may change over time. Nothing here is intended to disparage any vendor — where a competing product has genuine advantages, we aim to note them honestly. Verify all third-party details against the vendor's current official documentation before making decisions.
Understand how a CSA Loom tenant is shaped — one Data Management Zone (DMLZ) hub, N Data Landing Zone (DLZ) spokes, and a single Console — and how the two deployment flows (first-run and DLZ-attach) build it. ~15 minutes, conceptual.
This tutorial is the narrative companion to the Reference architecture and the committed diagram sources under docs/fiab/diagrams/.
The model in one picture¶
CSA Loom maps directly onto Microsoft CAF's Cloud-Scale Analytics pattern: the Admin Plane is the DMLZ (one subscription per organization), each DLZ is a domain / agency / mission (its own subscription in production), and a single Loom Console serves every DLZ from the Admin Plane.
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"]
direction TB
Console["Loom Console (one, serves all DLZs)"]:::console
MCP["Self-hosted Azure MCP server"]:::admin
Hub["Hub VNet + Azure Firewall + Private DNS"]:::admin
Cat["Shared catalog (Unity Catalog / Purview / Atlas)"]:::admin
ADX["Shared ADX cluster (one cluster, N databases)"]:::admin
end
subgraph DLZ1 ["DLZ — Domain A (one sub)"]
R1["ADLS medallion · Synapse · ADX DB · Power BI"]:::landing
end
subgraph DLZ2 ["DLZ — Domain B (one sub)"]
R2["...same shape..."]:::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| R1
Hub <-->|VNet peering| R2
R1 -->|domain DB attaches to| ADX
R2 -->|domain DB attaches to| ADX Key facts that fall out of this shape:
- One Console, one Admin Plane, one Entra tenant. The Console, MCP server, shared catalog (
catalogPrimary), and shared ADX cluster all live once in the Admin Plane. Entra groups are identical across every subscription. - DLZ = subscription; workspace = data product inside the DLZ. A single DLZ hosts many workspaces (one per team / project).
- Each DLZ attaches its own ADX database to the single shared cluster — there is one ADX cluster for the whole tenant, not one per DLZ.
Single-sub vs multi-sub¶
The topology is the deploymentMode parameter on platform/fiab/bicep/main.bicep:
| Mode | Shape | When |
|---|---|---|
single-sub | Admin Plane + exactly 1 DLZ in the same subscription | Trials, small agencies, single-mission POCs |
multi-sub | Admin Plane in sub-A; each DLZ in its own sub (B, C, … N) | Production federal deploys |
The in-app Deployment planner exposes this as a per-subscription Dropdown and emits param deploymentMode into the exported .bicepparam, so the plan you draw and the real az deployment sub create stay in sync.
Flow 1 — First-run (initial provision)¶
azd up or the Deploy-to-Azure button runs main.bicep once. It always brings up the Admin Plane first, then the first DLZ.
sequenceDiagram
autonumber
actor Op as Operator
participant Boot as azd up / Deploy button
participant Main as main.bicep
participant AP as module adminPlane
participant DLZ as singleDlz / dlz[*]
Op->>Boot: git clone + azd up
Boot->>Main: deploymentMode = 'single-sub' | 'multi-sub'
Main->>AP: deploy Admin Plane (Console, MCP, shared catalog, shared ADX)
AP-->>Main: outputs (hubVnetId, lawId, adxClusterPrincipalId)
alt single-sub
Main->>DLZ: 1 DLZ (domainName='default')
else multi-sub
loop each domain in dlzDomainNames
Main->>DLZ: DLZ in its sub (spoke peers to hub; ADX DB attaches to cluster)
end
end
Main-->>Op: consoleUrl + "Admin Plane + first DLZ deployed" Flow 2 — DLZ-attach (add a domain later)¶
Adding a domain after the platform is up does not redeploy the Admin Plane. The Console's "Add Data Landing Zone" action registers the new domain in the DlzOnboardingRegistry, then re-runs main.bicep with an expanded dlzSubscriptionIds / dlzDomainNames. Only the new spoke is created — it peers to the existing hub, attaches its ADX database to the shared cluster, and the setupOrchestratorSpokeRbac grant gives the Console UAMI Contributor on the new spoke subscription.
sequenceDiagram
autonumber
actor Admin as Loom admin
participant UI as Console "Add Data Landing Zone"
participant Reg as DlzOnboardingRegistry
participant Main as main.bicep (multi-sub)
participant Spoke as New DLZ spoke
Admin->>UI: Add Data Landing Zone (sub + domain name)
UI->>Reg: register new DLZ
Reg-->>Main: expanded dlzSubscriptionIds + dlzDomainNames
Main->>Spoke: deploy the new DLZ module only
Spoke->>Main: peer spoke VNet to adminPlaneHubVnetId + attach ADX DB
Main->>Spoke: setupOrchestratorSpokeRbac (Console UAMI → Contributor)
Main-->>UI: "Domain added — workspaces can be created" Domain & RBAC model¶
Human Entra groups (one set, tenant-wide) map to per-engine roles. Each domain 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 id fill:#0078D4,stroke:#fff,color:#fff,stroke-width:2px
GAdmin["Loom Admin group"]:::grp
GWs["Workspace member groups"]:::grp
GStew["Data Steward group"]:::grp
UC["Unity Catalog / Synapse / Hive roles"]:::role
UAMI["Workspace UAMI (per workspace)"]:::id
GAdmin --> UC
GWs --> UC
GStew --> UC
UC -.governs.-> UAMI See the full domain/RBAC diagram and the Identity flow for the JIT elevation sequence.
Data flow — domain item to shared catalog¶
A domain item lands in its DLZ resources (ADLS medallion → Synapse Serverless, the per-DLZ ADX database), then surfaces through the shared Admin-Plane catalog and marketplace. Microsoft Fabric is optional / plan-only — the Azure-native lake is the default and works with LOOM_DEFAULT_FABRIC_WORKSPACE unset.
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
Item["Domain item (lakehouse / warehouse / model)"]:::item
ADLS["ADLS Gen2 medallion (Bronze→Silver→Gold)"]:::dlz
ADXdb["ADX database (this domain)"]:::dlz
ADXc["Shared ADX cluster"]:::shared
Cat["Shared catalog + marketplace"]:::shared
Item --> ADLS
ADLS --> ADXdb
ADXdb -->|attaches to| ADXc
ADLS -.cataloged by.-> Cat How this relates to the estate pipeline¶
CSA Loom has two Bicep layers. The .github/workflows/deploy.yml estate pipeline vends the subscriptions and networks (ESLZ Cloud-Scale Analytics: management groups + Management/Connectivity platform subs + raw DMLZ/DLZ scaffolding). platform/fiab/bicep/main.bicep then runs Loom inside them. Loom does not require the estate pipeline — azd can target any existing subs. Full explanation: Relationship to the ALZ estate pipeline.
Per-cloud note¶
The topology shape is identical across every boundary; only the per-node service substitution changes (per the per-boundary dispatch matrix):
| Boundary | Default region | Container host | Catalog primary | Agent orchestrator |
|---|---|---|---|---|
| Commercial | eastus2 | Container Apps | Unity Catalog managed | Foundry Agent Service |
| GCC | usgovvirginia | Container Apps | Microsoft Purview | Foundry Agent Service |
| GCC-High / IL4 | usgovvirginia | AKS | Microsoft Purview | MAF + AOAI direct |
| IL5 | usgovarizona | AKS | Atlas-on-AKS | MAF + AOAI direct |
Where to read next¶
- Reference architecture — the full contract
- Diagram sources — committed
.mmd+.excalidraw - Deployment planner parity — emit your own
.bicepparam - Tenant bootstrap — post-deploy one-time config