
Row-Level Security: Dynamic RLS Patterns
Implement advanced row-level security with dynamic rules, organizational hierarchies, and multi-tenant filtering for secure Power BI deployments at scale.
Row-level security (RLS) in Power BI restricts data access at the row level based on user identity, ensuring that a regional manager sees only their region's data while a VP sees everything. In multi-tenant environments, regulated industries, and any organization handling sensitive data, RLS is not a nice-to-have — it is a compliance requirement. Misconfigured RLS has caused HIPAA violations, financial data exposure, and audit failures. Our Power BI consulting team implements dynamic RLS patterns for healthcare organizations, financial services firms, and government agencies where data access controls must be demonstrably correct and auditable.
This guide covers static and dynamic RLS patterns, multi-tenant security architectures, testing methodologies, performance implications, and governance practices for enterprise Power BI environments in 2026.
Understanding RLS Fundamentals
RLS works by applying DAX filter expressions to tables based on the authenticated user's identity. When a user opens a report, the Power BI engine evaluates the RLS filter and only returns rows that pass the filter condition.
RLS execution flow:
``` User opens report -> Power BI authenticates user (Azure AD) -> Identifies user's RLS role membership -> Applies DAX filter to each secured table -> All visuals only see filtered data -> User cannot access restricted rows through any interaction ```
Key properties: - RLS filters are applied at the model level, not the visual level — there is no way for a user to bypass them through slicers, drillthrough, or Q&A - RLS is enforced in Power BI Service, Embedded, and Mobile - RLS is NOT enforced in Power BI Desktop (developers see all data for design purposes) - Multiple RLS roles are additive — a user in multiple roles gets the UNION of all role filters - RLS interacts with relationships — a filter on a dimension table cascades to related fact tables
Static RLS Patterns
Static RLS uses hard-coded values in filter expressions. It works but does not scale.
Example: Region-based static RLS
Create a role "East Region" with this filter on the Geography table: ```dax [Region] = "East" ```
Create a role "West Region" with: ```dax [Region] = "West" ```
Problems with static RLS at scale: - 50 regions = 50 roles to manage - Adding a new region requires model changes and redeployment - User-to-role assignment becomes a spreadsheet-tracking exercise - Cannot handle users who need access to multiple non-contiguous regions - Audit requires reviewing every role's filter expression
Use static RLS only for: Simple scenarios with fewer than 5 roles that rarely change.
Dynamic RLS Patterns
Dynamic RLS uses the authenticated user's identity to look up their permissions at query time. One role handles all users.
Pattern 1: User-to-Region Mapping Table
The most common enterprise pattern. A security mapping table defines which users can access which data.
Security table structure:
| UserPrincipalName | Region |
|---|---|
| [email protected] | East |
| [email protected] | Central |
| [email protected] | West |
| [email protected] | ALL |
RLS filter on the Geography table: ```dax [Region] IN SELECTCOLUMNS( FILTER( SecurityMapping, SecurityMapping[UserPrincipalName] = USERPRINCIPALNAME() || SecurityMapping[Region] = "ALL" ), "Region", SecurityMapping[Region] ) ```
Note the "ALL" pattern: Users tagged with "ALL" in the security table see all data. This handles executives and administrators without a separate role.
How to implement:
- Create the SecurityMapping table in your data source
- Import it into the Power BI model (set to Import mode for performance)
- Create a relationship from SecurityMapping[Region] to Geography[Region] (inactive, for reference)
- Create a single RLS role called "DynamicSecurity"
- Apply the DAX filter on the Geography table
- Add ALL Power BI users to the DynamicSecurity role
- Manage access by updating rows in the SecurityMapping table (no model changes needed)
Pattern 2: Hierarchical Security
When organizational hierarchy determines data access — a VP sees all their direct reports' data, a director sees their team's data, a manager sees their own data.
Security table with hierarchy:
| UserPrincipalName | ManagerChain |
|---|---|
| [email protected] | VP_Sales |
| [email protected] | VP_Sales/Dir_East |
| [email protected] | VP_Sales/Dir_East/Mgr_NY |
RLS filter using PATH functions: ```dax VAR CurrentUser = USERPRINCIPALNAME() VAR UserPath = LOOKUPVALUE( SecurityHierarchy[ManagerChain], SecurityHierarchy[UserPrincipalName], CurrentUser ) RETURN PATHCONTAINS( SecurityHierarchy[ManagerChain], UserPath ) ```
This pattern enables: A VP sees all data under their hierarchy branch. Adding a new team member only requires a row in the security table.
Pattern 3: Multi-Tenant Dynamic RLS
For organizations serving multiple tenants (business units, clients, or external customers) from a shared dataset.
Multi-tenant security model:
| UserPrincipalName | TenantID | AccessLevel |
|---|---|---|
| [email protected] | TENANT_A | ReadAll |
| [email protected] | TENANT_A | ReadOwn |
| [email protected] | ALL | Admin |
RLS filter on the Fact table: ```dax VAR CurrentUser = USERPRINCIPALNAME() VAR UserTenants = CALCULATETABLE( VALUES(TenantSecurity[TenantID]), TenantSecurity[UserPrincipalName] = CurrentUser ) RETURN [TenantID] IN UserTenants || "ALL" IN UserTenants ```
**This pattern is critical for multi-tenant architectures.** Combined with composite models, it provides data isolation within a shared model.
Pattern 4: Object-Level Security (OLS) Combined with RLS
While RLS restricts rows, Object-Level Security restricts columns and tables. Use them together for defense in depth.
Example: HR dataset where: - RLS ensures managers only see their direct reports' records - OLS hides the Salary column from non-HR users - OLS hides the Medical Leave table from non-benefits users
OLS implementation: Configure in Tabular Editor — define roles with table/column permissions. OLS roles are separate from RLS roles but can be combined.
Testing RLS Thoroughly
RLS misconfiguration is a security vulnerability. Testing must be systematic and documented.
Testing Method 1: "View as" in Power BI Desktop
Power BI Desktop's "View as" feature (Modeling tab > View as) lets you test RLS by simulating a specific user.
Limitations: - Only tests the DAX filter logic - Cannot test Azure AD group membership - Cannot test multiple roles simultaneously in all scenarios
Testing Method 2: DAX Query View
Use DAX queries to validate RLS returns the correct row counts:
```dax EVALUATE CALCULATETABLE( ROW( "TotalRows", COUNTROWS(Sales), "TotalRegions", DISTINCTCOUNT(Geography[Region]), "TotalRevenue", SUM(Sales[Revenue]) ), USEROBJECTID("alice-object-id-here") ) ```
Compare results against expected values for each test user.
Testing Method 3: Automated RLS Validation
Build automated tests that run during CI/CD deployment:
```python def test_rls_regional_manager(): """Alice should only see East and Central regions.""" result = execute_dax_as_user( "[email protected]", "EVALUATE VALUES(Geography[Region])" ) expected = {"East", "Central"} assert set(result) == expected, f"Expected {expected}, got {result}"
def test_rls_executive(): """Carol (ALL access) should see all regions.""" result = execute_dax_as_user( "[email protected]", "EVALUATE VALUES(Geography[Region])" ) assert len(result) == total_region_count
def test_rls_no_access_user(): """Unknown user should see zero rows.""" result = execute_dax_as_user( "[email protected]", "EVALUATE ROW('Count', COUNTROWS(Sales))" ) assert result[0] == 0 ```
Testing Matrix
For every RLS implementation, complete this test matrix:
| Test Case | User | Expected Behavior | Verified |
|---|---|---|---|
| Regional user sees own region | [email protected] | East + Central only | Yes/No |
| Regional user cannot see other regions | [email protected] | West not visible | Yes/No |
| Executive sees all data | [email protected] | All regions | Yes/No |
| Unknown user sees nothing | [email protected] | Zero rows | Yes/No |
| Multi-role user gets union | [email protected] (2 roles) | Both roles' data | Yes/No |
| New hire with no mapping | [email protected] | Zero rows (safe default) | Yes/No |
Performance Impact of RLS
RLS adds computational overhead. For simple patterns, the impact is negligible. For complex patterns, it can degrade query performance significantly.
Performance factors:
| Pattern | Performance Impact | Optimization |
|---|---|---|
| Simple column filter | Negligible | None needed |
| USERPRINCIPALNAME() lookup | Low | Keep security table small and Imported |
| PATH-based hierarchy | Medium | Pre-flatten hierarchy, limit depth to 5-7 levels |
| CONTAINS with large lists | Medium-High | Use relationships instead of CONTAINS where possible |
| CROSSFILTER manipulation | High | Avoid in RLS expressions; use model relationships instead |
| Multiple secured tables | Cumulative | Minimize the number of tables with direct RLS filters; rely on relationship propagation |
**Optimization best practices:** - Always Import (not DirectQuery) the security mapping table - Minimize the size of the security table — only include active users - Use relationships to propagate security filters rather than applying filters to every table - Test DAX performance with RLS enabled (use "View as" while running Performance Analyzer) - Monitor query times per user role in production using monitoring tools
RLS with Different Data Connectivity Modes
| Mode | RLS Behavior | Considerations |
|---|---|---|
| Import | Filter applied in VertiPaq engine | Fastest, most common |
| DirectQuery | Filter translated to SQL WHERE clause pushed to source | Source must handle additional predicates efficiently |
| Composite Model | Import tables filtered in VertiPaq; DQ tables filtered at source | Test both paths independently |
| Direct Lake | Filter applied during Parquet file reading | Similar to Import performance |
| Live Connection to AAS | RLS defined in Azure Analysis Services, not Power BI | Manage in AAS, not Power BI Service |
RLS Governance
Access Management
- Never assign individual users to RLS roles directly. Use Azure AD security groups. Assign groups to roles.
- Document the security model. Maintain a diagram showing which tables have RLS, what the filter expression is, and which groups are assigned.
- Review access quarterly. Cross-reference group membership with the security mapping table.
- Implement a change process. Security table modifications should go through approval workflow, not ad-hoc edits.
Compliance Documentation
For regulated industries, document:
- Data classification of each table (PII, PHI, financial, public)
- RLS roles and their filter logic
- User-to-role assignment matrix
- Test results showing RLS correctly restricts access
- Audit log showing who accessed what data and when (via Activity Log)
**For HIPAA compliance:** Demonstrate that PHI is restricted to authorized users with documented role assignments and quarterly access reviews. See our healthcare analytics guide for HIPAA-specific patterns.
For SOX compliance: Demonstrate separation of duties — users who manage RLS roles should not be the same users who certify datasets.
Frequently Asked Questions
Can users bypass RLS by creating their own reports? No. RLS is enforced at the semantic model level. Any report, Q&A query, or API call that reads the dataset will have RLS applied. However, if users have Build permission on the dataset, they can create reports and see data within their RLS scope (not beyond it).
**Does RLS work with Power BI Embedded?** Yes. When generating embed tokens, you can specify effective identity and RLS roles. For multi-tenant embedded scenarios, this is the primary security mechanism. See our embedded analytics guide.
How many RLS roles can a model have? No practical limit on role count, but dynamic RLS (one role with a lookup table) is vastly easier to manage than hundreds of static roles.
What happens if a user is not assigned to any RLS role? They see all data — which is a security risk. Best practice: Enable RLS and assign ALL consumers to at least one role. The default should be "see nothing," not "see everything."
Can I use Azure AD group membership in RLS filters? Not directly in DAX. However, you can maintain a mapping table that syncs Azure AD group memberships via Microsoft Graph API. Update this table on a schedule to keep RLS in sync with group changes.
Next Steps
Row-level security is one of the most important yet most commonly misconfigured aspects of enterprise Power BI. Getting it right requires careful design, thorough testing, and ongoing governance. Our Power BI consulting team designs and implements dynamic RLS architectures for regulated industries where data access control failures have real consequences. Contact us to discuss your security requirements.
**Related resources:** - Power BI Security Best Practices - Enterprise BI Architecture Patterns - Governance Framework Implementation - Power BI Consulting Services
Enterprise Implementation Best Practices
Row-level security implementations in regulated industries demand precision that goes beyond writing correct DAX filters. Having deployed dynamic RLS architectures for healthcare organizations handling PHI, financial institutions under SOX compliance, and government agencies requiring FedRAMP controls, these practices prevent the security failures that lead to audit findings and data breaches.
- Default to zero-access. Design your RLS architecture so that any user not explicitly granted access sees zero rows. This means never using "exclude" logic — always use "include" logic where the security mapping table explicitly defines what each user can see. A user missing from the mapping table should see nothing, not everything.
- Use Azure AD security groups for role assignment, never individual users. Assigning individual users to RLS roles creates an unmanageable security posture at scale. Map Azure AD groups to roles, and manage group membership through your identity governance process. This integrates RLS access reviews with your existing IAM lifecycle.
- **Implement automated security table synchronization.** The security mapping table must reflect current organizational reality. Build a scheduled process (Power Automate, Azure Function, or Fabric notebook) that syncs the security table from your authoritative source (HR system, Active Directory) at least daily. Stale security tables are the most common source of RLS failures.
- **Build a comprehensive test harness before production deployment.** Create automated tests that validate every user-to-data mapping using the XMLA endpoint. For each user in the security table, execute a count query and compare against expected row counts. Run these tests in your CI/CD pipeline before every deployment. Manual "View as" testing does not scale beyond 10 users.
- Document the security model for compliance auditors. Maintain a living document that includes: data classification of each table, RLS filter logic in plain English, user-to-role mapping procedures, testing evidence, and quarterly access review results. For HIPAA environments, this documentation is required for audit readiness. For SOX environments, demonstrate separation of duties between RLS administrators and data consumers.
- **Monitor RLS performance impact proactively.** Dynamic RLS adds query overhead that compounds with model complexity. Benchmark query performance with and without RLS using DAX Studio. For security mapping tables with more than 10,000 rows, consider partitioning strategies or pre-computed security flags on fact tables.
- Test the "ALL" access pattern rigorously. Executive users who should see all data (the "ALL" flag in the security table) must be tested independently. A common bug is an "ALL" user seeing aggregated data incorrectly because the RLS filter interacts unexpectedly with DAX measures that use CALCULATE with REMOVEFILTERS.
- Plan for organizational changes. Mergers, reorganizations, and rapid hiring create security mapping table churn. Build your automation to handle bulk changes gracefully — a reorganization that moves 500 employees should not require 500 manual updates. Design the mapping table with organizational hierarchy references that cascade automatically.
Measuring Success and ROI
RLS implementations protect against compliance violations and data breaches whose costs dwarf the implementation investment. Track these metrics to demonstrate security posture improvement and justify ongoing RLS governance investment.
**Compliance and security metrics:** - **RLS coverage rate:** Percentage of datasets containing PII, PHI, or financial data that have active RLS rules. Target 100% for regulated data. Track this through automated Scanner API inventory scans that cross-reference data classification labels with RLS configuration. - Access review completion rate: Percentage of quarterly access reviews completed on schedule. Regulatory frameworks (HIPAA, SOX) require documented evidence of periodic access reviews. Track completion rate and remediation actions taken. - Security mapping table freshness: Maximum lag between an organizational change (new hire, termination, transfer) and the corresponding security table update. Target under 24 hours. Each hour of lag represents a window where terminated employees may retain data access or new employees cannot access required data. - Test automation coverage: Percentage of user-to-data mappings validated by automated tests. Manual testing covers maybe 5% of mappings. Automated test suites should validate 100% of security mappings on every deployment. - Incident avoidance value: Calculate the cost of a potential data breach or compliance violation in your industry (healthcare: $100-$400 per record under HIPAA, financial: $150-$350 per record under SOX). Multiply by the number of records protected by RLS. This quantifies the risk reduction that RLS delivers — typically millions of dollars in avoided exposure.
For expert help implementing row-level security in your enterprise, contact our consulting team for a free assessment.``` User opens report -> Power BI authenticates user (Azure AD) -> Identifies user's RLS role membership -> Applies DAX filter to each secured table -> All visuals only see filtered data -> User cannot access restricted rows through any interaction ```
Key properties: - RLS filters are applied at the model level, not the visual level — there is no way for a user to bypass them through slicers, drillthrough, or Q&A - RLS is enforced in Power BI Service, Embedded, and Mobile - RLS is NOT enforced in Power BI Desktop (developers see all data for design purposes) - Multiple RLS roles are additive — a user in multiple roles gets the UNION of all role filters - RLS interacts with relationships — a filter on a dimension table cascades to related fact tables
Static RLS Patterns
Static RLS uses hard-coded values in filter expressions. It works but does not scale.
Example: Region-based static RLS
Create a role "East Region" with this filter on the Geography table: ```dax [Region] = "East" ```
Create a role "West Region" with: ```dax [Region] = "West" ```
Problems with static RLS at scale: - 50 regions = 50 roles to manage - Adding a new region requires model changes and redeployment - User-to-role assignment becomes a spreadsheet-tracking exercise - Cannot handle users who need access to multiple non-contiguous regions - Audit requires reviewing every role's filter expression
Use static RLS only for: Simple scenarios with fewer than 5 roles that rarely change.
Dynamic RLS Patterns
Dynamic RLS uses the authenticated user's identity to look up their permissions at query time. One role handles all users.
Pattern 1: User-to-Region Mapping Table
The most common enterprise pattern. A security mapping table defines which users can access which data.
Security table structure:
| UserPrincipalName | Region |
|---|---|
| [email protected] | East |
| [email protected] | Central |
| [email protected] | West |
| [email protected] | ALL |
RLS filter on the Geography table: ```dax [Region] IN SELECTCOLUMNS( FILTER( SecurityMapping, SecurityMapping[UserPrincipalName] = USERPRINCIPALNAME() || SecurityMapping[Region] = "ALL" ), "Region", SecurityMapping[Region] ) ```
Note the "ALL" pattern: Users tagged with "ALL" in the security table see all data. This handles executives and administrators without a separate role.
How to implement:
- Create the SecurityMapping table in your data source
- Import it into the Power BI model (set to Import mode for performance)
- Create a relationship from SecurityMapping[Region] to Geography[Region] (inactive, for reference)
- Create a single RLS role called "DynamicSecurity"
- Apply the DAX filter on the Geography table
- Add ALL Power BI users to the DynamicSecurity role
- Manage access by updating rows in the SecurityMapping table (no model changes needed)
Pattern 2: Hierarchical Security
When organizational hierarchy determines data access — a VP sees all their direct reports' data, a director sees their team's data, a manager sees their own data.
Security table with hierarchy:
| UserPrincipalName | ManagerChain |
|---|---|
| [email protected] | VP_Sales |
| [email protected] | VP_Sales/Dir_East |
| [email protected] | VP_Sales/Dir_East/Mgr_NY |
RLS filter using PATH functions: ```dax VAR CurrentUser = USERPRINCIPALNAME() VAR UserPath = LOOKUPVALUE( SecurityHierarchy[ManagerChain], SecurityHierarchy[UserPrincipalName], CurrentUser ) RETURN PATHCONTAINS( SecurityHierarchy[ManagerChain], UserPath ) ```
This pattern enables: A VP sees all data under their hierarchy branch. Adding a new team member only requires a row in the security table.
Pattern 3: Multi-Tenant Dynamic RLS
For organizations serving multiple tenants (business units, clients, or external customers) from a shared dataset.
Multi-tenant security model:
| UserPrincipalName | TenantID | AccessLevel |
|---|---|---|
| [email protected] | TENANT_A | ReadAll |
| [email protected] | TENANT_A | ReadOwn |
| [email protected] | ALL | Admin |
RLS filter on the Fact table: ```dax VAR CurrentUser = USERPRINCIPALNAME() VAR UserTenants = CALCULATETABLE( VALUES(TenantSecurity[TenantID]), TenantSecurity[UserPrincipalName] = CurrentUser ) RETURN [TenantID] IN UserTenants || "ALL" IN UserTenants ```
**This pattern is critical for multi-tenant architectures.** Combined with composite models, it provides data isolation within a shared model.
Pattern 4: Object-Level Security (OLS) Combined with RLS
While RLS restricts rows, Object-Level Security restricts columns and tables. Use them together for defense in depth.
Example: HR dataset where: - RLS ensures managers only see their direct reports' records - OLS hides the Salary column from non-HR users - OLS hides the Medical Leave table from non-benefits users
OLS implementation: Configure in Tabular Editor — define roles with table/column permissions. OLS roles are separate from RLS roles but can be combined.
Testing RLS Thoroughly
RLS misconfiguration is a security vulnerability. Testing must be systematic and documented.
Testing Method 1: "View as" in Power BI Desktop
Power BI Desktop's "View as" feature (Modeling tab > View as) lets you test RLS by simulating a specific user.
Limitations: - Only tests the DAX filter logic - Cannot test Azure AD group membership - Cannot test multiple roles simultaneously in all scenarios
Testing Method 2: DAX Query View
Use DAX queries to validate RLS returns the correct row counts:
```dax EVALUATE CALCULATETABLE( ROW( "TotalRows", COUNTROWS(Sales), "TotalRegions", DISTINCTCOUNT(Geography[Region]), "TotalRevenue", SUM(Sales[Revenue]) ), USEROBJECTID("alice-object-id-here") ) ```
Compare results against expected values for each test user.
Testing Method 3: Automated RLS Validation
Build automated tests that run during CI/CD deployment:
```python def test_rls_regional_manager(): """Alice should only see East and Central regions.""" result = execute_dax_as_user( "[email protected]", "EVALUATE VALUES(Geography[Region])" ) expected = {"East", "Central"} assert set(result) == expected, f"Expected {expected}, got {result}"
def test_rls_executive(): """Carol (ALL access) should see all regions.""" result = execute_dax_as_user( "[email protected]", "EVALUATE VALUES(Geography[Region])" ) assert len(result) == total_region_count
def test_rls_no_access_user(): """Unknown user should see zero rows.""" result = execute_dax_as_user( "[email protected]", "EVALUATE ROW('Count', COUNTROWS(Sales))" ) assert result[0] == 0 ```
Testing Matrix
For every RLS implementation, complete this test matrix:
| Test Case | User | Expected Behavior | Verified |
|---|---|---|---|
| Regional user sees own region | [email protected] | East + Central only | Yes/No |
| Regional user cannot see other regions | [email protected] | West not visible | Yes/No |
| Executive sees all data | [email protected] | All regions | Yes/No |
| Unknown user sees nothing | [email protected] | Zero rows | Yes/No |
| Multi-role user gets union | [email protected] (2 roles) | Both roles' data | Yes/No |
| New hire with no mapping | [email protected] | Zero rows (safe default) | Yes/No |
Performance Impact of RLS
RLS adds computational overhead. For simple patterns, the impact is negligible. For complex patterns, it can degrade query performance significantly.
Performance factors:
| Pattern | Performance Impact | Optimization |
|---|---|---|
| Simple column filter | Negligible | None needed |
| USERPRINCIPALNAME() lookup | Low | Keep security table small and Imported |
| PATH-based hierarchy | Medium | Pre-flatten hierarchy, limit depth to 5-7 levels |
| CONTAINS with large lists | Medium-High | Use relationships instead of CONTAINS where possible |
| CROSSFILTER manipulation | High | Avoid in RLS expressions; use model relationships instead |
| Multiple secured tables | Cumulative | Minimize the number of tables with direct RLS filters; rely on relationship propagation |
**Optimization best practices:** - Always Import (not DirectQuery) the security mapping table - Minimize the size of the security table — only include active users - Use relationships to propagate security filters rather than applying filters to every table - Test DAX performance with RLS enabled (use "View as" while running Performance Analyzer) - Monitor query times per user role in production using monitoring tools
RLS with Different Data Connectivity Modes
| Mode | RLS Behavior | Considerations |
|---|---|---|
| Import | Filter applied in VertiPaq engine | Fastest, most common |
| DirectQuery | Filter translated to SQL WHERE clause pushed to source | Source must handle additional predicates efficiently |
| Composite Model | Import tables filtered in VertiPaq; DQ tables filtered at source | Test both paths independently |
| Direct Lake | Filter applied during Parquet file reading | Similar to Import performance |
| Live Connection to AAS | RLS defined in Azure Analysis Services, not Power BI | Manage in AAS, not Power BI Service |
RLS Governance
Access Management
- Never assign individual users to RLS roles directly. Use Azure AD security groups. Assign groups to roles.
- Document the security model. Maintain a diagram showing which tables have RLS, what the filter expression is, and which groups are assigned.
- Review access quarterly. Cross-reference group membership with the security mapping table.
- Implement a change process. Security table modifications should go through approval workflow, not ad-hoc edits.
Compliance Documentation
For regulated industries, document:
- Data classification of each table (PII, PHI, financial, public)
- RLS roles and their filter logic
- User-to-role assignment matrix
- Test results showing RLS correctly restricts access
- Audit log showing who accessed what data and when (via Activity Log)
**For HIPAA compliance:** Demonstrate that PHI is restricted to authorized users with documented role assignments and quarterly access reviews. See our healthcare analytics guide for HIPAA-specific patterns.
For SOX compliance: Demonstrate separation of duties — users who manage RLS roles should not be the same users who certify datasets.
Frequently Asked Questions
Can users bypass RLS by creating their own reports? No. RLS is enforced at the semantic model level. Any report, Q&A query, or API call that reads the dataset will have RLS applied. However, if users have Build permission on the dataset, they can create reports and see data within their RLS scope (not beyond it).
**Does RLS work with Power BI Embedded?** Yes. When generating embed tokens, you can specify effective identity and RLS roles. For multi-tenant embedded scenarios, this is the primary security mechanism. See our embedded analytics guide.
How many RLS roles can a model have? No practical limit on role count, but dynamic RLS (one role with a lookup table) is vastly easier to manage than hundreds of static roles.
What happens if a user is not assigned to any RLS role? They see all data — which is a security risk. Best practice: Enable RLS and assign ALL consumers to at least one role. The default should be "see nothing," not "see everything."
Can I use Azure AD group membership in RLS filters? Not directly in DAX. However, you can maintain a mapping table that syncs Azure AD group memberships via Microsoft Graph API. Update this table on a schedule to keep RLS in sync with group changes.
Next Steps
Row-level security is one of the most important yet most commonly misconfigured aspects of enterprise Power BI. Getting it right requires careful design, thorough testing, and ongoing governance. Our Power BI consulting team designs and implements dynamic RLS architectures for regulated industries where data access control failures have real consequences. Contact us to discuss your security requirements.
**Related resources:** - Power BI Security Best Practices - Enterprise BI Architecture Patterns - Governance Framework Implementation - Power BI Consulting Services
Frequently Asked Questions
What is the performance impact of row-level security in Power BI?
RLS has minimal performance impact when implemented correctly. Power BI applies RLS filters at the storage engine level before aggregating data, so calculations only process authorized rows. Performance degradation occurs when: (1) RLS rules are overly complex with multiple LOOKUPVALUE functions, (2) Security tables are not properly related to fact tables, or (3) Many-to-many relationships force bidirectional filtering. Best practices for performance: use simple filters on dimension tables, ensure proper relationships exist, avoid calculated columns in RLS expressions, and use USERPRINCIPALNAME() lookups on indexed security tables. Well-designed RLS adds less than 100ms query overhead even with millions of rows.
Can I have different RLS rules for different reports in the same workspace?
No, RLS is defined at the dataset (semantic model) level, not per report. All reports connected to a dataset inherit the same RLS rules. However, you can implement conditional RLS that behaves differently based on context: (1) Use multiple roles with different rules and assign users to appropriate roles, (2) Create role-specific measures that show/hide data based on role membership, or (3) Deploy separate datasets with different RLS for different user groups. For complex scenarios with vastly different security requirements, consider deploying multiple specialized datasets rather than trying to consolidate all rules into one dataset. This improves both performance and maintainability.
How do I test RLS rules without publishing to Power BI Service?
Power BI Desktop includes View as Role feature for testing RLS locally. In Modeling tab, select View As and choose roles to test. You can also specify a particular user to simulate USERNAME() or USERPRINCIPALNAME() functions. This shows exactly what that role/user would see in the report. For automated testing, use Tabular Editor or DAX Studio to query the model with different security contexts. In Power BI Service, workspace admins can use View As to test RLS without being assigned to roles. Always test edge cases: users with no matching security records, users in multiple regions, and users with conflicting role assignments. Common mistake: forgetting to test with actual usernames from Azure AD—test with real UPNs before production rollout.