,

How I Structure Microsoft Fabric Workspaces for Dev, Test and Prod

By

·

Your Microsoft Fabric workspace structure takes seconds to create, but changing it once dozens of pipelines, semantic models and reports depend on it is a project in its own right. That’s why, on the AI-ready Fabric platform I’m building today, I treat workspace design as one of the very first decisions, made before anyone creates a lakehouse.

In my Microsoft Fabric lakehouse tips I said I design workspaces around lifecycle and ownership. This post goes deeper: the options I considered, the layout I use, the naming conventions, how workspaces map to capacities and security groups, and how changes move from dev to production. It also covers when this approach is overkill.

Feature behaviour described here reflects Microsoft Learn documentation as of October 2026. Fabric changes monthly, so check the linked docs before you rely on any detail.

Why workspace design is the decision that’s hardest to undo

A Fabric workspace isn’t just a folder. It’s the boundary for almost everything that matters operationally:

  • Security. Workspaces divide OneLake into separate containers that can be secured independently, and access is granted through four workspace roles: Admin, Member, Contributor and Viewer.
  • Cost and performance. Each workspace is assigned to a capacity, so the workspace decides whose compute your workload consumes.
  • Lifecycle. Git integration works at workspace level, and Microsoft’s guidance notes that a workspace can only be connected to a single branch at a time. Each stage of a deployment pipeline is also a workspace.
  • Ownership and discovery. Workspaces, not individual items, are what you assign to domains.

So when you pick a workspace layout, you’re really choosing your security model, cost model and release process at the same time.

Three ways to slice workspaces

I’ve seen three broad patterns. All three can work, but they suit very different situations.

Option 1: One workspace per environment

Everything for the platform lives in analytics-dev, analytics-test and analytics-prod. It’s simple and it’s where most proofs of concept start. The trouble arrives when a second team joins: everyone shares one set of roles, one Git branch and one release cadence, and one team’s half-finished change blocks another team’s release. Workspaces also have a documented limit of 1,000 items, which a busy shared workspace can approach sooner than you’d expect.

Option 2: One workspace per Medallion layer

bronze-prod, silver-prod, gold-prod, and so on. It looks tidy on a diagram and makes layer-level permissions easy. In practice, a single change to one data product often touches a pipeline, a notebook and a table in all three layers, so a release spans three workspaces and three Git connections. Layers are a design concept; they don’t need to be a deployment boundary.

Option 3: One workspace per domain and purpose, per environment

This is what I use. Each business domain gets a data workspace (lakehouses for bronze, silver and gold, plus the notebooks and pipelines that load them) and an analytics workspace (semantic models, reports and data agents). Each exists in dev, test and prod. A small platform workspace holds genuinely shared assets, such as conformed reference data and monitoring.

Microsoft’s own lifecycle management best practices support the core idea: separate workspaces for different teams let each team have different permissions, use different repositories and ship on a different cadence, while most items can still use data across workspaces.

Per environmentPer layerPer domain + purpose
Number of workspacesLowestMediumHighest
Independent team releasesNoNoYes
Least-privilege accessHardLayer-level onlyNatural
One change = one releaseYesRarelyUsually
Best forPoC, one small teamRarely my first choiceMultiple teams or domains

The layout I use

Microsoft Fabric workspace structure with sales, finance and platform workspaces promoted from dev to test to prod across two capacities
An illustrative Fabric workspace layout: each domain has data and analytics workspaces in dev, test and prod, on separate non-production and production capacities.

A few points about the diagram:

  • Data and analytics are separate on purpose. Engineers and BI developers are often different people with different release rhythms. Splitting the workspaces means a report fix doesn’t wait for a pipeline release, and report authors don’t need write access to lakehouses.
  • Analytics reads gold, never silver. The analytics workspace consumes gold tables through OneLake shortcuts, so data isn’t copied and the gold layer is the only contract between the two teams.
  • The platform workspace stays small. If it starts collecting domain-specific items, it’s becoming Option 1 again.
  • Developers work in isolation. Microsoft recommends an isolated environment for development, either a client tool such as VS Code or Power BI Desktop, or a separate (branched) workspace, before changes are merged through a pull request.

Naming conventions

Naming feels trivial until you’re scripting deployments. Two rules matter most: names must be predictable, and item names must be identical in every environment. Only the workspace name carries the environment.

Workspaces      <domain>-<purpose>-<env>        sales-data-dev, sales-analytics-prod
Capacities      cap-<scope>-<env>                cap-nonprod, cap-prod
Lakehouses      lh_<layer>                       lh_bronze, lh_silver, lh_gold
Notebooks       nb_<layer>_<entity>              nb_silver_customer
Pipelines       pl_<source>_<action>             pl_crm_ingest
Semantic models sm_<subject>                     sm_sales_performance
Reports         rpt_<subject>                    rpt_sales_performance
Variable libs   vl_<scope>                       vl_config
Entra groups    sg-fabric-<workspace>-<role>     sg-fabric-sales-data-prod-viewer

I use lowercase and hyphens for workspaces because they’re easy to type into APIs and scripts, and some special characters aren’t supported in workspace names over the XMLA endpoint.

Mapping workspaces to capacities, groups and roles

Capacities

My default is two capacities: one for dev and test, one for production. A heavy backfill in dev shouldn’t slow down executive dashboards. Microsoft’s guidance is a little more nuanced for test: test should resemble production in data volume, usage and capacity, but load-testing on the production capacity can destabilise it, so they suggest a separate capacity of similar size, ideally one you only pay for while testing. Where budget allows, I follow that for major releases.

Roles

I assign roles to Entra security groups only, never to individuals; the roles documentation confirms you can assign security groups, Microsoft 365 groups and distribution lists.

WorkspaceDevTestProd
*-data-*Engineers: ContributorEngineers: Viewer; testers: ViewerDeployment identity: Admin; support: Viewer
*-analytics-*BI developers: ContributorTesters: ViewerDeployment identity: Admin; consumers via app or item sharing

One detail is worth checking carefully. In the roles table, Viewer can still read lakehouse and warehouse data through the SQL analytics endpoint. That’s why I don’t give business users the Viewer role on a data workspace. They get access through the analytics workspace (an app or shared report) instead.

Only Admins can connect a workspace to Git or create a workspace identity, so I keep the Admin group small. For production connections to Entra-secured sources I prefer a workspace identity over a personal account, because Fabric manages its credentials.

Domains

I assign every workspace to a domain so ownership is visible in the OneLake catalog. But domains organise; they don’t secure. Microsoft’s documentation is explicit that domain assignment doesn’t affect item visibility or access, so never rely on it as a security control.

Promoting changes: Git and deployment pipelines

Microsoft documents several CI/CD workflow options. The two I see most often are:

  1. Git for dev, deployment pipelines for test and prod. Only the dev workspace is connected to Git; content then moves between workspaces with Fabric deployment pipelines (two to ten stages, three by default). Deployment rules handle stage-specific settings. This suits BI-heavy teams and teams new to DevOps tooling.
  2. Git as the source of truth for every stage. A pipeline in Azure DevOps or GitHub Actions deploys the same tested commit to test and then, after approval, to prod, typically with the open-source fabric-cicd Python library.

I usually start teams on the first and move to the second once they need approval gates and environment-specific substitution. Either way, keep environment-specific values out of item definitions. A variable library holds one value set per stage, with one active per workspace, and pipelines, notebooks, Dataflows Gen2 and lakehouse shortcuts can consume it.

With fabric-cicd, a parameter.yml file in the repository swaps values per environment at deploy time. Here’s a simplified example that repoints notebooks at the target workspace’s own lh_silver and disables schedules outside production:

find_replace:
  # Default lakehouse ID inside notebook definitions
  - find_value: \#\s*META\s+"default_lakehouse":\s*"([0-9a-fA-F-]{36})"
    replace_value:
      TEST: "$items.Lakehouse.lh_silver.$id"
      PROD: "$items.Lakehouse.lh_silver.$id"
    is_regex: "true"
    item_type: "Notebook"

key_value_replace:
  # Only run schedules in production
  - find_key: $.schedules[?(@.jobType=="Execute")].enabled
    replace_value:
      TEST: false
      PROD: true
    file_path: "**/.schedules"

And the deployment step itself, run by a service principal from the release pipeline:

from azure.identity import DefaultAzureCredential
from fabric_cicd import FabricWorkspace, publish_all_items

workspace = FabricWorkspace(
    workspace_id="<sales-data-prod workspace id>",
    environment="PROD",                  # must match a key in parameter.yml
    repository_directory="./sales-data",
    item_type_in_scope=["Lakehouse", "Notebook", "DataPipeline", "VariableLibrary"],
    token_credential=DefaultAzureCredential(),
)
publish_all_items(workspace)

Treat these as starting points and check the current fabric-cicd documentation, because the library and its supported item types move quickly.

Pitfalls, and when not to do this

  • Don’t build nine workspaces for a proof of concept. One domain with dev and prod is enough to start; deployment pipelines need a minimum of two stages. Add test and further domains when a second team or a real release process arrives.
  • Don’t create items directly in test or prod. Deployment pipelines pair items between stages. Items that aren’t paired don’t overwrite on deployment; you get a duplicate instead. Everything should arrive by deployment.
  • Don’t hard-code GUIDs. Notebook default lakehouses and connection IDs differ per workspace. Use variable libraries, deployment rules or parameterisation from day one.
  • Don’t treat a domain as a security boundary. Use workspace roles and OneLake security for access.
  • Don’t delete and recreate workspaces casually. When a workspace is permanently deleted, its workspace identity goes with it; restoring a deleted workspace doesn’t restore the identity, and connections relying on it break.
  • Don’t split by Medallion layer just for neatness. If one change always spans three workspaces, the boundary is in the wrong place.
  • Don’t point dev at full production data. Microsoft recommends separate, smaller development data sources and a test source that’s as close to production as possible.

A checklist before you create workspace number one

  1. Which domains and teams will own data, and who are the owners?
  2. Which environments do you need now, and which later?
  3. How many capacities, and which workspaces go on each?
  4. What are the naming conventions for workspaces, items and groups?
  5. Which Entra groups get which role in each workspace?
  6. Which promotion route: deployment pipelines, fabric-cicd, or both?
  7. Where do environment-specific values live?
  8. Which domain does each workspace belong to?

If you can answer these on one page, you’re ready to build.

Key takeaways

  • Workspaces are your boundary for security, capacity, Git and deployment, so design them before your first lakehouse.
  • Slice by domain and purpose (data vs analytics), then by environment. Medallion layers live inside a workspace, not across workspaces.
  • Keep item names identical across environments and put the environment only in the workspace name.
  • Grant roles to groups, keep Admin small, and remember that Viewer on a data workspace can read data through the SQL analytics endpoint.
  • Start with Git plus deployment pipelines; move to fabric-cicd when you need approvals and parameterisation.
  • Scale the structure to your team. A small team can start with far fewer workspaces.

Keep reading

If this was useful, these posts cover the next decisions in the same journey:

Structure your workspaces differently? I’d be glad to compare notes. Leave a comment or get in touch.

Sources

Views on this site are my own and don’t represent my employer. This post is for general information only; see the Disclaimer.

Have a Question?

Curious about Fabric, Databricks or AI agents? I’m happy to share what I’ve learned.

Leave a Reply

Your email address will not be published. Required fields are marked *