Power BI

Row-Level Security (RLS) in Power BI

Set up row-level security in Power BI: static roles, dynamic RLS with USERPRINCIPALNAME(), testing with View as, assigning users and common mistakes.

By DashCore Hub Editorial TeamUpdated 7 min read

Row-level security (RLS) in Power BI restricts which rows of data each user can see in the same report. You define roles with DAX filter expressions in Power BI Desktop, test them with "View as", and then assign users or groups to those roles in the Power BI Service. One report can then show each sales rep only their own region, without building separate copies.

What RLS does (and does not do)

RLS works at the semantic model level. When a user opens any report built on that model, Power BI applies the role's filter before calculating anything, so totals, cards and charts only reflect the rows that user is allowed to see. Because the filter lives in the model, it also applies to anyone who connects to the model from Excel or builds a new report on top of it.

Some important limits to understand from the start:

  • RLS only filters for people with the Viewer role in the workspace (or app users and people the model is shared with). Workspace Admins, Members and Contributors can edit the model, so RLS does not restrict what they see.
  • RLS filters rows, not columns or tables. Hiding an entire column such as salary requires object-level security (OLS), which is configured with external tools like Tabular Editor.
  • RLS is not a replacement for source security. If people have direct access to the database, they can still query it there.

Static RLS: one role per group

Static RLS hard-codes the filter value in each role. It is the easiest way to start and works well when you have a small, stable number of groups, such as four sales regions.

Create a static role step by step

  1. In Power BI Desktop, go to Modeling > Manage roles.
  2. Select New and name the role, for example Southeast.
  3. Choose the table to filter, ideally a dimension such as Region, not the fact table.
  4. Add the filter. The default editor lets you pick a column and value; you can switch to the DAX editor to type the expression.
  5. Repeat for each region, then select Save.

The DAX expression for the Southeast role on the Region table is simply:

[Region] = "Southeast"

Because the Region table filters the Sales fact table through a one-to-many relationship, every sales measure automatically respects the role. This is one more reason a clean star schema matters: filters flow from dimensions to facts the way RLS expects.

The downside is maintenance. With 40 stores you would need 40 roles, and every new store means editing and republishing the model. That is where dynamic RLS comes in.

Dynamic RLS with USERPRINCIPALNAME()

Dynamic RLS uses one role for everyone and looks up each user's permissions in a table. The DAX function USERPRINCIPALNAME() returns the signed-in user's sign-in name, which in the Power BI Service is normally their work email address, such as maria@contoso.com.

1. Build a security table

Create a table, for example in Excel, SharePoint or your database, that maps users to what they may see:

EmailRegion
maria@contoso.comSoutheast
james@contoso.comNortheast
james@contoso.comMidwest
lena@contoso.comWest

Load it into the model as UserRegion. A user can appear on several rows to see several regions. Keeping this table in a place the business can update means access changes do not require a developer.

2. Write the role filter

You could relate UserRegion to Region and filter the security table, but that requires bidirectional filtering, which can cause ambiguity and slow performance. A cleaner pattern is to leave UserRegion disconnected and filter the Region dimension directly. Create a role named Dynamic Region with this DAX filter on the Region table:

[Region]
    IN CALCULATETABLE (
        VALUES ( UserRegion[Region] ),
        UserRegion[Email] = USERPRINCIPALNAME ()
    )

For each row of Region, the expression checks whether that region is in the list of regions assigned to the current user. Maria sees only Southeast; James sees Northeast and Midwest.

3. Handle managers who see everything

A common need is letting leadership see all rows. One option is a separate static role whose filter is simply TRUE(), with leaders assigned to it. Another is adding a flag column to the security table:

VAR CanSeeAll =
    LOOKUPVALUE ( Managers[SeeAll], Managers[Email], USERPRINCIPALNAME () )
RETURN
    CanSeeAll = TRUE ()
        || [Region]
            IN CALCULATETABLE (
                VALUES ( UserRegion[Region] ),
                UserRegion[Email] = USERPRINCIPALNAME ()
            )

Here Managers is a small table with one row per email and a true/false SeeAll column. If a user belongs to more than one role, Power BI combines the roles, so they see the union of rows allowed by each.

AI shortcut: Describe your model to ChatGPT or Claude and ask: "My Power BI model has Sales (fact), Store (StoreID, Region, District) and a security table UserAccess (Email, District). Write a dynamic RLS filter for the Store table using USERPRINCIPALNAME() without bidirectional relationships, and explain how to test it." Always test the result with View as before publishing.

Testing RLS before you publish

Never publish RLS without testing it. In Power BI Desktop:

  1. Go to Modeling > View as.
  2. Check the role you want to test.
  3. For dynamic RLS, also check Other user and type an email from your security table, such as maria@contoso.com.
  4. Select OK and review every page. A banner shows which role you are viewing as.
  5. Check totals, not just tables. A card showing company-wide revenue for a regional user means a measure or relationship is bypassing the filter.
Tip: Add a temporary card with a measure such as Who Am I = USERPRINCIPALNAME() while testing. In Desktop it shows the identity being simulated; in the Service it confirms the exact sign-in name Power BI sees, which helps when emails in your security table do not match.

Assigning users in the Power BI Service

Roles are defined in Desktop, but membership is managed in the Service:

  1. Publish the report to a workspace.
  2. In the workspace, open the semantic model's menu (the three dots) and select Security.
  3. Select a role and add people or, better, Microsoft Entra security groups.
  4. Use Test as role in the same screen to confirm behavior with real accounts.
  5. Give business users the Viewer role in the workspace or distribute the report through an app.

For dynamic RLS, add everyone (or a broad group) to the single dynamic role; the security table decides what each person sees. Remember that a user with access to the report but not assigned to any role will see no data at all, which is a safe default. For more on workspaces and apps, see Power BI Service: Publishing, Workspaces and Sharing.

Common RLS mistakes

  • Testing as an Admin. Admins, Members and Contributors bypass RLS, so the report looks unfiltered. Test with a Viewer account or "Test as role".
  • Filtering the fact table directly. Filtering large fact tables row by row is slower and harder to maintain than filtering a dimension.
  • Email mismatches. Aliases, upper and lower case differences or guest accounts can break dynamic RLS. Confirm with the "Who Am I" measure.
  • Forgetting new data sources. If you add a new fact table that is not related to the secured dimension, it will not be filtered.
  • Heavy filter logic. Complex DAX in role filters runs for every query; keep it simple, and review Power BI performance tips if reports slow down after adding RLS.

Next steps

Start with a static role on one dimension to see how RLS behaves, then convert it to dynamic RLS with a security table and USERPRINCIPALNAME(). Test with View as, publish, assign security groups in the Service, and verify with a real Viewer account. If DAX filter context still feels unfamiliar, review The CALCULATE Function in DAX, Explained Simply, since the dynamic pattern relies on it.

Watch: video lessons

Power BI RLS Tutorial: Control Who Sees Your Reports! 👀by Pragmatic Works · Watch on YouTube ↗
¿Cómo implementar RLS estática y dinámica en Power BI?by NamasData · Álex Ayala · Watch on YouTube ↗Video in Spanish.

Frequently asked questions

Does row-level security apply to workspace Admins?

No. RLS only restricts users with the Viewer role, app users and people the model is shared with. Admins, Members and Contributors see all data.

What is the difference between static and dynamic RLS?

Static RLS hard-codes a filter value in each role, such as one role per region. Dynamic RLS uses one role that looks up each user's permissions in a security table with USERPRINCIPALNAME().

What happens if a user is not assigned to any role?

If a model has RLS roles and a Viewer is not a member of any role, they see no data in reports built on that model.

Can RLS hide columns such as salary?

No, RLS only filters rows. To hide tables or columns, use object-level security, which is typically configured with external tools like Tabular Editor.

Learning with the Academy?
Mark this lesson complete to track your rank progress.
Take the quiz