How to define static and dynamic row-level security roles in Power BI Desktop with DAX filters and USERPRINCIPALNAME.
From Ultra Transcenders PL-300 by Tony Rough (coming December 2026)
Row-level security (RLS) restricts which rows each user can see. Roles and their DAX filter rules are defined in Power BI Desktop (or when editing the data model in the service) and published with the model; members are assigned later in the service.
Dynamic rules using USERNAME() or USERPRINCIPALNAME() can only be written in the DAX editor. In the DAX editor, arguments are separated by commas even in locales that normally use semicolons.
A static rule hard-codes a value, so each audience needs its own role. A dynamic rule uses the signed-in identity, so one role serves everyone; the usual pattern stores each user’s sign-in name in a table related to the data and filters that table. Each line below is the rule for a different role:
-- Static rule on the Region table (one role per region)
[Region] = "West"
-- Dynamic rule on a user-mapping table (one role for everyone)
[UserEmail] = USERPRINCIPALNAME()
When the rule is on a hidden user-mapping table (for example a salesperson table with one row per user and their region), the filter propagates through relationships to the fact tables. Figure 14.1 traces the filter from the signed-in user to the fact table.
| Function | In Power BI Desktop | In the Power BI service |
|---|---|---|
| USERNAME() | DOMAIN | The user’s UPN |
| USERPRINCIPALNAME() | The UPN, such as user@domain | The user’s UPN |
Because USERNAME() returns a different format in Desktop, USERPRINCIPALNAME() is the more consistent choice. For guests (Microsoft Entra B2B users), the value can appear as an email address or in a tenant-resolved form containing EXT, so the mapping table must store exactly what USERPRINCIPALNAME() returns.
Rules for unexpected values should return no rows rather than all rows. A rule like the following, based on a Microsoft Learn example, returns everything for any value other than the one tested, so a typo exposes all data:
IF(
USERNAME() = "Worker",
[Type] = "Internal",
TRUE()
)
Testing each expected value and ending with FALSE() closes that gap:
IF(
USERNAME() = "Worker",
[Type] = "Internal",
IF( USERNAME() = "Manager", TRUE(), FALSE() )
)
Common trap: Using USERNAME() and testing the mapping table in Desktop with DOMAINvalues - in the service USERNAME() returns the UPN, so the mapping must hold UPNs; USERPRINCIPALNAME() returns the UPN in both places.
Common trap: Adding a user to a restrictive role to “deny” rows they get from another role - RLS roles combine as a union, so membership of a broader role always wins.
Common trap: Relying on an inactive relationship (activated with USERELATIONSHIP) to carry an RLS filter to a fact table - RLS filters never propagate through inactive relationships.
This note is one section of Ultra Transcenders PL-300: Microsoft Power BI Data Analyst, an independent study guide that explains every topic the exam covers by technology, with comparison tables, diagrams and the common traps, plus a glossary linked to Microsoft Learn.
Due on Amazon in December 2026, in Kindle and paperback editions.
About the book · PL-300 terms in the glossary · All PL-300 study notes
How a referenced query differs from a duplicated one, and what each choice means for refresh, maintenance and query dependencies.
Where to change a source's credentials and path, and how None, Private, Organizational and Public privacy levels affect combining data.
How to filter one fact table by the same dimension in several roles, such as order date and ship date, with inactive relationships or copies of the table.
How CALCULATE changes filter context and when to use ALL, REMOVEFILTERS, KEEPFILTERS, USERELATIONSHIP and other modifiers.
How to report stock levels and account balances that sum across categories but not over time, using LASTDATE, LASTNONBLANK and closing-balance functions.
Which way of getting reports to readers fits each audience, licence and security need.
Which sources, storage modes and refresh scenarios need an on-premises data gateway and which connect directly from the cloud.