Skip to content

Platform Configuration

This page is for the platform team. It describes how the solution is laid out as code, which prerequisites must be in place, and how the tiers are configured. For the design see Architecture.

Reference design, your deployment may differ

Variable names and defaults below are those of the solution code. The pipelines and the tfvars files of a deployment live in a configuration repository of that deployment, which is not part of the solution source.

Repository layout

The solution is delivered as a Terraform code repository. A deployment adds its own configuration (the tfvars files) and pipelines.

Path Purpose
infra/deploy/shared Root module of the shared tier: capacities, custom role, auto-pause
infra/deploy/workload Root module of the workload tier: gateway, workspaces, connections, data lake
infra/deploy/governance Root module of the optional Purview tier
infra/deploy/observability Root module of the optional Grafana tier
infra/modules/fabric-workspace Workspace, role assignments, private link, network policy scripts, Git, Lakehouses, Warehouses
infra/modules/fabric-connections ADLS, SQL, Oracle Cloud Storage connections, managed private endpoints, outbound connection rules
infra/modules/purview, infra/modules/grafana Modules of the optional tiers
docs/ HLD, ADR and runbooks of the solution

Tooling. Terraform >= 1.13, < 2.0. Providers: azurerm (>= 4.0.0, < 5.0), azuread, azapi and the Microsoft fabric provider (>= 1.7.0, < 2.0, in preview mode) for the workload tier, and grafana (>= 3.0.0, < 4.0) for the observability tier. The state is stored in an Azure storage account (azurerm backend) with Entra ID authentication. Several steps call Fabric REST APIs through PowerShell scripts, so the agent that runs the pipeline needs PowerShell (pwsh) and the Azure CLI with a signed-in session.

Prerequisites

Some steps cannot be automated and must be done before the first deployment.

Prerequisite Detail
A spoke per environment and one shared spoke The spoke canvas provides the VNet, private endpoint subnet, Key Vault, storage account and Log Analytics (Spoke canvas). The workload tier also needs a subnet for the VNet data gateway.
Graph permissions for the service principal of the shared spoke Application.Read.All, Directory.Read.All and User.Read.All. Needed to set the capacity administrators.
Fabric admin portal: workspace-level networking Enabled in the tenant settings
Fabric admin portal: service principal access Enabled, so that the deployment identity can use the Fabric APIs
Fabric admin portal: guest account access Enabled
Fabric admin portal: GitHub access Enabled, for workspaces with a Git link
Fabric admin portal: Configure workspace-level outbound network rules Enabled, so that workspace admins can use outbound protection
Secrets in the spoke Key Vault The client ID and secret of the service principal for each ADLS or SQL connection, username and password for basic connections and Oracle Cloud Storage, and for Git a personal access token in a secret named GitHub-PAT
Entra ID groups One per workspace role you want to use, and the Grafana groups when the observability tier is used

The source lists the four Fabric tenant settings as screenshots. Confirm the exact setting names in the Fabric admin portal of your tenant.

The deployment identity also needs rights on the spoke: it creates a key and reads secrets in the spoke Key Vault, assigns roles on the storage account, and creates resources in the subscription. What the spoke service principal has is described in Spoke canvas.

Variables common to all tiers

Variable Notes
location westeurope, northeurope, swedencentral or uksouth
environment development, test, acceptance, production (and shared for the shared and governance tiers)
workload_name At most 8 characters
spoke_config Names of the spoke resources to build on (see below)

spoke_config for the shared tier: subscription_id, resource_group_name, log_analytics_name, key_vault_name, storage_account_name, vnet_name, snet_pe_name. The workload tier adds snet_gateway_name. The governance tier takes a smaller set. These are the names of resources that the spoke canvas creates (What you get). The solution reads them with data sources and does not create them.

Shared tier

capacity_administrators = ["<spoke service principal object id>"]

fabric_capacities = {
  development = { sku = "F4" }
  production  = { sku = "F8" }
}

capacity_auto_pause         = { enabled = false }  # see Capacity auto-pause
capacity_operator_group_ids = []
Variable Purpose
capacity_administrators Principal IDs that become capacity administrators: the spoke principals of the workloads that use the capacity, and optionally users
fabric_capacities Map of capacities. The key becomes part of the capacity name and is what the workload tier refers to. At least one capacity. SKU F2, F4, F8, F16, F32, F64, F128, F256, F512, F1024 or F2048.
capacity_auto_pause See Capacity auto-pause
capacity_operator_group_ids Groups that get the Fabric Capacity Operator role

The resource providers Microsoft.Fabric and Microsoft.Logic are registered by the deployment.

Workload tier

fabric_capacities = {
  development = { display_name = "<name of the capacity from the shared tier>" }
}

workspaces = {
  engineering = {
    capacity_key                    = "development"
    admins_group_id                 = "<entra group object id>"
    contributors_group_name         = "<entra group display name>"
    viewers_group_id                = "<entra group object id>"
    outbound_network_policy_enabled = true      # outbound protection off, see Workspace network policy

    git_config = {
      owner_name      = "<organisation>"
      repository_name = "<repository>"
      branch_name     = "<branch>"
      directory_name  = "<directory>"           # must exist in the repository
    }

    lakehouses = {
      bronze = { description = "Raw data", enable_schemas = true }
    }
    warehouses = {
      silver = { description = "Business entities", collation_type = "Latin1_General_100_BIN2_UTF8" }
    }

    adls_connections = {
      landing = {
        container_name            = "landing"
        client_id_secret_name     = "<secret name>"
        client_secret_secret_name = "<secret name>"
      }
    }

    workspace_connections = {
      to-reporting = { workspace_key = "reporting" }
    }

    managed_private_endpoints = {
      sql = {
        target_private_link_resource_id = "<resource id>"
        subresource_type                = "<sub-resource>"
      }
    }
  }
}
Setting of a workspace Notes
capacity_key Must be a key of fabric_capacities (validated)
admins_*, contributors_*, members_*, viewers_* Each role has _group_id and _group_name. The ID wins.
outbound_network_policy_enabled Default false (outbound deny). See Workspace network policy.
git_config github_url defaults to github.com. See Connectivity.
lakehouses description, enable_schemas
warehouses description, collation_type (two allowed values)
adls_connections Container and the two secret names. A container is created per connection.
sql_connections Server, database, client ID and secret names (service principal)
sql_connections_basic Server, database, username and password secret names
oracle_cloud_storage_connections API endpoint, username and password secret names
workspace_connections Target workspace key (a workspace of the same deployment)
managed_private_endpoints Resource ID of the target and the sub-resource type
data_pipelines Accepted by the variable, but not passed on by the root module

Validations in the code: every capacity_key must exist, Warehouse and Lakehouse names must be unique within a workspace and across both types, and collations must be one of the two allowed values. A Fabric capacity must be found by display_name (a data lookup), so the shared tier must be deployed first.

What the workload tier creates per run is listed on Architecture. Notable behaviour:

  • One VNet data gateway is created for each capacity key of the deployment (1 member gateway, 30 minutes of inactivity before sleep), in the gateway subnet of the spoke. All workspaces of a capacity use it.
  • The storage account key expiration_date is ignored after creation, and the private DNS zone groups of private endpoints are ignored because policy sets them.
  • The network policy and Git steps run PowerShell scripts as local-exec provisioners, triggered by changes of their inputs.

Deploying a change

Changes follow Request and change. In short: edit the configuration, open a pull request, review the plan, deploy through the pipeline. Check the plan for:

  • additive changes only when adding a role group, connection or Lakehouse,
  • no replacement of a workspace resource,
  • the 403 skips of the network scripts on small SKUs (Workspace network policy).