The pipeline report that showed every region
A sales operations lead at a manufacturer called me in on a Friday. A regional manager in Iberia had opened the weekly pipeline report and found opportunities from every country the company sells in, with deal values from teams he had no business seeing. In Dynamics 365 Sales he could see his own region and nothing else.
The dataset had been built by an analyst holding a system administrator role, because that was the account that made the gateway work on the first attempt. The refresh ran under it every night, so every opportunity row in the company landed in the model, and the model went to a workspace forty people could open.
Nothing had failed. The refresh succeeded for eleven months, the numbers reconciled, and finance liked the report. The access model the platform team had spent two years configuring stopped at the edge of the dataset, and nobody noticed, because a report that works does not raise an alert.
The copy is where the rules get lost
Dataverse decides what you can see one record at a time, at the moment you ask. Business units place you in the organisation. Security roles grant privileges at a depth, from your own records up to everything. Ownership puts each record under a user or a team, and sharing hands one person a single record outside all of that. The platform evaluates the combination on every query.
An import into Power BI makes a copy. The refresh reads the tables with the credentials of whoever set it up, and what comes back is whatever that account was allowed to read. The rows arrive stripped of the machinery that decided who should receive them, and the report then shows its contents to anyone who can open it.
Nothing here is broken. The default behaviour of an import is to carry everything the refresh account could read, and that default is wrong for most business application data. The question for any such dataset is whose eyes the copy was taken through, and what was built afterwards to narrow it. If the answer to the second half is nothing, the real permission on that data is the permission on the workspace.
Roles, filters, and the name they key on
Row-level security in a Power BI model runs through roles you define on the model itself. Each role carries a filter expression written against one or more tables, and members see only the rows the filter allows. Those filters apply before anything else in the query, so every visual, every measure and every export inherits them.
What the filters key on is the identity of the person viewing the report, returned as their user principal name. A filter comparing a column of sign-in addresses on a people table against that value is the entire mechanism.
Past the simplest case, the path from that identity to a set of records runs through a mapping table. One row per person per thing they may see, whether a territory, a business unit, an account or a cost centre. The filter narrows the mapping table down to the viewer, and a relationship carries that narrowing through to the fact table.
The real job is the mapping table. Someone has to populate it, keep it current when a rep changes territory, and clear people out on their last day. Build it from the Dataverse security model itself, from team membership or the business unit on the user record, and you inherit the maintenance the platform team already does. Build it from a spreadsheet, and you own a second permission system nobody audits.
Import mode against letting Dataverse answer
Rebuilding is only necessary because the data was imported. A live connection back to the source keeps the evaluation where the rules already live, so a viewer's query reaches Dataverse under their own identity and returns already trimmed. Nothing to rebuild and nothing to maintain, which makes it the right answer whenever it is available.
It is often neither available nor fast. A live query puts every visual on the page in front of the source, and a report with eight visuals and a slicer will feel that. Complex measures push work back to a system built for transactions rather than aggregation. Some connection paths never pass the viewer identity through at all, so the queries run under stored credentials and you are back to the same copy problem with worse response times.
The choice sounds plain in a design review. Import when the report has to be fast and the audience is wide, and accept that you now own a security model. Use a live connection when the audience is small and the query pattern is narrow. Whether a report belongs in Power BI or in Fabric changes the plumbing around this, and it does not change the trade.
Managers, hierarchies, and totals that still talk
A manager who should see their whole reporting line is the requirement naive filters get wrong. A filter matching records whose owner reports directly to the viewer covers one level, so the director two levels up sees nothing from the teams below their direct reports. The usual fix adds a second filter, then a third. The structure is recursive and the filter has to be as well, through a column holding every ancestor of a person or a function that resolves the parent and child chain at query time.
Totals are the failure that survives a correct filter. Hide the rows and the aggregates can still answer questions nobody meant to answer. A viewer who sees three of their own accounts alongside a company-wide target can work backwards to a number they were never meant to hold. A card showing average deal size across a set filtered to one record reveals that record exactly.
Decide for each measure whether it respects the row filter or deliberately ignores it, and write that decision down beside the measure. Most should respect it. The few that must not, such as a company benchmark people are allowed to compare themselves against, belong in a labelled part of the report with a named approver. Agreeing what a report has to answer before it is built is the right moment for that conversation.
Viewing as yourself proves nothing
The only verification that means anything is opening the report while impersonating a specific restricted user. Power BI Desktop and the service both allow a test view for a chosen role or a chosen person, and the difference matters, since testing a role checks your filter while testing a named person also checks the mapping row behind them.
Build the test list from the awkward people rather than the obvious ones. A rep who moved territory last month. A manager with a vacancy in their reporting line. Someone holding a shared record they do not own. A person sitting in two business units after a reorganisation. Each of those has broken a model I have reviewed, and each takes about ninety seconds to check.
Then look at who owns the refresh. The dataset holds exactly what that account could read, so the security of the whole thing rests on one person's role assignment, and that person will leave or change team. Move the refresh onto a service principal with a purpose-built security role scoped to the tables the report needs. A permission audit on any platform opens with the same question about accounts nobody remembers granting.
What to check before the report goes out
Before a report built on business application data reaches an audience, someone other than the person who built it should walk this list. It takes twenty minutes, and it has turned up something every time I have run it against a report that was already live.
Keep the answers with the report, in the workspace description or wherever the model is documented, so the next person to change it reads them before publishing. The list stays short on purpose, and a working understanding of what Dataverse actually is sits behind most of it.
- Whose credentials run the refresh, and whether that account belongs to a named individual.
- Which roles are defined on the model, and who is assigned to each one in the service.
- Where the mapping of people to records comes from, and what updates it when someone changes team.
- What a viewer with no mapping row sees, which should be nothing rather than everything.
- Which measures and totals ignore the row filter, and who agreed to that.
- The result of a test view for at least three named restricted users, one of whom should see no rows.
- Who gets told when the report's audience or the underlying security model changes.


