TL;DR
Row-level security (RLS) in Power BI restricts which rows each user can see by assigning them to roles whose DAX filter expressions run against the semantic model before any visual renders, so the wrong region, customer, or department never reaches the report in the first place. Roles are configured in Power BI Desktop and published to the service, then mapped to Azure AD users or groups for static RLS, or driven by USERPRINCIPALNAME() against a mapping table for dynamic RLS at scale.
Watch on YouTube
Boosting Financial Efficiency with Power BI
Kanerika on how Power BI delivers governed, finance-grade analytics — the same controls that underpin row-level security in production.
Most Power BI deployments hit the same wall around the 200-user mark. The dashboard works, the data is accurate, but the wrong people can see numbers that should be locked to their region, customer, or department. The fix is not another deployment pipeline or a new permissions group in Azure AD. The fix is row-level security , the data-model-level control that decides which rows each user can query before a single visual draws on screen.
Key Takeaways Power BI row-level security (RLS) is a model-level filter that restricts which rows of a semantic model each user can read, applied before any visual or DAX measure runs. Static RLS hard-codes filter values in each role and suits a small fixed set of access boundaries; dynamic RLS uses a security mapping table and USERPRINCIPALNAME(), and is the right pattern for any deployment that grows. Workspace permissions, app audiences, and sharing decide who opens a report; only RLS decides which rows they see inside it, so these controls solve different problems and usually run together. Performance issues almost always trace to oversized mapping tables, bidirectional filters across high-cardinality dimensions, or DAX measures inside the role filter; the fix is rarely to remove RLS. Workspace contributors and admins are not filtered by RLS, so always validate with a Viewer-only test account, not with an account that has build permissions. Kanerika is a Microsoft Solutions Partner and Fabric Featured Partner whose MVP-led team has consolidated 40-plus regional workspaces onto a single RLS-protected Fabric semantic model for enterprise clients. Yet RLS is also where Power BI pro jects most often stall. Teams confuse static and dynamic patterns. They forget to assign users to roles after publishing. They write DAX filters that work in Desktop but break on the service. They layer RLS on top of Direct Lake semantic models without testing performance, and reports that used to render in 2 seconds suddenly take 20.
This guide walks through what RLS actually is, how it sits inside Power BI’s security stack, how to implement both static and dynamic roles step by step, and the patterns Kanerika’s Microsoft MVP-led team uses to deploy RLS across Fortune 500 estates without slowing reports or breaking governance.
What Power BI Row-Level Security Is and Why It Matters Row-level security in Power BI is a model-level filter that restricts which rows of a semantic model each user can read. It runs inside the engine, before any visual or DAX measure executes, so a user who is filtered out of a row literally cannot see it through any report, app, or even a direct semantic model query.
Listen on Spotify
How Do Fortune 500 Enterprises Actually Govern Their Data?
The control point is a role . You define roles in Power BI Desktop, attach DAX filter expressions to one or more tables inside each role, and then assign users or Azure AD security groups to those roles in the Power BI Service. When a user opens a report, the service looks up which roles they belong to, applies the union of their filters, and returns only the rows that pass.
This matters because Power BI’s other access controls work at the wrong layer. Workspace permissions decide who can open a report. App audiences decide which pages a user sees. Sharing decides whether someone has read access at all. None of them filter the data inside the model. Without RLS, a sales manager in the West region who is granted access to a national pipeline report sees the entire national pipeline, not just their region.
RLS closes that gap. It is also the foundation for several adjacent capabilities: dynamic personalisation, multi-tenant Power BI Embedded apps, regulated-data scenarios where audit trails must prove that users only ever queried the rows they were entitled to, and Fabric workloads where the same semantic model serves both BI and AI consumption.
RLS in the Power BI Security Stack: Where It Sits RLS is one of three model-level security controls in Power BI. Knowing how they differ keeps teams from over-engineering one when another is the right tool.
Row-level security (RLS) filters rows. A user assigned to the West role queries the Sales table and gets only West rows back, the rest are invisible.
Object-level security (OLS) hides entire tables or columns from specific roles. A user assigned to a non-finance role might not see the Profit Margin column at all, even in the field list.
Column-level security (CLS) is a closely related pattern, implemented today through OLS on columns, that hides specific columns based on role. It is useful when a model contains a PII column some users must not see, but where row access is otherwise the same.
RLS is the most common of the three because most regulatory and organisational rules are about who can see which records, not which columns. The other two layer on top when needed. They are not substitutes for RLS, and RLS is not a substitute for them.
Control What it hides How it is defined Best for Row-Level Security (RLS) Specific rows of a table DAX filter on a role, applied to one or more tables Region, customer, or tenant-based data isolation Object-Level Security (OLS) Entire tables or columns Per-role table or column visibility, set in Tabular Editor or via API Sensitive PII fields some users must never see Column-Level Security (CLS) Specific columns OLS pattern applied to individual columns Salary, margin, or identity columns where row access is unchanged
Above the model sit workspace roles, app audiences, sensitivity labels, and Microsoft Purview-driven information protection. Below the model sit source-system permissions, Microsoft Fabric OneLake security, and any database-level SQL row-level security on the source. RLS is the model-internal layer, the one that determines what reaches the report once a user is already authenticated.
Static vs Dynamic RLS: When to Use Each Power BI supports two RLS implementation patterns. The choice between them is the single most important architectural decision in any RLS project, because it shapes how the model is built, how roles are maintained, and how performance scales.
Static RLS hard-codes the filter inside the role. A “West Region” role might contain a filter like [Region] = "West". The DAX is fixed, and you create one role per access boundary. Static RLS is simple to build and easy to audit, but it does not scale. Twenty regions need twenty roles. A new region means a model redeploy.
Dynamic RLS uses a security mapping table inside the model that links each user’s email or UPN to the data they can see. The DAX filter references USERPRINCIPALNAME() or USERNAME() at query time, so a single role serves every user. Adding a new user is an update to the mapping table, not a model change.
Kanerika Service
Power BI and Microsoft Fabric Services
Kanerika is a Microsoft Solutions Partner and Fabric Featured Partner. Our MVP-led team designs, secures, and operates Power BI estates at enterprise scale.
Explore Power BI Services For any deployment beyond a handful of fixed access boundaries, dynamic RLS is the right pattern. It is also the pattern most enterprises eventually adopt even when they start static, because the maintenance cost of dozens of hard-coded roles compounds.
Dimension Static RLS Dynamic RLS Role count One per access boundary Usually one Maintenance Redeploy on changes Update mapping table Best for 2 to 5 stable boundaries Users that grow or change Performance Slightly faster, simple filter Depends on mapping size Audit clarity Each role visible in Desktop Centralised in mapping table Complexity Low Medium
Setting Up Static RLS in Power BI Desktop, Step by Step Static RLS is the right starting point for two scenarios. The first is a small, fixed set of access boundaries that will not change, like four regional roles in a stable sales org. The second is a learning exercise, because static patterns are the easiest way to understand how Power BI’s filter engine interacts with roles.
The setup runs entirely inside Power BI Desktop, with role assignment done later in the service.
Open the report in Power BI Desktop and switch to the Modeling tab. Click Manage roles , then New , and give the role a clear name like “West Region” or “Finance Read”. Select the table you want to filter. For a regional split this is usually the Sales or Fact table. Under Filter data , enter a DAX expression that returns TRUE for rows the role can see, for example [Region] = "West" or [CountryCode] IN {"US", "CA"}. Save the role. Repeat for each additional access boundary. Test the role before publishing using View as on the Modeling tab. Pick a role, and the report renders as a user in that role would see it. Publish the model to the Power BI Service workspace. In the service, open the semantic model’s Security screen, find the role, and add the email addresses or Azure AD security groups that should belong to it. That last step is where most projects fail their first deployment. Publishing the model does not assign users to roles. A role with no members filters out everyone. Always verify role membership in the service before declaring a deployment done.
Implementing Dynamic RLS with USERPRINCIPALNAME, Step by Step Dynamic RLS replaces dozens of hard-coded roles with a single role that filters based on the logged-in user’s identity. It needs a security mapping table inside the model, a relationship from that table to your fact data, and a DAX rule that uses the USERPRINCIPALNAME() DAX function .
Create a security mapping table with columns for UserEmail, accessible attribute (RegionCode, CustomerID, etc.), and any grouping you need. Load it from a governed source like an Azure SQL table or a Dataverse entity. Avoid storing it in a manual Excel file once you are past 50 users. Mark UserEmail as a unique key. Hide the mapping table from report view so analysts cannot expose it accidentally. Build relationships from the mapping table to the dimension or bridge table that owns the access attribute. A single relationship on RegionCode is usually enough. In Manage roles , create one role named something like “Dynamic User”. On the Security mapping table, add the DAX filter [UserEmail] = USERPRINCIPALNAME(). Power BI propagates the filter through relationships down to the fact table at query time. Test the role with View as , but use the new Other user option and enter a real UPN. The Desktop preview does not always evaluate USERPRINCIPALNAME against the current Windows user the way the service does. Publish and assign every report user, or the security group that owns them, to the single Dynamic User role in the service. Two patterns make dynamic RLS scale further. The first is a many-to-many relationship through a bridge table when a user can see multiple regions. The second is a LOOKUPVALUE or CONTAINS filter when the access rule is not a clean equality, for example “user can see all regions in their division”.
Kanerika Service
Microsoft Fabric and Power BI Consulting
Kanerika designs, migrates, and operates Microsoft Fabric and Power BI estates end to end, from semantic model architecture to RLS-protected production rollout.
Explore Microsoft Fabric Services Common Mistakes to Avoid in Power BI RLS Several RLS bugs cost Power BI teams a disproportionate amount of debugging time. They are almost always one of these.
Forgetting to assign users to roles after publish. The role is there, the filter works in Desktop, the report shows no data. The fix is the Security screen in the service, not the model.Using static patterns when dynamic is needed. Twenty roles for twenty regions is a maintenance burden waiting to fail. Migrate to a mapping table early.Filter direction set to single. Dynamic RLS depends on bidirectional filter propagation from the mapping table down to facts. If your relationship is single-direction the filter never reaches the fact rows.USERPRINCIPALNAME vs USERNAME confusion. Use USERPRINCIPALNAME for cloud Azure AD authentication, USERNAME for legacy NTLM on-premises Analysis Services. Picking the wrong one returns empty results in the service.Workspace admins bypassing RLS. Workspace contributors and admins are not filtered. Use a Viewer-only test account to validate, never an account with build permissions.RLS on calculated tables. Filters do not always propagate through calculated tables built from DAX. Materialise the security boundary in the source, not in DAX.Composite models with cross-source RLS. A model that combines DirectQuery and Import can apply RLS inconsistently if filter direction crosses storage modes. Test each storage mode independently.Performance: Why RLS Slows Reports and How to Fix It RLS adds a filter to every query. That filter is cheap when the model is small and the role is simple. It is expensive when the mapping table grows past tens of thousands of rows, when bidirectional filters propagate across many relationships, or when the model runs on Direct Lake or DirectQuery against a source that cannot push filters down efficiently.
Teams that report sudden post-RLS slowdowns usually hit one of four root causes. The mapping table is too wide, with one row per user-region rather than user-grouped. The relationship between mapping and fact data crosses high-cardinality dimensions. The role includes complex DAX measures inside the filter, instead of a clean equality. Or the model has both import and DirectQuery tables, and the engine cannot push the security predicate to both.
The fix is rarely “remove RLS”. It is usually one of these. Slim the mapping table. Add a grouping dimension between users and access attribute so the engine filters once per group, not once per user. Use single-direction filters wherever possible, even in dynamic patterns, by promoting the security attribute into a junction dimension. And test the RLS-on baseline early, before report consumers complain about performance changes that look mysterious if RLS was added later.
Direct Lake models, the default for new Microsoft Fabric semantic models, deserve a separate performance pass. RLS on Direct Lake works, but a model that falls back to DirectQuery under load may evaluate RLS in the source compute layer, which has different cost characteristics from the in-memory VertiPaq tabular engine .
Testing RLS Before and After Deployment The single biggest correlate of an RLS failure in production is skipping the test step. Power BI provides two test surfaces, and a healthy deployment uses both.
In Power BI Desktop , use View as on the Modeling tab. Pick a role, optionally enter a UPN under Other user, and the report renders with the role applied. This catches DAX errors, missing filter direction, and obvious row-count gaps before publish.
Talk to Kanerika
Designing RLS for Your Power BI Estate?
Kanerika scopes the right static-vs-dynamic mix, mapping table design, and Azure AD group strategy for your reporting environment. A short working session turns ambiguity into a plan.
Schedule a Demo → In Power BI Service , open the semantic model, go to Security, and use Test as role on each role. The service evaluates the filter using the same engine that serves real users. This catches the differences between Desktop preview and service execution, including how Azure AD authentication maps to USERPRINCIPALNAME.
Beyond the built-in tests, document the expected row counts per role in a small spreadsheet during development. After deploy, query the model with a Viewer-only test account in each role and verify the counts match. This 10-minute step catches more RLS regressions than any other QA practice.
RLS in Microsoft Fabric and Direct Lake Semantic Models Power BI semantic models are now part of Microsoft Fabric’s permission model , and RLS is one of three model security layers that work together. The Fabric model adds OneLake security underneath, which means a user can have OneLake access to raw files but be filtered down to specific rows when they query through a semantic model.
For Direct Lake semantic models, RLS works exactly the same way as classic semantic models from the report consumer’s point of view. The setup uses the same Manage roles dialog, the same DAX filter syntax, and the same Security screen in the workspace. The behaviour under the hood is different, because Direct Lake queries Delta tables in OneLake without the in-memory VertiPaq cache, but that is invisible to the modeller.
The one Fabric-specific consideration is that semantic-model RLS does not extend to direct SQL endpoint access against the underlying Lakehouse or Warehouse. If a user has SQL endpoint permissions on the source, they can query the raw tables without the RLS filter. Closing that gap requires Fabric workspace permissions and, for sensitive data, a separate SQL row-level security policy on the source itself.
Auditing and Governing RLS Across an Enterprise Once an estate has more than 10 RLS-protected models, governance becomes the bottleneck. The patterns that scale share three properties.
First, role membership is managed through Azure AD security groups, not individual user emails. A “Sales-West-Viewers” group owns the membership, the role in the service is assigned the group, and joiner-mover-leaver processes happen entirely in identity, not in Power BI.
Second, the security mapping table for dynamic RLS lives in a governed source. Treat it as production data, with its own pipeline, lineage, and change history. Manual edits to Excel-backed mapping tables are the most common source of stale or accidentally over-permissive access.
Third, audit and review run on a schedule. Power BI’s activity logs record who accessed which model and which role applied. Microsoft Purview can capture the same events as part of broader information protection. A quarterly review that maps current role membership back to the access policy catches drift before an auditor does.
Real-World Use Cases for Power BI RLS Some industries use RLS as table stakes and others as a competitive advantage. The patterns are similar but the stakes differ.
Multi-tenant SaaS dashboards. Each customer signs in and sees only their data, served from a single shared semantic model. Dynamic RLS on a TenantID column is the canonical pattern.Regional sales reporting. Each manager sees their region. Dynamic RLS with a Region mapping table replaces a stack of hard-coded reports.Clinical and patient data in healthcare. Care teams see only their patient cohort. RLS on a care-team identifier complements role-based application access.Financial close and consolidation. Business unit controllers see only their BU. RLS on a CostCentre or BU code prevents accidental cross-BU visibility before the consolidated view is published.Manufacturing line and plant data. Plant managers see their plant, regional ops see all plants in their region. Hierarchical dynamic RLS handles the cascade.Insurance underwriter portfolios. Underwriters see only their book. RLS on a UnderwriterID combined with effective-date filtering supports portfolio reviews and audit-grade tracking.The pattern that fails most often is treating a workspace permission as a substitute for RLS. A “regional manager” workspace with separate reports per region is a brittle workaround that breaks the moment cross-region analytics are needed, and it sidesteps the data stewardship processes that should own who sees what.
Case Study
Microsoft Power BI Migration for a Global Manufacturer
Kanerika consolidated regional Power BI workspaces onto a governed Fabric semantic model with RLS, cutting manual reconciliation by 60% and accelerating new-region onboarding from weeks to a single mapping-table update.
Read the Case Study → How Kanerika Implements RLS at Enterprise Scale Kanerika is a Microsoft Solutions Partner and a Fabric Featured Partner, with a Microsoft MVP-led Power BI practice that has shipped RLS-protected semantic models for Fortune 500 enterprises across BFSI, healthcare, manufacturing, and supply chain . Our delivery approach reflects the patterns above, applied at scale.
We start with an access policy audit . Before any role is written, we map every reporting consumer to the smallest set of access boundaries the business actually needs. Most estates discover during this step that they have been over-segmenting in some areas and under-segmenting in others, and that the right model is far simpler than the original brief assumed.
From there we design the RLS architecture as code . Roles, mapping tables, and Azure AD group assignments are version-controlled alongside the semantic model. New regions, products, or business units are a pull request, not a manual model edit. The deployment runs through Power BI deployment pipelines with automated test users in each role.
The build and migration phase moves any legacy reports off workspace-permission-only segmentation onto a unified RLS model. For estates with hundreds of reports, our FLIP migration accelerator automates conversion of legacy patterns (separate per-region reports, hard-coded slicers acting as access controls, etc.) into a single governed semantic model with dynamic RLS.
We then layer in governance and operations : scheduled audit reviews, drift detection on mapping tables, performance regression monitoring on RLS-protected models, and Purview-aligned activity log capture for compliance reviews. The same model that serves BI also serves Fabric AI and Copilot workloads, with the same row-level guarantees.
On a recent engagement for a global supply-chain enterprise, Kanerika consolidated 40-plus regional Power BI workspaces onto a single RLS-protected Fabric semantic model. Manual reconciliation across regions dropped by 60 percent, and onboarding a new region went from a multi-week project to a single mapping-table update.
Frequently Asked Questions What is Power BI row-level security? Power BI row-level security (RLS) is a model-level filter that restricts which rows each user can see inside a semantic model. You define roles in Power BI Desktop, attach DAX filter expressions to them, and assign users or Azure AD security groups in the Power BI Service. The filter runs before any visual or measure executes, so a user filtered out of a row cannot reach it through any report or app.
What is the difference between static and dynamic row-level security? Static RLS hard-codes filter values inside the role, so you create one role per access boundary like one role per region. Dynamic RLS uses a security mapping table and a DAX rule based on USERPRINCIPALNAME(), so a single role serves every user. Dynamic scales better and is the right pattern beyond a handful of fixed boundaries.
Should I use row-level security or workspace permissions? Workspace permissions decide who can open a report. RLS decides which rows they see inside it. They solve different problems and are usually used together. RLS is the right control when different users of the same report must see different rows.
How do I test row-level security in Power BI? Use View as on the Modeling tab in Power BI Desktop to test roles during development, and Test as role on the Security screen in the Power BI Service after publishing. For full confidence, query the model with a Viewer-only test account in each role and verify expected row counts before declaring a deployment complete.
Does row-level security work with Direct Lake semantic models? Yes. RLS works on Direct Lake semantic models with the same Manage roles dialog and DAX syntax as classic models. The engine behaviour differs underneath, and you should test performance under realistic load because Direct Lake can fall back to DirectQuery in some scenarios.
Can a workspace admin bypass row-level security? Yes. Workspace contributors and admins, and members assigned the Build permission on a semantic model, are not filtered by RLS. Always validate with a Viewer-only test account, never with an account that has build or admin permissions.
How does Power BI row-level security affect report performance? RLS performance depends mostly on the size of the security mapping table and how filters propagate. Slim mapping tables, group security at the right level, and prefer single-direction filters where possible. Test the RLS-on baseline early so any regression is caught before report consumers do.
Is row-level security the same as column-level security in Power BI? No. Row-level security hides rows, column-level security (delivered through object-level security on columns) hides specific columns. Both are model-level controls and they often run together when some users must not see specific PII columns even within rows they are entitled to read.
How do I use USERPRINCIPALNAME for dynamic row-level security? Build a security mapping table with a UserEmail column and the access attribute, mark UserEmail as a unique key, create a relationship from the mapping table to the dimension or bridge table that owns the access attribute, then write a DAX filter on the mapping table that reads [UserEmail] = USERPRINCIPALNAME(). Use USERNAME() only for legacy NTLM Analysis Services scenarios.