Row-Level Security (RLS) in Power BI restricts data access at the row level — meaning different users see different subsets of the same dataset without needing separate reports. A national sales manager sees all regions; a regional manager sees only their region; a branch manager sees only their branch — all from the same Power BI report, with access enforced server-side.
This guide covers both static RLS (fixed roles assigned to users) and dynamic RLS (data-driven, using the logged-in user's identity to filter data automatically) — the two patterns that cover 95% of enterprise RLS requirements.
Why Row-Level Security Matters
Without RLS, sharing a report with a regional manager means they can see data for all regions — a data governance failure. The traditional workaround (separate reports per user or per region) creates a maintenance nightmare: 50 regional managers means 50 separate reports to update every time the design changes.
RLS solves this cleanly: one report, one dataset, one maintenance task — with server-enforced data access control that cannot be circumvented even if a user downloads the underlying data.
How RLS Works in Power BI
RLS is defined as DAX filter expressions on tables in the Power BI semantic model. When a user with an RLS role opens a report, Power BI automatically applies the DAX filter to every query — they can only see rows that the DAX expression permits. RLS is enforced in Power BI Service (cloud), not in Power BI Desktop — in Desktop, report creators see all data regardless of RLS roles.
Static RLS: Fixed Roles
Static RLS assigns users to roles with fixed DAX filters. Best for simple hierarchies with a small number of distinct access levels.
Example: Regional Sales Data
Suppose you have a Sales table with a Region column. Create three roles:
Role: North Region
-- Table: Sales
[Region] = "North"
Role: South Region
-- Table: Sales
[Region] = "South"
Role: All Regions (National)
-- No filter — sees everything
In Power BI Service, assign each user (or Azure AD group) to the appropriate role. Users assigned to "North Region" see only North data; users in "All Regions" see everything.
Limitations of Static RLS
Static RLS requires a new role every time a new region, branch, or business unit is added. For organisations with 50 regions or 500 branches, managing static roles is unsustainable. This is where dynamic RLS becomes essential.
Dynamic RLS: Data-Driven Security
Dynamic RLS uses the USERPRINCIPALNAME() DAX function — which returns the email address of the currently logged-in Power BI user — to filter data based on a mapping table in the model.
Setup: Create a User-Region Mapping Table
Add a table to your data model that maps each user's email to their allowed region(s):
| UserEmail | Region |
|---|---|
| rahul@company.com | North |
| priya@company.com | South |
| admin@company.com | All |
This table can be loaded from SQL Server, SharePoint, or Azure Active Directory — and automatically picks up new users without any changes to the RLS role definition.
Dynamic RLS DAX Filter
-- Applied to the DimRegion table (which relates to Sales via RegionID)
[Region] IN
CALCULATETABLE (
VALUES ( UserRegionMap[Region] ),
UserRegionMap[UserEmail] = USERPRINCIPALNAME ()
)
|| CONTAINS (
CALCULATETABLE (
VALUES ( UserRegionMap[Region] ),
UserRegionMap[UserEmail] = USERPRINCIPALNAME ()
),
UserRegionMap[Region], "All"
)
This single DAX expression handles every user in the organisation. When Rahul opens the report, Power BI passes his email to USERPRINCIPALNAME(), looks up his region in the mapping table ("North"), and applies that filter automatically. Admins with "All" in the mapping see every region. No role changes are needed when new users are added — just add a row to the mapping table.
Multi-Level RLS: National → State → Branch
For organisations with hierarchical access (common in NBFC, insurance, FMCG distribution), extend the mapping table with all three levels and use a relationship-based filter that cascades:
-- DimBranch table filter (cascades to all related fact tables)
[BranchID] IN
CALCULATETABLE (
VALUES ( UserAccessMap[BranchID] ),
UserAccessMap[UserEmail] = USERPRINCIPALNAME ()
)
Because Power BI's relationships propagate filters, applying RLS on the Branch dimension automatically filters all related fact tables — Sales, Collections, Disbursements, Inventory — without needing separate filters on each fact table.
RLS on DirectQuery Models
RLS works with both Import and DirectQuery models, but with different performance characteristics. With Import, the RLS filter is applied to the in-memory model — fast. With DirectQuery, the RLS DAX filter is translated into a WHERE clause in the source database query — performance depends on the source database's index coverage on the filtered columns. Always test DirectQuery RLS performance with realistic data volumes before production deployment.
Testing RLS in Power BI Desktop
Use View As Role in Power BI Desktop to preview what a specific role sees before publishing. In Power BI Service, use Test As Role in the dataset settings to impersonate a specific user and verify their data access.
-- Always test these scenarios:
-- 1. User sees only their own region data
-- 2. User sees correct aggregated totals (not just filtered rows)
-- 3. Admin user sees all data
-- 4. New user not yet in mapping table sees no data (not an error)
-- 5. User with multiple regions in mapping sees data for all their regions
Common RLS Mistakes
- Applying RLS to the wrong table: RLS filters must be on the dimension table in a star schema, not the fact table — let the relationship propagate the filter to facts.
- Not testing with real email addresses:
USERPRINCIPALNAME()returns the exact AAD email. Mismatched casing or domain suffix (@company.com vs @company.in) will cause users to see no data. - Forgetting to publish roles to Power BI Service: RLS roles defined in Desktop must be published to Service and users assigned there — roles are not enforced in Desktop.
- Bidirectional relationships and RLS: Bidirectional relationships can bypass RLS filters in some model configurations. Audit your model relationships when implementing RLS.
RLS Performance Best Practices
- Keep the user mapping table small — index the email column in the source if loading from SQL Server
- Use Azure AD groups for role assignment where possible (easier management at scale)
- For very large user populations (10,000+), consider object-level security (OLS) in addition to RLS to hide entire tables from certain roles



