In the last post I walked through auditing a semantic model before connecting it to an AI tool like Fabric Data Agent. Descriptions, naming, explicit measures, star schema: the things that decide whether a Fabric data agent generates an accurate query or a confident wrong one. I left one thing out on purpose, because it deserved its own post and because I got it wrong the first time I thought about it.
Security.
Here is the setup. I inherited a model. It had row-level security on it. Sales people saw their own region, the finance role saw everything, and the reports had been running like that for two years without a complaint. So when the request came in to connect a data agent to the workspace, I ticked the security box in my head and moved on. RLS was handled.
It was handled for people browsing reports. That is not the same thing as handled for an agent answering questions over the model, and the gap between those two sentences is where the trouble lives.
What I got wrong first
My first assumption was the dramatic one: the agent runs as some service identity, ignores the report layer, and reads everything. That is wrong, and the reason it is wrong is the useful part.
A Fabric data agent honors the security on the underlying data. Microsoft is explicit: the agent honors all user permissions to the data, including row-level security and column-level security, and for a Power BI semantic model the querying user only needs Read permission on the model, not workspace access, as documented in Fabric data agent sharing and permission management. The data agent concepts page repeats it in the limitations section: access through a data agent is governed by Read permission, and RLS and CLS still apply. So the agent is not a backdoor. It queries as the person asking the question, and that person’s RLS role filters the rows.
Good. So where is the problem? The problem is everything that decides what “the person’s RLS role” actually evaluates to. RLS is only as strong as the identity model around it, and that is where I had two real holes.
Hole one: RLS only binds Viewers
This is the one that catches people, and it caught me.
Row-level security only restricts users with the Viewer role in the workspace. It does not apply to Admins, Members, or Contributors. Those three roles carry edit permission on the semantic model, and a user who can edit the model can read all of it. Microsoft says it plainly in Row-level security (RLS) with Power BI: if you want RLS to apply to someone in a workspace, the only role you can give them is Viewer. Even a Viewer with Build permission still gets filtered. A Contributor does not.
Now connect that to the agent. The agent filters by the asking user’s identity. If the people allowed to ask the agent questions sit in the workspace as Contributors, because that is how the workspace was set up back when it was three developers and no governance, then RLS evaluates to “see everything” for every one of them. The agent is behaving correctly. It is honoring RLS. RLS just happens to be off for those identities.
The same rule applies to object-level security. OLS only binds Viewers too, as the object-level security documentation states in its limitations. The Admin, Member, and Contributor roles see the secured tables and columns regardless.
So the report side and the model side diverge exactly here. A Contributor browsing a report might still look safe, because the report only contains the visuals someone built. Point that same Contributor’s identity at the model through an agent that can write arbitrary DAX, and there is no report layer left to hide behind.
Hole two: service principals sit outside RLS entirely
The second hole is worse, because it fails silently in both directions.
Service principals cannot be added to an RLS role. The consequence, in Microsoft’s own words, is that RLS is not applied for apps using a service principal as the final effective identity. That is point three in the RLS considerations and limitations. The XMLA endpoint documentation says the same thing from the other side: service principals do not work with RLS and OLS and cannot be added as model role members.
This matters the moment any part of the chain runs as a service principal rather than a signed-in user. Automation, an embedded app, a fixed-identity Direct Lake model, a pipeline that calls the model. If the effective identity is a service principal, static RLS is simply not applied, and dynamic RLS is worse than not applied: USERPRINCIPALNAME() and USERNAME() return the application ID or an empty string for a service principal, so a filter written as [Region] = USERPRINCIPALNAME() matches nothing, or matches the wrong thing, depending on how the model was built. You either leak everything or you hand back silently incorrect slices, and neither failure announces itself.
So before connecting anything, I now need to know exactly what identity the thing on the other end runs as. “It uses RLS” is not an answer. “It queries as the signed-in user, who is a Viewer, whose role filters to their region” is an answer.
Hiding a column is not securing it
This one is subtle and I want to be careful with it.
When you set a column to hidden in the model, you remove it from the field list. You have not secured it. A hidden column is still queryable. Microsoft makes the equivalent point about perspectives, which are also just a curated view: a user can still query a table or column even when it is not visible to them, so a perspective is a convenience, not a security feature. That note lives in the report consumer security planning guidance. The hidden flag is the same kind of convenience. The agent generating DAX is not limited to your field list. It reads the model metadata, and hidden objects are still part of the model.
The actual controls are object-level security and column-level security. OLS removes the table or column for a role so completely that, to that role, it does not exist, and unlike RLS it also hides the object name and metadata, so the field cannot even be discovered. Column-level security does the same at the column grain by setting the metadata permission to none, as shown in the Analysis Services object-level security reference. You cannot author OLS in Power BI Desktop today. You set it in Tabular Editor or another external tool by setting the table or column permission to None for the role. And, to close the loop, OLS binds Viewers only, same as RLS.
So if there is a salary column, or a cost column, or anything that should not reach the agent, hiding it does nothing. Securing it with OLS or CLS does, for Viewer identities, and for no one else.
Report-level safe is not model-level safe
If there is one sentence to take from this, it is that one. Reports hide visuals. The model still answers DAX. An agent talks to the model, not to the report, so every assumption that lived in the report layer needs to be re-checked at the model layer before the agent goes live.
The check I run now
Before I connect a semantic model to a data agent, I work through this. It is short on purpose.
- List every RLS role and write down, in one line each, exactly what it filters. If you cannot describe a role’s filter in a sentence, you do not yet understand what the agent will return for its members.
- List the members of each role, and then list everyone with Admin, Member, or Contributor on the workspace. That second list is the bypass list. For those identities RLS and OLS do not apply at all. Decide whether every name on it should genuinely see all rows.
- Identify the agent’s effective identity. If it is a service principal or a fixed identity, RLS is not being applied, and you cannot fix that by adding the principal to a role, because you cannot add it to a role. Reach for a different pattern, such as a model whose fixed identity is itself scoped to safe data, or per-user identity passthrough.
- Check sensitive columns for OLS or CLS, not for the hidden flag. The hidden flag is decoration. Confirm the salary-shaped columns are actually secured, and remember that the security only binds Viewers.
- Test as the agent’s identity, not as yourself. The Test as role feature evaluates dynamic RLS using your own UPN, so it cannot show you what a service principal or a guest would see. Where it matters, sign in as the real identity, or run the same DAX the agent would run, as that identity, and look at the rows that come back.
- Default everyone who only needs to ask questions to Read on the model, nothing more. The agent needs Read, not Build, not a workspace role. Least privilege here is not a nicety. It is the only configuration where RLS actually does its job.
Where I am still unsure
I have not fully mapped how a Fabric data agent resolves identity across every sharing mode it offers. The sharing documentation is clear that the agent honors RLS and CLS and that consumers need Read on the model, and that lines up with the per-user story. What I want to test next is the boundary cases: an agent shared with a consumer who has no workspace role at all, a model on a fixed identity, and an agent invoked from automation rather than a chat window. I expect the identity that reaches the model to differ across those, and the difference is exactly what decides whether RLS holds. When I have run it, that is the next post.
Until then, the rule I am keeping is the unglamorous one. Before the agent, list the roles, list who bypasses them, find out what identity actually hits the model, and test as that identity. Not as me.
Sources
- Row-level security (RLS) with Power BI: RLS only restricts Viewers, service principals can’t be added to a role, and the dynamic RLS and Test as role limitations
- Roles in workspaces in Power BI: what Admin, Member, Contributor, and Viewer can each do
- Object-level security (OLS): securing tables and columns, and the Viewer-only limitation
- Object-level security in Analysis Services, including column-level security: CLS and the rule that RLS and OLS can’t be combined from different roles
- Fabric data agent sharing and permission management: the agent honors RLS and CLS, and Read permission is sufficient to query
- Fabric data agent concepts: RLS and CLS still apply when querying a semantic model through an agent
- Semantic model connectivity with the XMLA endpoint: service principals don’t work with RLS or OLS, and Contributor and above have write access
- Security in Microsoft Fabric: how RLS, CLS, and OLS limit Viewer access
- Power BI implementation planning: report consumer security: perspectives are a convenience, not a security boundary




