Fabric's security model has four distinct permission layers — and most teams only know about one of them. This deep-dive lesson teaches you how workspace roles, item permissions, and OneLake data access policies interact so you can design and audit a governance model that actually holds up in production.

Imagine you've spent three months building a medallion architecture in Microsoft Fabric — bronze tables landing from pipelines, silver layers cleaned by Spark notebooks, gold semantic models powering executive dashboards. The project is a success. Then your CISO asks a single question: "Who has access to what, and how do you know?" You open the Fabric portal, click around for a few minutes, and realize you actually can't answer that question clearly. You granted a few workspace roles early on, shared a couple of items ad-hoc, and now the access picture is a tangled web of overlapping permissions that nobody fully understands.
This is the most common governance failure in Fabric deployments. It's not malice — it's the natural result of moving fast and treating security as an afterthought. Fabric's permission model is genuinely sophisticated, but that sophistication cuts both ways: it gives you fine-grained control, but it also gives you plenty of rope to tangle yourself with. The system has at least four distinct permission layers — capacity, workspace, item, and OneLake — and they interact in ways that aren't always obvious.
By the end of this lesson, you'll be able to design and implement a complete access control strategy for a real Fabric deployment. That means knowing which permission layer to use for which scenario, understanding how permissions propagate through OneLake, and knowing where the sharp edges are before you hit them in production.
What you'll learn:
You should be comfortable working in the Fabric portal and understand the basic building blocks: workspaces, lakehouses, warehouses, and semantic models. If you need a foundation, read What Is Microsoft Fabric? Workloads, OneLake, and How It Fits with Power BI first. You should also understand how Fabric capacities and workspaces are structured, since capacity-level admin permissions sit above everything else we'll discuss. Basic familiarity with OneLake and Delta tables is assumed.
Before you can govern Fabric intelligently, you need a mental model of how its permission system is actually layered. Most tutorials talk only about workspace roles. That leaves out three other distinct permission systems that interact — sometimes in surprising ways.
Layer 1: Capacity-level permissions sit at the very top. Capacity admins can access any workspace assigned to that capacity, pause and resume the capacity, and override workspace-level settings. This role is typically held by platform engineers or central IT, not data teams.
Layer 2: Workspace roles control who can see and interact with a workspace and its contents. There are four roles: Admin, Member, Contributor, and Viewer. These are coarse-grained and apply uniformly to everything inside the workspace.
Layer 3: Item-level permissions are granted on individual Fabric items — a specific lakehouse, warehouse, semantic model, or report. These can be granted to users who have no workspace role at all, enabling read-only or compute-only access to a single asset without exposing the rest of the workspace.
Layer 4: OneLake data access policies are a newer addition that operate beneath all of the above. They control access to specific paths inside OneLake — folders, tables, and files — independently of whether someone has a workspace role or item permission.
These layers are additive in the sense that they all apply simultaneously, but restrictive in the sense that a lower layer can restrict what a higher layer grants. A workspace Viewer can be blocked from reading a specific Delta table by an OneLake policy that doesn't include them. A user with no workspace role can be granted read access to a single lakehouse via an item permission. Understanding these interactions is the entire game.
Key insight
There's no single "permissions report" in Fabric that shows you the union of all four layers for a given user. You need to understand the model well enough to reason through it manually — or build your own audit tooling via the Fabric Admin APIs.
Workspace roles are the backbone of access control for most teams. Let's be precise about what each role actually means, because the marketing-level descriptions ("Contributor can create content!") hide important nuances.
Workspace Admins have full control: they can add and remove members, change the workspace's capacity assignment, publish apps, delete the workspace, and access every item inside it. From a governance perspective, the key risk with Admin is that it's impossible to audit what an Admin did after the fact at the workspace level without digging into activity logs. Every Fabric deployment should maintain a short, documented list of workspace Admins.
One subtle behavior: Workspace Admins can grant themselves access to items even if item-level permissions were deliberately configured to exclude them. The Admin role effectively bypasses item-level permission restrictions. This is a critical point we'll return to when discussing the separation of duties.
Members can do almost everything Admins can except manage membership and delete the workspace. They can create, read, update, and delete items; they can publish and update apps; they can share items with other users. The "share" capability is where governance breaks down in many organizations — any Member can grant external users access to individual items, creating permission grants that aren't visible from the workspace role list.
Contributor is the role most engineers end up in, and it has a subtle limitation that catches teams off guard: Contributors cannot share items or publish apps. They can create and modify items, run notebooks and pipelines, and read everything in the workspace — but they can't extend access to others. This makes Contributor appropriate for data engineers and analysts who are building content but shouldn't be making governance decisions.
Warning
Contributors have full read access to all data in all items in the workspace by default. A data engineer with Contributor access to your gold workspace can query your entire customer dataset from a Spark notebook. If that's a problem, you need item-level permissions or OneLake policies to further restrict data access — not just workspace roles.
Viewers can read items but cannot create or modify them. However, "read" means different things for different item types. A Viewer on a workspace can:
But a Viewer cannot:
This asymmetry is important. Viewer is appropriate for business consumers who access data through published Power BI reports. It's not appropriate as a substitute for granular data access on lakehouses or warehouses — those require item-level permissions on top of, or instead of, a workspace role.
It's completely valid to grant a user access to a specific Fabric item without giving them any workspace role at all. A user with no workspace role who has been granted item-level Read access to a warehouse can connect via the SQL analytics endpoint and run queries — they just can't see the workspace in their workspace list or access any other items. This pattern is important for data consumers who should see exactly one dataset, not the entire engineering workspace.
Item permissions let you carve out access to individual Fabric items with more precision than workspace roles allow. Each item type exposes a slightly different permission model.
To share an item, navigate to it in the Fabric portal, open the "..." context menu, and select "Manage permissions." From there, you can grant users or groups direct access. The "Share" button also exists in the item toolbar for supported item types.
When you share an item, you can optionally include:
These checkboxes look innocuous but have significant governance implications. Granting Reshare to a consultant or partner creates a permission chain that's essentially invisible from your workspace management view.
For lakehouses, item-level sharing grants access to the lakehouse's SQL analytics endpoint and to the default semantic model. You can share a lakehouse with Read, ReadData, or ReadAll permissions:
Warning
ReadAll on a lakehouse item is powerful. It gives the recipient programmatic access to every file in the lakehouse, including raw files in the /Files section and all Delta tables in /Tables. If you need table-level granularity, don't use ReadAll — use OneLake data access policies or SQL-level grants instead.
Warehouses expose a more SQL-centric permission model. At the item level, you can grant workspace users or external recipients the SQL permissions they need — but for fine-grained column or row-level security, you implement that with T-SQL directly inside the warehouse using GRANT, DENY, and CREATE ROW SECURITY POLICY statements, just as you would in Azure SQL Database. Item-level sharing gets users connected to the warehouse endpoint; T-SQL security controls what data they can actually see.
This approach is explored further in Building a Fabric Data Warehouse with T-SQL: Tables, Views, and Cross-Database Queries, where the security model for warehouse objects is covered in detail.
Semantic models support the most nuanced item-level permission model because Power BI's own permission system is embedded here. The key permissions are:
For organizations implementing Direct Lake mode, there's an additional wrinkle: the semantic model must have access to the underlying OneLake data. This is typically handled by granting the semantic model's service principal Read access to the lakehouse. If the underlying OneLake permissions change in a way that breaks this service principal's access, your Direct Lake model will fail silently — it will fall back to DirectQuery mode or throw errors depending on configuration.
Tip
When you share a semantic model in Direct Lake mode, think carefully about the data access path. Users querying the model are getting data through the model's identity, not their own. Row-level security (RLS) defined in the model is your primary data governance tool for controlling what different users see in reports.
OneLake data access policies are the newest and most granular layer of the permission stack. They allow you to define access controls at the level of individual folders and tables within OneLake, independently of workspace or item permissions.
Consider a lakehouse that contains tables for twelve different business domains — HR, Finance, Supply Chain, Customer, and so on. The data engineering team needs full access to the entire lakehouse to run transformations. The Finance analytics team should only see Finance tables. The Customer team should only see Customer tables. The HR data is sensitive and should be restricted to a very small group.
Without OneLake policies, your options are ugly:
OneLake data access policies solve this directly. You define a policy that grants a security group read access to only the Tables/finance path in the lakehouse. Contributors and Viewers in the workspace who are not in that group cannot read those tables from notebooks, the SQL endpoint, or direct OneLake file access.
OneLake data access policies are managed through the Fabric Admin portal, not the workspace UI — a fact that surprises most people when they first look for them.
Navigate to: Admin Portal → OneLake data access settings (or in some tenants, through the lakehouse settings directly via the "..." menu → "Manage OneLake security").
Note
As of mid-2024, OneLake data access policies are in general availability for lakehouses but are still rolling out for other item types. Always verify current support in the Microsoft documentation for your specific Fabric version.
A policy definition looks like this in its logical form:
Resource: onelake://<workspace-id>/<lakehouse-id>/Tables/finance
Principal: finance_analytics_group@contoso.com
Permission: Read
You can define multiple policies for different paths and different principals. The key behaviors are:
/Tables/finance and /Tables/supply_chain, they can read both path subtrees.A common source of confusion is the relationship between OneLake policies and the SQL object permissions you can set on the lakehouse's SQL analytics endpoint. These are separate systems:
If you implement SQL-level grants but not OneLake policies, a data engineer with a Spark notebook can bypass your SQL security entirely by reading the Delta table files directly from OneLake. This is a real attack vector — not hypothetical.
The right architecture for truly secure data domains within a single lakehouse is to implement both: SQL object permissions for fine-grained column/row security on the SQL surface, and OneLake policies for restricting which paths are accessible at all. Think of SQL permissions as the "what can you do once you're in the room" control, and OneLake policies as "can you enter the room at all."
Key insight
For workloads where all data access happens through Power BI reports using a semantic model with RLS, OneLake policies may be overkill. But the moment users can access data directly — via Spark notebooks, the SQL endpoint with external tools, or OneLake APIs — you need OneLake policies to close the perimeter.
Let's translate all of this into a practical design. We'll use a realistic scenario: a mid-size financial services company building a Fabric platform for three teams.
The teams:
The Fabric items:
platform-dev, platform-prod, and analytics-prodplatform-prodanalytics-prodanalytics-prodplatform-dev:
Admin: data-platform-leads@contoso.com (2 people)
Member: data-platform-team@contoso.com (6 people)
Contributor: (none — avoid giving Contributor to non-engineers)
Viewer: (none)
platform-prod:
Admin: data-platform-leads@contoso.com
Member: data-platform-team@contoso.com
Contributor: (none)
Viewer: (none — no business users in the platform workspace)
analytics-prod:
Admin: data-platform-leads@contoso.com
Member: data-analytics-leads@contoso.com (2 people)
Contributor: finance-analytics-team@contoso.com
Viewer: risk-management-team@contoso.com
Notice that the Risk Management team is Viewer in analytics-prod — they can see reports, but we still need to control their data access at the semantic model level via RLS.
The gold lakehouse in platform-prod contains Tables/finance, Tables/customer, Tables/hr, and Tables/risk. Here's the OneLake policy design:
Policy 1:
Resource: platform-prod/gold-lakehouse/Tables/finance
Principal: finance-analytics-team@contoso.com
Permission: Read
Policy 2:
Resource: platform-prod/gold-lakehouse/Tables/risk
Principal: risk-management-team@contoso.com
Permission: Read
Policy 3:
Resource: platform-prod/gold-lakehouse/Tables/*
Principal: data-platform-team@contoso.com
Permission: ReadWrite
But wait — neither the Finance Analytics team nor the Risk Management team is in platform-prod. They're in analytics-prod. Do they need workspace access to platform-prod too?
No — and this is where OneLake shortcuts come in. The Finance warehouse in analytics-prod uses a shortcut to point at Tables/finance in the gold lakehouse. When a Finance analyst queries the warehouse, they're reading the gold lakehouse data through the shortcut — but their access is governed by the OneLake policy on the source lakehouse path. They never need to be in the platform-prod workspace at all. The shortcut acts as a read-through, and OneLake policies on the source still apply.
Tip
Shortcuts preserve the access control of the source. When you create a shortcut in a lakehouse or warehouse pointing to another OneLake path, users accessing data through the shortcut must have permission on the source path, not just the destination item. This is by design and is a critical security property — it prevents "shortcut laundering" of permissions.
The Risk semantic model aggregates from Finance and Customer data into a single Risk summary. Risk Management team members are workspace Viewers, so they can see the model and interact with reports. But within the model, we need RLS to ensure that different Risk analysts only see data for their assigned portfolios.
In Power BI Desktop (or the web model editor), you define RLS roles:
// Role: Portfolio_Restricted
[PortfolioManagerEmail] = USERPRINCIPALNAME()
This DAX filter ensures each analyst only sees rows where their email matches the portfolio manager field. You assign the risk-management-team@contoso.com security group to this role in the semantic model's security settings.
Warning
RLS in semantic models only applies when users are accessing data through the semantic model. If a Risk analyst has direct read access to the underlying lakehouse tables (via a workspace role, item permission, or OneLake policy), they can bypass RLS entirely. Ensure that your OneLake policies are configured so Risk analysts can only reach the Risk table — not the Finance or Customer source tables.
Building an access model is half the job. Knowing whether it's working as designed over time is the other half — and most teams skip this part.
Fabric writes activity events to the Microsoft 365 Unified Audit Log. You can query this via PowerShell or the Microsoft Purview portal. Key event categories include:
CreateItem, DeleteItem, UpdateItem — item lifecycle eventsShareItem, UpdateItemPermissions — permission change eventsViewItem, ReadItem — data access events (not all item types generate these)To pull these logs programmatically:
# Requires Microsoft.Graph or ExchangeOnlineManagement module
Search-UnifiedAuditLog `
-StartDate (Get-Date).AddDays(-7) `
-EndDate (Get-Date) `
-RecordType FabricData `
-Operations "ShareItem","UpdateItemPermissions" `
-ResultSize 1000 |
Select-Object CreationDate, UserIds, Operations, AuditData |
Export-Csv ./fabric_permission_changes.csv -NoTypeInformation
This gives you a log of who changed permissions on what items over the past week. In a governed environment, this should be reviewed weekly and any unexpected permission grants should trigger an incident.
The Fabric REST APIs expose workspace membership and item permission information programmatically. To get all workspace role assignments:
GET https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/roleAssignments
Authorization: Bearer {token}
Response:
{
"value": [
{
"id": "8f3a9c12-...",
"role": "Member",
"principal": {
"id": "a1b2c3d4-...",
"displayName": "Finance Analytics Team",
"type": "Group"
}
}
]
}
You can combine this with the activity log data to build a permissions inventory: who has what role in which workspace, and when was it last modified. In a regulated environment, you'd run this weekly and compare against an approved baseline configuration stored in source control.
Speaking of source control — if you're using Fabric Git integration, workspace settings and item metadata are versioned, but workspace role assignments are not included in the Git sync. You'll need to manage role assignments separately, ideally through a deployment script that runs as part of your CI/CD pipeline.
import requests
import json
def set_workspace_roles(workspace_id, token, role_config):
"""
role_config: list of {"principalId": "...", "principalType": "Group", "role": "Contributor"}
"""
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
# First, get current assignments
current = requests.get(
f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/roleAssignments",
headers=headers
).json()["value"]
current_ids = {r["principal"]["id"] for r in current}
desired_ids = {r["principalId"] for r in role_config}
# Remove unexpected assignments
for assignment in current:
if assignment["principal"]["id"] not in desired_ids:
requests.delete(
f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/roleAssignments/{assignment['id']}",
headers=headers
)
# Add missing assignments
for desired in role_config:
if desired["principalId"] not in current_ids:
requests.post(
f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/roleAssignments",
headers=headers,
json=desired
)
This pattern — define desired state in a config file, compare against actual state, reconcile — is how mature platform teams manage permissions at scale. It's infrastructure-as-code applied to access control.
One of the most powerful — and most commonly misunderstood — features of Fabric is how OneLake shortcuts interact with security across workspaces and external sources.
When you create a shortcut from Lakehouse B pointing to a path in Lakehouse A, users accessing data through the shortcut in Lakehouse B are validated against the source permissions in Lakehouse A. This is a critical security property.
What this means in practice:
/Tables/hr_salary in Lakehouse A./Tables/hr_salary in Lakehouse A via OneLake policies or workspace roles.This is the correct behavior — but it requires you to explicitly grant source access to anyone who needs it. In the previous multi-team design, we granted finance-analytics-team an OneLake policy on the source gold lakehouse tables. That's what makes the shortcut usable for them.
When you create a shortcut to an external source like Azure Data Lake Storage Gen2, the security model changes. The shortcut uses a credential (either a service principal or a managed identity) to authenticate to the external source. Users querying data through the shortcut are not authenticated against the external source with their own identity — they inherit the credential attached to the shortcut.
This has a significant implication: anyone who can read the shortcut in Fabric can access the external data, subject only to their Fabric permissions. The external source's RBAC (e.g., Azure RBAC on the ADLS Gen2 account) controls what the shortcut credential can do — but individual users querying through the shortcut don't need their own Azure RBAC grants.
Warning
This "pass-through credential" behavior for external shortcuts means that your OneLake access policies and workspace roles become the sole access control layer for external data accessed via shortcuts. If you have sensitive data in ADLS Gen2, ensure you don't create shortcuts to it in workspaces with permissive workspace roles. The external source's RBAC grants are on the credential, not the individual user.
Production Fabric deployments almost always involve automated processes: pipelines, notebooks running on schedule, external applications calling the Fabric APIs. These need identities too — and service principals are how you handle that.
Service principals follow the same four-layer permission model as users. A pipeline running as a service principal that is a workspace Contributor can read all items in that workspace unless OneLake policies further restrict it.
Tip
Follow the principle of least privilege for service principals ruthlessly. A data pipeline that only needs to write to one lakehouse should be a Contributor in exactly one workspace and have an OneLake write policy on exactly that lakehouse path — nothing more. Service principals don't get tired of arguing about access; they'll use every permission they have, and so will anyone who obtains their credentials.
For automated deployments and data pipeline orchestration, use separate service principals for dev, test, and production environments. Never share credentials across environments — the ability to deploy to production should be explicitly gated, not inherited from dev.
Some Fabric items can use managed identities rather than service principals — particularly when connecting to Azure services. A Dataflow Gen2, for example, can use a managed identity to connect to Azure Key Vault for secrets, eliminating hardcoded credentials. The governance implication is that managed identity permissions must be tracked in your Azure RBAC inventory, not just your Fabric permissions inventory.
This exercise walks you through configuring a realistic multi-team permission structure from scratch.
Setup: You'll need a Fabric workspace with at least one lakehouse containing a few Delta tables. If you've completed the lakehouse fundamentals lesson, use that workspace and lakehouse.
Step 1: Establish baseline workspace roles
Step 2: Grant item-level access to the lakehouse
SELECT * FROM <table_name> LIMIT 10.Step 3: Configure an OneLake data access policy
Tables/<your_table_name>.Step 4: Audit your permissions via the API
Using the Fabric REST API or PowerShell with the MicrosoftFabric PowerShell module, retrieve the full list of workspace role assignments:
# Install the module if needed
Install-Module -Name MicrosoftFabric -Scope CurrentUser
# Connect
Connect-FabricAccount
# List workspace role assignments
$workspaceId = "<your-workspace-id>"
Invoke-FabricRestMethod `
-Method GET `
-RelativeUrl "workspaces/$workspaceId/roleAssignments" |
Select-Object -ExpandProperty value |
ForEach-Object {
[PSCustomObject]@{
Principal = $_.principal.displayName
Type = $_.principal.type
Role = $_.role
}
} | Format-Table
Compare the output against what you configured. Verify the Viewer is listed correctly and no unexpected principals appear.
This is the most common governance failure. Contributor sounds modest, but it grants read access to all data in the workspace. In a gold workspace with financial or PII data, Contributor effectively means "can read everything." Reserve Contributor for people who are actively building content, and use Viewer plus item-level permissions for data consumers.
Sharing a lakehouse gives access to the SQL analytics endpoint. It doesn't necessarily grant direct OneLake file access (unless you use ReadAll). Many teams discover this when an analyst tries to load a Delta table into a Spark session from a shared lakehouse and gets a 403. The fix is either ReadAll permission (with appropriate OneLake policies to compensate) or a workspace role that includes OneLake access.
Every lakehouse automatically creates a default semantic model. When you share a lakehouse, this model is also implicitly shared. If users should not have access to the semantic model (because it exposes all tables), you need to check the semantic model's permissions separately. The semantic model has its own "Manage permissions" panel.
SQL-level grants on the SQL analytics endpoint do nothing for users accessing data via Spark notebooks. If a Contributor runs spark.read.format("delta").load("abfss://...") pointing at a table path directly, SQL-level DENY statements have no effect. Only OneLake data access policies can enforce access control across all access methods.
When you move a shortcut from one workspace to another (via deployment pipelines), the shortcut destination path is preserved but the source-side OneLake policies may not be correctly configured for the new workspace context. After any deployment pipeline promotion, verify that shortcut access is working correctly for end users, and check that the OneLake policies on the source items still cover the identities that need access.
Note
Deployment pipelines clone item definitions — including shortcut definitions — but they do not clone permission assignments. After promotion from dev to production, you need a separate step (manual or automated) to apply the correct production permission configuration.
This usually means workspace role is set (so item is visible) but item-level or OneLake permissions are missing. Check:
sys.database_permissions.The most common cause is that the semantic model's service principal or managed identity does not have Read access to the OneLake table paths it needs. Check the semantic model's connection settings and verify the identity has an explicit OneLake policy or item-level ReadData permission on the source lakehouse. If the model is falling back to DirectQuery silently, the symptom is much slower query performance than expected.
Fabric's security model is genuinely powerful, but it requires you to think in layers. Workspace roles are the starting point, not the destination. Item permissions let you grant scoped access without exposing an entire workspace. OneLake data access policies are the enforcement layer that applies regardless of access method — and the only reliable way to secure data within a shared lakehouse against direct Spark or API access.
The key design principles to take away:
For your next steps, consider how this governance model applies to a complete data architecture. If you're building a medallion architecture in Fabric, the workspace and permission structure you design for bronze/silver/gold layers will directly determine how much of this governance complexity you can manage. And if your platform involves database mirroring from external sources like Azure SQL or Snowflake, the OneLake policies you define on those mirrored tables control who can access them — making the mirroring security story consistent with everything we've covered here.
Fabric's governance capabilities are still maturing rapidly. OneLake policies, in particular, are receiving significant investment. Build your governance model with the assumption that new capabilities will arrive — design it to be extensible rather than fully relying on workarounds. The teams that get this right early will spend their time building data products, not untangling permission disasters.