How-to guide
Power BI Row Level Security Setup
Done right, power bi row level security setup lets one report serve everyone while quietly showing each person only the rows they are allowed to see. Row-level security — RLS — filters data at the model level based on who is signed in, so a regional manager sees only their region and a rep sees only their accounts. This guide walks through static and dynamic RLS, the roles and DAX rules behind them, how to test, and the pitfalls that trip teams up.
The appeal is obvious once you have ever maintained five near-identical copies of the same report, one per region, just to keep people from seeing each other’s numbers. With RLS you build the report once and let the model decide, at query time, which rows each viewer is entitled to. The mechanism is a set of roles, each carrying one or more DAX filter expressions, mapped to users or security groups after you publish. Microsoft documents the full behavior in its row-level security documentation (opens in new tab), which is the canonical reference once the concepts below make sense.
What is row-level security in Power BI?
Row-level security is a model-level filter that restricts which rows a given user can see in a report. You define named roles, attach DAX filter rules to specific tables, and assign people to those roles. When a viewer opens the report, Power BI silently applies their role’s filters, so two people see different data through the same visuals.
When do you actually need RLS?
You need RLS whenever different viewers of one report should see different slices of the same data — sales reps limited to their accounts, store managers to their stores, clients to their own records in a shared portal. If everyone is allowed to see everything, you do not need RLS. It governs data visibility, not which reports a person can open.
What is the difference between static and dynamic RLS?
Static RLS hard-codes the filter inside each role — one role per group, such as “West” filtering Region = "West". Dynamic RLS uses a single role whose DAX reads the signed-in user’s identity and filters accordingly, driven by a mapping table. Static is simpler for a few fixed groups; dynamic scales to hundreds of users without new roles.
The trade-off is maintenance. With static RLS, every new territory means a new role and a new member assignment in the service — fine for three or four regions, painful at thirty. Dynamic RLS pushes that complexity out of the model and into data: you maintain a small bridge table mapping each user’s email to the dimensions they may see, and the same one role serves everyone. Most production deployments end up dynamic for exactly this reason.
| Aspect | Static RLS | Dynamic RLS |
|---|---|---|
| How roles map to users | One role per group; users assigned to the matching role | A single role that reads the signed-in user’s identity |
| The DAX filter | Hard-coded value, e.g. [Region] = "West" |
Identity-driven, e.g. [UserEmail] = USERPRINCIPALNAME ( ) |
| Maintenance as users grow | A new role and assignment for every new group | Add a row to the mapping table — no model change |
| Uses USERPRINCIPALNAME() | No | Yes — to match the viewer to a mapping table |
| Best for | A few fixed groups that rarely change | Many users and territories that scale over time |
How do you create a role and a DAX filter rule?
In Power BI Desktop, open the Modeling ribbon and choose Manage roles. Create a role, name it clearly (for example, West Region), pick the table to restrict, and enter a DAX boolean that returns true for the rows that role may see. Every row where the expression is true stays visible; every other row is hidden.
A static filter on a dimension is the simplest case:
[Region] = "West"
You can combine conditions with && and ||, and you can place rules on more than one table within the same role. Two rules on two tables behave as an AND across those tables. A common mistake is filtering the large fact table directly; instead, filter the smaller dimension (such as DimRegion) and let the model’s relationships propagate that filter down to the fact table. That is both cleaner and faster.
How does dynamic RLS work with USERPRINCIPALNAME?
Dynamic RLS uses the USERPRINCIPALNAME() function (opens in new tab), which returns the signed-in user’s login (usually their email). You store a mapping of users to the values they may see in a security table, then write one rule that filters that table to the current user. The model’s relationships carry the restriction through to the fact data automatically.
Suppose a UserRegion table has columns UserEmail and Region. The rule on that table reads:
[UserEmail] = USERPRINCIPALNAME ( )
Now UserRegion is filtered to only the rows belonging to the signed-in person. If UserRegion relates to DimRegion, which relates to your Sales fact table, that single filter cascades down and the user sees only their regions’ sales. Add a new user by adding a row to the mapping table — no model change, no redeploy. For broader matching you can also use USERNAME(), but USERPRINCIPALNAME() is the reliable choice in the cloud service because it consistently returns the user’s UPN.
How do you test RLS roles before publishing?
In Power BI Desktop, use Modeling → View as to test. Tick a role to see the report exactly as a member of that role would, and for dynamic RLS also tick Other user and type a specific email so USERPRINCIPALNAME() resolves to that person. Confirm the totals shrink to the expected subset before you ship anything.
Testing is the step people skip and regret. “View as” is the only way to confirm your DAX actually does what you intend, and it catches the two most embarrassing failures early: a role that shows nothing because the filter is too strict or the relationship does not propagate, and a role that shows everything because the rule silently evaluated to true. Always test at least one populated case and one user who should see a narrow slice, and verify a grand total against a known figure.
How do you publish and assign members in the service?
Roles travel with the model when you publish, but membership does not — you assign people in the Power BI Service or Microsoft Fabric. Open the semantic model’s Security settings, pick each role, and add users or, far better, Microsoft Entra (Azure AD) security groups. Group-based assignment means you manage access in one place as people join and leave.
A few practical notes. Members with workspace Admin, Member, or Contributor roles are not constrained by RLS when they edit the model, so do not rely on RLS to hide data from your own report authors. Viewers are the ones RLS governs. For embedded and app scenarios the same role assignments apply, and apps respect the security defined on the underlying semantic model. After assigning, use Test as role in the service to confirm the live behavior matches what you saw in Desktop.
What are the most common RLS pitfalls?
The frequent failures are bidirectional cross-filtering that leaks rows or breaks propagation, users landing in multiple roles whose filters combine as a permissive union, performance drag from heavy filter DAX on large models, and admins forgetting that workspace editors bypass RLS entirely. Each is avoidable once you know to look for it.
- Bidirectional relationships. Two-way cross-filtering can let a filter travel back up a path you did not intend, exposing rows or producing blanks. RLS interacts with bidirectional filters in subtle ways; prefer single-direction relationships and only enable the
CROSSFILTERbehavior deliberately where a security rule needs it. - Role unions. If a person belongs to two roles, Power BI shows the union of what both roles permit, not the intersection. Someone in “West” and “East” sees both regions. Map users to a single role — dynamic RLS with a mapping table avoids this trap entirely.
- Performance. Complex filter expressions run on every query for every affected visual. Filter compact dimension tables rather than wide fact tables, keep mapping tables lean, and avoid expensive functions inside RLS rules so reports stay responsive.
- Editors bypass RLS. RLS restricts viewers, not workspace Admins, Members, or Contributors. If a person must see only their slice, give them the Viewer role, not edit access.
- No matching mapping row. In dynamic RLS, a user with no row in the mapping table sees nothing. Decide deliberately whether that “deny by default” behavior is what you want, and keep the mapping table current.
How does RLS depend on your data model?
RLS is only as reliable as the model under it. Filters propagate along relationships, so a clean star schema (opens in new tab) — slim dimensions joined to a central fact table with single-direction relationships — makes rules short, fast, and predictable. A tangled model forces convoluted DAX and is where leaks hide. If your relationships are not in order, fix the model first.
This is why we treat security and modeling as one job rather than a bolt-on. Getting the schema right is the bulk of the work; the RLS rules themselves are usually a handful of lines once the relationships are sound. If you want the foundations done properly, our data modeling and DAX service covers exactly this, and the same filter-context thinking behind RLS is explained in our guide to DAX measures versus calculated columns.
A short worked example, end to end
Say a company wants each regional manager to see only their region in a shared sales report. With dynamic RLS you add a UserRegion table mapping each manager’s email to their region, relate it to DimRegion, and relate DimRegion to the Sales fact table. You create one role with the rule [UserEmail] = USERPRINCIPALNAME ( ) on UserRegion. In Desktop you test with “View as → Other user” using a real manager’s email and confirm the totals match that region. You publish, assign an Entra security group of managers to the role in the service, and re-test there. New managers are onboarded by adding a row to UserRegion — the report and roles never change again.
The same pattern scales to viewers outside your own company. In a franchise network the mapping table ties each owner to the units they hold, so one report serves head office and every franchisee without anyone seeing another owner's numbers — see Power BI dashboards for franchise owners for how that model is built.
Do you have to set this up yourself?
You do not. RLS is one of those features that is simple in a demo and surprisingly fiddly in production, where multiple groups, security-group nesting, and embedded scenarios collide with a model that grew organically. If your team wants to own it, our training and support covers RLS hands-on, including testing discipline and the bidirectional and union traps above. If you would rather have it built and verified correctly the first time, that is the kind of task our prepaid hours are made for.
Want RLS set up correctly the first time?
Row-level security is easy to get subtly wrong — a leaking bidirectional filter or a permissive role union can quietly expose data. Bring your model to a free demo. We scope it, you approve, and most RLS setups land in 3–5 business days.
FAQ
Frequently asked questions
It can if the filter DAX is heavy or it runs against wide fact tables. Keep rules simple, filter compact dimension tables rather than large fact tables, and keep mapping tables lean. Done that way on a clean star schema, the performance cost is usually small and barely noticeable.
Viewers cannot, but workspace Admins, Members, and Contributors are not constrained by RLS when they edit the model. Do not rely on RLS to hide data from report authors. If a person must see only their slice, give them the Viewer role rather than edit access to the workspace.
Yes. RLS is enforced at the semantic model level, so it applies when a viewer connects through Analyze in Excel or other client tools, not just in the Power BI report. The same role assignments govern the data regardless of how the user queries the published model.
Use static RLS for a few fixed groups that rarely change, since it is simpler to set up. Use dynamic RLS when you have many users or territories: a single role plus a mapping table scales without new roles, and onboarding a user is just adding a row. Most production deployments end up dynamic.
Book a free demo
Get senior Power BI help, on demand
Tell us the task. We scope it, you approve, and a senior consultant delivers — typically in 3–5 business days. Prepaid hours that never expire.
- Prepaid hours from $100/hour — and they never expire
- Senior-led delivery — 20+ years on the Microsoft platform
- Typical 3–5 business day turnaround
- Full refund if you’re unhappy in your first 4 hours
The fastest way to reach us is the form — tell us the task and we'll reply within one business day.