
Implement static, dynamic, and hierarchical RLS to secure enterprise data at the row level across Power BI Service, Embedded, and DirectQuery.
Row-Level Security (RLS) in Power BI restricts data access at the row level based on user identity. Instead of creating separate reports for each department, region, or role, RLS dynamically filters a single dataset so users see only the data they are authorized to access. For enterprise organizations in regulated industries, RLS is not optional — it is a compliance requirement for protecting sensitive data including PHI, PII, and financial records.
Quick Answer: To implement RLS in Power BI, create a role in Power BI Desktop (Modeling > Manage Roles), define a DAX filter expression, and assign Azure AD users or groups to the role in Power BI Service. For enterprise, use dynamic RLS with USERPRINCIPALNAME() and a security mapping table to automatically filter data based on the logged-in user — scaling to thousands of users without individual role management.
This guide covers every enterprise RLS pattern — from basic static roles to complex manager-hierarchy security — based on EPC Group's experience implementing RLS across Fortune 500 Power BI deployments in healthcare, finance, and government.
Static RLS
Hardcoded role-based filtering
Dynamic RLS
User identity-based filtering
Object-Level (OLS)
Column and table visibility
DAX Patterns
Enterprise filter expressions
Static RLS uses hardcoded values in DAX filter expressions. Each role is defined with a specific data filter that applies to all users assigned to that role.
DAX — Static RLS Example (West Region Role)
[Region] = "West"Dynamic RLS uses DAX functions to resolve the current user identity and filter data based on a security mapping table — a single role scales to thousands of users.
DAX — Dynamic RLS Example
[UserEmail] = USERPRINCIPALNAME()Two key DAX functions power dynamic RLS. Understanding when to use each is critical for correct implementation.
Returns the Azure AD User Principal Name (UPN) of the current user — typically their email address (e.g., john.doe@contoso.com). This is the recommended function for enterprise RLS because UPN is a globally unique, Azure AD-governed identifier.
DAX — Security Table Filter with USERPRINCIPALNAME
// Filter the SecurityMapping table
// Users see only rows where their UPN matches
[UserEmail] = USERPRINCIPALNAME()Returns the user identity in DOMAIN\username format for on-premises environments or UPN format in Power BI Service. Use USERNAME() only when you need backward compatibility with Analysis Services or on-premises gateway scenarios.
DAX — USERNAME for On-Premises Scenarios
// USERNAME() returns DOMAIN\user on-prem
// Returns UPN in Power BI Service
[DomainUser] = USERNAME()The most complex enterprise RLS pattern — managers see data for their direct and indirect reports using parent-child DAX functions.
DAX — Manager Hierarchy RLS
// Step 1: Build hierarchy path (calculated column)
HierarchyPath =
PATH(Employee[EmployeeID], Employee[ManagerID])
// Step 2: RLS filter expression
VAR currentUser = USERPRINCIPALNAME()
VAR userID =
LOOKUPVALUE(
Employee[EmployeeID],
Employee[Email],
currentUser
)
RETURN
PATHCONTAINS(Employee[HierarchyPath], userID)When users need access across multiple dimensions (region + department + product line), use a security mapping table with multiple columns.
DAX — Multi-Dimension Security Filter
// SecurityMapping table has columns:
// UserEmail, Region, Department, ProductLine
// Create relationship from SecurityMapping
// to each dimension table
// RLS filter on SecurityMapping table:
[UserEmail] = USERPRINCIPALNAME()
// Relationships propagate filters to
// fact table through dimension tablesWhen using DirectQuery, RLS DAX expressions are translated to SQL WHERE clauses and pushed to the source database. This provides security at the database level — data never leaves the source unless the user is authorized to see it — which is a significant security advantage for sensitive environments.
Open Modeling > Manage Roles. Create roles with DAX filter expressions. Use dynamic RLS with USERPRINCIPALNAME() for enterprise scale.
Use Modeling > View as Roles. Select the role and optionally specify "Other user" to simulate specific identities. Validate that restricted data is not visible.
Publish the report and dataset to Power BI Service. Navigate to the dataset > Security tab.
Add Azure AD users or security groups to each role. Use security groups (not individual users) for maintainability. Assign at the Azure AD group level.
Click the ellipsis next to a role > "Test as role." Verify with multiple user identities, including edge cases (new users, multi-role users, admins).
Have data owners from each business unit validate they see exactly the correct data — no more, no less. Document sign-off.
Power BI Embedded enables ISVs and enterprise applications to embed analytics in custom applications. RLS in embedded scenarios requires passing an effective identity in the embed token — the embedded user does not need a Power BI license, but security is enforced server-side.
Your application authenticates with a service principal and generates embed tokens with customer identity claims. The application controls who sees what by including RLS roles and usernames in the GenerateToken API call.
// Embed token with RLS
EffectiveIdentity: {
username: "customer@contoso.com",
roles: ["DynamicRLS"],
datasets: ["dataset-guid"]
}The user authenticates directly with Azure AD. Their identity passes through to Power BI Service, and RLS is enforced based on their Azure AD group membership — identical to standard Power BI Service behavior.
// User token - RLS automatic
// Azure AD token passed to PBI
// RLS resolved from dataset
// security role assignmentsLearn more about embedded analytics patterns in our Power BI Embedded Analytics Guide.
Object-Level Security (OLS) complements RLS by hiding entire tables or columns from specific roles. While RLS filters rows, OLS prevents users from even knowing certain data exists in the model. OLS is configured using Tabular Editor — it is not available in the standard Power BI Desktop interface.
Healthcare (HIPAA)
Hide PHI columns (SSN, DOB, diagnosis codes) from non-clinical roles
Financial Services
Hide compensation and bonus data from non-HR roles
Government
Hide classified data columns from uncleared personnel
Multi-Tenant SaaS
Hide internal metrics tables from external customer-facing reports
Important: OLS requires Tabular Editor (external tool) to configure. It cannot be set up in Power BI Desktop directly. When OLS hides a column used by a visual, the visual shows an error — design reports with OLS restrictions in mind from the start, not as an afterthought.
Sales reps see only their assigned territories. Managers see their team territories. VPs see all regions. Implemented with a SecurityMapping table joining users to territory dimension.
Finance sees financial data, HR sees HR data, Operations sees operational metrics. Each department mapped in a security table with USERPRINCIPALNAME() lookup.
Managers see data for their direct and indirect reports using PATH/PATHCONTAINS DAX functions. Automatically cascades through the organizational hierarchy.
Budget owners see only their assigned cost centers. Cross-functional managers see multiple cost centers. CFO sees all. Security table maps users to cost center arrays.
Clinical staff see only their assigned patients. Care team leads see department patients. Audit logging on all RLS-filtered data access for HIPAA compliance.
Customer-facing embedded reports show only data belonging to the authenticated customer tenant. Internal users see cross-customer analytics for benchmarking.
RLS adds query overhead because every DAX query must be wrapped in security filter context. The performance impact varies by implementation complexity.
| RLS Pattern | Typical Overhead | Optimization Strategy |
|---|---|---|
| Simple Static RLS | 1-3% | No optimization needed — negligible impact |
| Dynamic RLS (USERPRINCIPALNAME) | 5-10% | Keep security table in Import mode; index join columns |
| Multi-Dimension Dynamic RLS | 8-15% | Pre-compute user-to-data mappings in ETL; minimize relationships |
| Hierarchical RLS (PATH) | 15-30% | Flatten hierarchy in ETL; use materialized security columns |
| RLS + OLS Combined | 10-20% | Test with DAX Studio; profile query plans for bottlenecks |
Most common cause: dataset owners and workspace admins bypass RLS by default. In Power BI Service, only users assigned to a role via the Security tab are filtered. Admins see all data for management purposes — use "Test as role" to validate the user experience.
Check that the user email in the security table exactly matches their Azure AD UPN (case-sensitive in some configurations). Verify the relationship between the security table and fact tables is active and filtering in the correct direction (single or both).
Desktop uses simulated identities while Service uses real Azure AD tokens. Common issues: UPN format differences (Desktop may show DOMAIN\user while Service uses email), B2B guest user identity format (guest#EXT#@tenant), and SSO configuration for DirectQuery.
Use DAX Studio to profile queries with and without RLS. Check if RLS filter expressions cause storage engine scans instead of seeks. Optimize by pre-computing security lookups, keeping security tables small, and ensuring join columns are indexed.
In composite models (Import + DirectQuery), RLS must be defined on both storage modes. DirectQuery tables apply RLS at the source, while Import tables apply RLS in the Vertipaq engine. Ensure consistent role definitions across both modes.
500+
Power BI deployments with RLS
28+
Years of Microsoft expertise
HIPAA
Compliant RLS for healthcare
10K+
Max users per RLS deployment
RLS misconfigurations are the number one cause of data breaches in Power BI environments. EPC Group implements enterprise-grade RLS with automated testing, compliance documentation, and ongoing security monitoring.
Enterprise dashboard development, data modeling, RLS implementation, and governance from EPC Group.
Read moreStar schema design, relationship optimization, and DAX patterns for enterprise Power BI models.
Read moreIntegrate Power BI analytics into custom applications with RLS enforcement via embed tokens.
Read moreTo implement Row-Level Security (RLS) in Power BI: 1) Open Power BI Desktop and go to Modeling > Manage Roles, 2) Create a new role and define a DAX filter expression on the table you want to restrict (e.g., [Region] = "West"), 3) For dynamic RLS, use USERPRINCIPALNAME() in your DAX expression to filter based on the logged-in user (e.g., [UserEmail] = USERPRINCIPALNAME()), 4) Test the role using "View as Role" in Desktop, 5) Publish to Power BI Service and assign users or Azure AD groups to the role under dataset Security settings. Dynamic RLS is preferred for enterprise because it scales without creating a new role for every user or permission combination.
Static RLS uses hardcoded values in DAX filter expressions — for example, [Region] = "West" — meaning each role sees a fixed subset of data. Dynamic RLS uses DAX functions like USERPRINCIPALNAME() or USERNAME() to automatically filter data based on the logged-in user identity, typically by joining a security table that maps users to their permitted data. Static RLS requires a separate role for each permission group, while dynamic RLS uses a single role with a security mapping table, making it far more scalable for enterprise. EPC Group recommends dynamic RLS for any deployment exceeding 10 distinct permission groups.
Yes, RLS works with DirectQuery in Power BI, but with important caveats. DAX filter expressions defined in roles are translated to SQL WHERE clauses and pushed to the source database, which means the filtering happens at the database level — providing both security and performance benefits. However, not all DAX functions translate cleanly to SQL. Functions like USERPRINCIPALNAME() work, but complex DAX calculations may not push down correctly, falling back to client-side evaluation which degrades performance. EPC Group recommends keeping DirectQuery RLS expressions simple and testing query pushdown behavior with SQL Profiler or Performance Analyzer.
In Power BI Desktop, test RLS using the "View as" feature: go to Modeling > View as Roles, select the role you want to test, and optionally check "Other user" to simulate a specific user identity for dynamic RLS testing. The report will refresh showing only data that the selected role/user would see. For enterprise testing, also test in Power BI Service by navigating to the dataset > Security tab > clicking the ellipsis next to a role > "Test as role." This tests with actual Azure AD identity resolution, which may differ from Desktop simulation in certain SSO and B2B guest scenarios.
Object-Level Security (OLS) restricts access to entire tables or columns in a Power BI data model, complementing Row-Level Security which filters rows. OLS is configured using Tabular Editor (not available in the standard Power BI Desktop UI) by setting table or column permissions to "None" for specific roles. This is critical for scenarios where certain user groups should not even see that a column or table exists — such as hiding salary columns from non-HR users, or hiding PHI columns from non-clinical staff in healthcare. OLS and RLS can be combined: OLS hides columns/tables, RLS filters visible rows.
For Power BI Embedded, RLS is enforced by passing an effective identity in the embed token generation API call. When generating a token using the Power BI REST API, include the EffectiveIdentity object specifying the username, roles, and datasets. The embedded report then renders with RLS applied for that identity — even though the end user may not have a Power BI license. This is essential for ISVs and customer-facing analytics applications. For "App Owns Data" scenarios, the service principal generates tokens with customer identity claims. For "User Owns Data," the user Azure AD token is passed through directly.
RLS does impact performance, but the degree depends on implementation. Simple static RLS adds minimal overhead (1-3% query time increase). Dynamic RLS with USERPRINCIPALNAME() lookups against a security table typically adds 5-10% overhead. Complex hierarchical RLS with parent-child DAX calculations can add 15-30% overhead on large models. Performance optimization strategies include: keeping the security mapping table in Import mode even if the main model uses DirectQuery, indexing the security column in source databases, using SUMMARIZECOLUMNS instead of nested CALCULATE for hierarchical filters, and pre-computing user-to-data mappings in the ETL pipeline.
Yes, Power BI RLS can enforce manager-hierarchy security using parent-child DAX functions. The pattern requires: 1) A dimension table with employee ID, manager ID, and email columns, 2) A DAX PATH function to build the hierarchy path for each employee, 3) A RLS filter using PATHCONTAINS to check if the current user exists in an employee hierarchy path. The DAX expression looks like: VAR currentUser = USERPRINCIPALNAME() VAR userEmployeeID = LOOKUPVALUE(Employee[EmployeeID], Employee[Email], currentUser) RETURN PATHCONTAINS(Employee[HierarchyPath], userEmployeeID). This ensures managers see data for their direct and indirect reports automatically.
Schedule a free Power BI security assessment. We will review your current data model, recommend the optimal RLS architecture, and ensure compliance with your industry regulations.