Static roles are simple and do not scale
A static role hard-codes a filter, for example region equals North. It works, but you need one role per region and manual membership management. Fine for five regions, unmanageable for fifty branches.
One published report that shows each person only their own rows — and an entitlement model your examiner can inspect.
This is what stops branch-level performance packs circulating as email attachments that immediately diverge. One report, correct for every reader, with the entitlement rules held in one place rather than in a distribution list.
Role definition, DAX filter expressions, the dynamic pattern using USERPRINCIPALNAME, entitlement tables sourced from your directory, and testing with View As before publishing.
Institutions produce a performance pack, filter it per region, and email the copies. The copies diverge immediately, nobody is sure which version is being quoted, and there is no single place where entitlement is defined.
The technical fix has existed for years. What stops it is that nobody has been given the authority to decide who is entitled to see what, so the report designer defaults to sending everyone everything and filtering by hand.
Check these before beginning. Most stalled implementations stall on one of them.
Power BI Pro at minimum. Fabric capacity (F64 or equivalent) if you intend to use Copilot against the model.
Workspace member or admin to configure roles; someone with authority to approve who sees what.
A reliable person-to-entity mapping. Directory data or an HR feed is far better than a hand-maintained list.
Written agreement on the entitlement rules before you build. This is a business decision, not a technical one.
Configuration is straightforward once these are clear. Skipping them is why most first attempts produce something that works and cannot be maintained.
A static role hard-codes a filter, for example region equals North. It works, but you need one role per region and manual membership management. Fine for five regions, unmanageable for fifty branches.
USERPRINCIPALNAME() returns the identity of the person viewing the report. Joined against an entitlement table, one role covers every user. Joiners and leavers are handled by the source of that table rather than by editing the model.
If a column itself is sensitive regardless of row, that is object-level security, which is a separate mechanism. Conflating the two is a common design error that surfaces at the worst moment.
Settings shown are the ones that matter, not every field on the form. Values are starting points to validate against your own environment.
One row per person per entity they may see. Source it from your directory or HR system rather than maintaining it manually — a hand-kept list is a leaver risk with a reporting interface.
Include the hierarchy explicitly. A regional director who should see six branches gets six rows, not a rule that walks a tree at query time.
In Power BI Desktop, Modeling then Manage Roles. Apply the filter to the entitlement table and let the relationship propagate it to the fact tables.
Filtering the entitlement table rather than the fact table directly is the pattern that performs well and stays readable.
Two workable approaches. Either add rows to the entitlement table covering every entity for those individuals, or add a boolean flag and extend the DAX to bypass the filter when it is set.
The first is more transparent and easier to audit. The second is tidier and harder to review. In a regulated institution we recommend the first.
Power BI Desktop lets you view the report as a specific user. Test at least one account per role type — a branch manager, a regional director, an executive, and someone who should see nothing.
Then test again in the service after publishing, because workspace membership can override what you expect.
Roles are defined in Desktop and populated in the service, under the semantic model's security settings. Assign a security group rather than individuals so membership is managed where identity is managed.
Note the exception that catches people out: workspace admins, members and contributors bypass row-level security entirely. Report consumers should be given Viewer access only.
Every one of these is avoidable, and every one of them is common enough that we check for it by default.
This is the one that produces incidents. A Contributor sees all rows regardless of role membership. Audit workspace access at the same time you configure row-level security, not afterwards.
A bidirectional relationship elsewhere in the model can propagate around your security filter. Keep cross-filter direction single wherever row-level security is in play, and test the specific paths.
A spreadsheet maintained by one person is a leaver risk. When they are on holiday, or when they leave, access reviews stop happening and nobody notices.
Row-level security filters what a report shows. It does not stop someone with dataset access from querying it another way. Model permissions matter as much as the role definition.
Pick one report that currently circulates as filtered exports. We will rebuild it with row-level security against your structure and walk your risk team through how the entitlement would be evidenced.
Describe the situation in your own words.