MICROSOFT POWER BI • SECURITY AND GOVERNANCE

Row-Level Security (RLS) — Create basic RLS roles and test them in Desktop (intro)

Restrict data visibility at the row level so each user sees only the data they are authorized to access.

Historical Context & Motivation

Enterprise reporting platforms have always grappled with the challenge of serving multiple audiences from a single dataset. In traditional database systems, administrators controlled visibility through views, stored procedures, and per-user schemas — an approach that scaled poorly when hundreds of business users needed personalized slices of the same warehouse. As self-service BI tools gained traction in the 2010s, a new problem surfaced: analysts could build reports from shared models, yet there was no native mechanism to ensure a regional manager in Europe could not inadvertently browse revenue figures belonging to the Asia-Pacific division. Row-Level Security (RLS) emerged as the industry-standard answer, embedding access-control logic directly into the semantic model so that the BI engine itself filters rows before they ever reach a visual.

2009
SQL Server RLS Precursors
Microsoft introduced security predicates in SQL Server, allowing database admins to attach filter functions to tables. These predicates automatically limited which rows a user's query could return — an early form of row-level filtering baked into the relational engine.
2013
Power Pivot & Tabular Models
SQL Server Analysis Services (SSAS) Tabular models introduced DAX-based role definitions. Analysts could write DAX expressions that acted as row filters, evaluated at query time against the user's identity — the conceptual ancestor of Power BI RLS.
2015
Power BI Desktop Launches
Power BI Desktop shipped with a built-in role management UI, enabling report authors to define and test RLS roles entirely within the authoring tool before publishing to the Power BI Service.
2016–2018
Dynamic RLS & USERNAME()/USERPRINCIPALNAME()
Power BI added DAX functions that resolve the identity of the currently authenticated user. This enabled dynamic RLS, where a single role definition could serve hundreds of users by joining their credentials against a security table.
2023–Present
Microsoft Fabric & Enhanced RLS
With the rollout of Microsoft Fabric, RLS capabilities extended to Direct Lake semantic models and cross-workspace datasets, reinforcing RLS as the foundational governance primitive in Microsoft's modern analytics stack.

The central question RLS answers is deceptively simple: How can a single Power BI report serve many users while guaranteeing that each user sees only the rows permitted by their organizational role? Rather than duplicating reports or datasets per department, RLS lets you author once, define roles, attach DAX filter expressions, and let the engine handle the rest — a pattern that computer scientists will recognize as a policy-enforcement layer inserted between the query optimizer and the rendering pipeline.

Core Principles & Definitions

Before diving into implementation, it is essential to understand the conceptual building blocks that underpin RLS in Power BI. Every RLS configuration revolves around three entities: roles, DAX filter expressions, and member assignments. A role is a named container that groups one or more filter expressions; each expression targets a specific table and restricts which rows are visible when a user belonging to that role queries the model. Member assignments happen in the Power BI Service (or via the XMLA endpoint), where administrators map Azure Active Directory identities to the roles defined in Desktop.

1

Role

A named security context (e.g., "Europe Sales") defined inside the .pbix model. A role contains zero or more table-level filter expressions. Users assigned to a role see only the rows that pass every filter.
2

DAX Filter Expression

A Boolean DAX expression evaluated row-by-row against a table (e.g., [Region] = "Europe"). Only rows returning TRUE are included in query results for that role.
3

Static vs. Dynamic RLS

Static RLS hard-codes values in the filter (e.g., "Europe"). Dynamic RLS uses DAX functions like USERPRINCIPALNAME() to resolve the logged-in user's email, enabling one role to serve many users.
4

Filter Propagation

RLS filters propagate along active model relationships. Restricting rows in a dimension table automatically restricts the related fact table rows, provided the cross-filter direction supports propagation — a crucial consideration in star schemas.
5

View As Role (Testing)

Power BI Desktop provides a "View as" feature that simulates a role's perspective. The author can toggle into any defined role (or combine roles) to verify that the filters restrict the expected rows before publishing.
KEY TAKEAWAY
Think of an RLS role as a one-way mirror installed in a database hallway. Everyone walks through the same hallway (the shared semantic model), but each mirror only lets a user see the rooms (rows) their badge grants access to. The mirror is defined by a DAX expression; the badge is the user's Azure AD identity. Because the mirror is evaluated by the Power BI engine at query time, the report author never has to duplicate reports — the engine simply withholds rows that fail the filter.

Visual Explanation — How RLS Filters Data

The diagram traces a query from two users through the Power BI engine. After authentication, the engine resolves each user's role, injects the corresponding DAX filter expression, and returns only the rows that satisfy the filter. User A (Europe Sales) sees European data; User B (APAC Sales) sees Asia-Pacific data — both from the same underlying model.

As the diagram illustrates, RLS operates transparently at the engine level. Neither user writes a custom query nor even knows what data has been withheld — the visuals simply render the filtered result set as though it were the entire dataset. This is an important security property: the exclusion is enforced server-side (or engine-side in Desktop during testing), meaning a technically skilled user cannot bypass the filter by, say, exporting the underlying data. The DAX filter expression is the lynchpin of this architecture; it is a Boolean predicate evaluated against every row of the target table, and only rows for which the expression returns TRUE survive into the query result.

How RLS Works Under the Hood

Power BI's tabular engine (a descendant of SQL Server Analysis Services VertiPaq) evaluates RLS by wrapping every user-initiated query in an implicit CALCULATETABLE call that appends the role's filter expression to the existing filter context. Understanding this mechanism is crucial because it explains how RLS interacts with relationships, bidirectional cross-filtering, and measures that use ALL() or REMOVEFILTERS(). In formal terms, if a role defines a filter expression F on table T, then every DAX query Q issued by a member of that role is semantically rewritten as follows.

IMPLICIT FILTER INJECTION
Q′ = CALCULATETABLE( Q, FILTER( T, F(row) = TRUE ) )
Where Q is the original query, T is the table the filter targets, F(row) is the DAX filter expression evaluated per row, and Q′ is the rewritten query the engine actually executes.

Because the filter is injected into the filter context rather than the row context, it propagates along relationships exactly as any slicer or visual-level filter would. In a standard star schema, placing the RLS expression on a dimension table (e.g., DimRegion) causes the fact table (e.g., FactSales) to be filtered automatically via the active many-to-one relationship. However, if the relationship is set to single-direction cross-filter (the default), placing the filter on the fact table will not propagate back to the dimension table — a subtlety that frequently trips up first-time implementers.

STATIC RLS EXPRESSION EXAMPLE
[Region] = "Europe"
Applied to the DimRegion table. Every row where Region ≠ "Europe" is excluded from query results for members of this role.
DYNAMIC RLS EXPRESSION EXAMPLE
[Email] = USERPRINCIPALNAME()
Applied to a DimEmployee security table. At query time, USERPRINCIPALNAME() resolves to the authenticated user's email, so only the row matching that email survives — and the filter propagates to all related tables.
⚠️ Important: CALCULATE & ALL() Interaction
A common pitfall: measures using ALL(Table) to create ratio-to-total calculations will not override the RLS filter. The engine enforces RLS at a security layer that sits above normal filter-context manipulation. This means CALCULATE(SUM(Sales[Amount]), ALL(DimRegion)) still respects the RLS restriction — a deliberate design choice to prevent accidental data leakage through DAX.

Step-by-Step Implementation in Power BI Desktop

Implementing RLS in Power BI Desktop follows a well-defined workflow: you create one or more roles in the Modeling tab, attach DAX filter expressions to tables within each role, and then use the View as feature to test each role's behavior before publishing. The following diagram captures the end-to-end authoring and testing process.

The five-step workflow: (1) build your data model with proper relationships, (2) create named roles via the Modeling tab, (3) attach DAX filter expressions to the relevant tables, (4) test the role using "View as" in Desktop, and (5) publish to the Power BI Service where you assign actual users or Azure AD groups to each role.
  1. Step 1 — Build the data model. Import your tables and establish relationships. A star schema (dimension tables surrounding a central fact table) is ideal because RLS filters placed on dimensions naturally propagate to the fact table.
  2. Step 2 — Create roles. Navigate to Modeling → Manage Roles. Click "Create" and name the role descriptively (e.g., "Europe Sales", "APAC Sales", "Manager").
  3. Step 3 — Add DAX filter expressions. Select the table to filter, then enter a DAX expression that evaluates to TRUE or FALSE for each row. For example: [Region] = "Europe" on DimRegion.
  4. Step 4 — Test with "View as Roles." Go to Modeling → View as Roles. Check one or more roles and click OK. A yellow banner appears confirming you are viewing the report through the selected role's filter. Verify visuals show only the expected data.
  5. Step 5 — Publish and assign members. After publishing, open the dataset's security settings in the Power BI Service. Add Azure AD users or security groups to each role. RLS enforcement begins immediately upon assignment.

Worked Example — Building & Testing a Two-Role RLS Configuration

Consider a sales analytics model with three tables: FactSales (columns: SalesID, RegionKey, Amount), DimRegion (columns: RegionKey, Region), and DimProduct (columns: ProductKey, ProductName). FactSales has a many-to-one relationship to DimRegion on RegionKey. Our goal is to create two static RLS roles — "Europe Sales" and "APAC Sales" — and verify their behavior in Desktop.

Creating & Testing Two Static RLS Roles
1
Step 1 — Open Role ManagerIn Power BI Desktop, click Modeling → Manage Roles. The "Manage Roles" dialog opens, showing an empty list of roles and a table pane on the right.
2
Step 2 — Create the Europe Sales RoleClick Create. Rename the role to Europe Sales. In the table list, select DimRegion. In the DAX expression box, enter:
[Region] = "Europe"
3
Step 3 — Create the APAC Sales RoleClick Create again to add a second role. Name it APAC Sales. Select DimRegion and enter:
[Region] = "APAC"
4
Step 4 — Save and Open View AsClick Save to close the dialog. Then go to Modeling → View as Roles. A dialog lists all defined roles. Check Europe Sales and click OK. A yellow banner at the top of the report canvas reads: "Now viewing report as: Europe Sales."
5
Step 5 — Verify the Filtered ResultsInspect the visuals on your report page. A bar chart of sales by region should now display only the Europe bar. A table visual listing sales transactions should show only rows linked to RegionKey values where Region = "Europe". Click Stop Viewing in the banner, then repeat the process selecting APAC Sales to confirm that APAC data appears and European data is excluded.
Both roles correctly restrict data. Europe Sales shows only Europe rows; APAC Sales shows only APAC rows. The model is ready to publish.

Strengths, Limitations, and Comparisons

RLS Strengths vs. Limitations
AspectStrengthsLimitations
Single Model, Many ViewsOne .pbix file serves all audiences, reducing maintenance overhead and storage costs. Changes propagate to all users simultaneously.Complex organizations may require many roles with overlapping filter logic, which can become difficult to audit.
Engine-Level EnforcementRLS filters are injected before query execution and cannot be bypassed by DAX functions like ALL(). This provides defense-in-depth.Report authors with edit permissions on the dataset can modify or delete roles. Governance requires proper workspace permission separation.
Desktop TestingView as Roles lets you verify behavior before publishing, preventing costly post-deployment security gaps.Desktop testing does not fully simulate dynamic RLS because USERPRINCIPALNAME() returns the author's own identity unless overridden with the "Other user" option.
PerformanceStatic RLS adds negligible overhead because VertiPaq can apply simple equality filters at the storage engine level.Complex dynamic RLS with multi-hop relationships or large security tables can degrade query performance. Careful model design is essential.
Relationship AwarenessFilters propagate automatically along active relationships, so a single role filter on a dimension secures all related fact tables.Bidirectional cross-filtering can cause unintended data leakage if not carefully managed. Single-direction is the safer default.
KEY TAKEAWAY
RLS is analogous to a view-based access control layer in an operating system's file system. Just as a Unix file permission prevents a user from reading a file they lack access to — even if they know the file path — RLS prevents a user from seeing a row even if they know it exists in the model. The filter is mandatory and non-negotiable at query time, making it a declarative security boundary rather than a UI-level cosmetic restriction.

Connection to Advanced RLS Patterns

The static RLS approach introduced in this lesson is the foundation upon which more sophisticated patterns are built. As your organization's security requirements grow, you will encounter scenarios that demand dynamic resolution, hierarchical filtering, and hybrid strategies. The table below contrasts the introductory approach with the more advanced patterns you will study next.

Static vs. Dynamic RLS Comparison
FeatureBasic (Static) RLSAdvanced (Dynamic) RLS
Filter ExpressionHard-coded value: [Region] = "Europe"Identity-based: [Email] = USERPRINCIPALNAME()
Roles NeededOne role per region/department (can proliferate)Typically one role for all standard users; security table handles mapping
MaintenanceMust edit role definitions when values change (e.g., new region)Add rows to security table; no role definition changes needed
Hierarchy SupportRequires separate roles for each hierarchy level (e.g., manager vs. rep)PATHCONTAINS() and parent-child DAX patterns support org-chart hierarchies in a single role
Best ForSmall teams, proof-of-concept, learning RLS fundamentalsEnterprise deployments with hundreds or thousands of users

Beyond dynamic RLS, Power BI supports Object-Level Security (OLS), which hides entire tables or columns from unauthorized roles rather than filtering rows. OLS is defined through Tabular Editor or the XMLA endpoint and complements RLS when certain metadata itself is sensitive. Additionally, Microsoft Fabric introduces column-level security for Direct Lake models, expanding the governance surface. Mastering static RLS in Desktop, as covered here, gives you the conceptual vocabulary to engage with all of these advanced patterns.

Practice Problems

PROBLEM 1CONCEPTUAL
Explain in your own words why placing an RLS filter on a dimension table (e.g., DimRegion) effectively restricts the related fact table (FactSales) without writing a separate filter expression on the fact table itself. What model characteristic makes this possible?
PROBLEM 2BASIC CALCULATION
You have a DimDepartment table with columns DeptKey and DeptName. The table contains five rows: HR, Engineering, Marketing, Finance, and Legal. You create a role named "Tech" with the DAX filter [DeptName] = "Engineering" applied to DimDepartment. The related FactExpenses table has 500 total rows: 120 for Engineering, 100 for HR, 80 for Marketing, 110 for Finance, and 90 for Legal. How many rows from FactExpenses will a member of the "Tech" role see?
PROBLEM 3INTERMEDIATE
A model has two dimension tables, DimRegion and DimProduct, both related to FactSales. You create a single role "Restricted Analyst" with two filter expressions: [Region] = "Europe" on DimRegion and [Category] = "Electronics" on DimProduct. Describe the effective filter on FactSales. Is it a union (OR) or intersection (AND) of the two restrictions?
PROBLEM 4APPLIED
You are the BI developer for a university with four colleges: Arts, Science, Engineering, and Business. Each college dean should see only their college's enrollment, budget, and staffing data. The model follows a star schema with DimCollege, FactEnrollment, FactBudget, and FactStaffing, all linked to DimCollege via CollegeKey. Design an RLS configuration (list roles and DAX expressions), then explain how you would test the Engineering dean's view in Desktop.
PROBLEM 5CRITICAL THINKING
A colleague suggests skipping RLS entirely and instead building four separate .pbix files — one per region — each with a hard-coded Power Query filter that loads only that region's data. They argue this is "simpler" and "more secure because the data never even enters the model." Evaluate this approach against RLS on at least three criteria (maintainability, security, performance), and argue for or against their proposal.

Summary

Row-Level Security (RLS) enables a single Power BI semantic model to serve multiple audiences by restricting which rows each user can see. You define roles in Power BI Desktop, attach DAX filter expressions to specific tables within each role, and the engine injects those filters into the filter context at query time — automatically propagating the restriction along model relationships. Static RLS hard-codes values (e.g., [Region] = "Europe"), while dynamic RLS resolves the user's identity via USERPRINCIPALNAME().

Testing is performed using the View as Roles feature in Desktop, which simulates a role's perspective before publishing. After publishing, member assignments in the Power BI Service map Azure AD identities to roles. Multiple filter expressions within a single role combine as an intersection (AND), while membership in multiple roles produces a union (OR). Mastering static RLS in Desktop lays the groundwork for dynamic RLS, Object-Level Security, and enterprise-scale governance patterns in Microsoft Fabric.

Varsity Tutors • Microsoft Power BI • Row-Level Security (RLS) — Create basic RLS roles and test them in Desktop (intro)