Object level security when the row is fine but the column is not
Object level security removes a table or column from the model for readers in a given role, including its name and metadata, so the field behaves as though it never existed. Row filters cannot do this, because a reader who can see a row can see every column on it.
The operations team asked for the exhibitor list. All nine shows, every stand, every company, because they were rebuilding the hall allocation process and needed to see who had been where for five years. Perfectly reasonable request, and the data team said yes in about four seconds.
Then somebody noticed that the exhibitor table carried contracted value, and that thirty operations people were about to be able to sort nine shows of booth pricing descending. Object level security exists for exactly this shape of problem, where the row is legitimate and one column on it is not.
What row filters cannot do
The instinct is to reach for the filter you already have. It does not help, and the documentation says so in as many words.
Microsoft's row-level security documentation (Microsoft Learn, 2026) puts the question and answer directly in its FAQ: "Can I use RLS to limit the columns or measures accessible by my users? Answer: No, if a user has access to a particular row of data, they can see all the columns of data for that row."
That is a property of how filtering works rather than a gap somebody forgot to fill. A row filter evaluates true or false per row and the rows that survive arrive whole. There is no expression you can write in that language that says "this row, minus one field".
So the choices narrow to three. Give the operations team a model without the column. Give them the column and rely on policy. Or remove the object from the model for them specifically, which is the mechanism this post is about.
How does object level security differ from hiding a field?
Hiding a field in the model is a convenience for report authors. It takes the field out of the default field list and leaves it fully queryable by anybody who knows its name, which in practice means anybody who has ever seen the model in a tool that shows hidden objects.
Object level security is enforcement. The documentation (Microsoft Learn, 2026) describes it as enabling "model authors to secure specific tables or columns from report viewers" and adds that "you can also restrict object names and metadata". The consequence it draws is the one that matters: "For viewers that don't have the required permission, it's as if the secured tables or columns don't exist."
That last property is worth dwelling on for a commercial dataset. A reader who can see a greyed-out field called contracted_value has learned that contracted value exists in the model, which is itself information. A reader for whom the object was removed has learned nothing.
The rule is set inside a model role, either through a script in the model definition language or through an external modelling tool, and the permission on the object takes one of two values. None means the object is hidden from that role and the security is enforced. Read means the object is visible. There is no partial state, no masking, no rounding.
Hiding contracted value from thirty operations users
Work the case. One semantic model across nine shows. A contract fact table with a company key, a show key, a stand number, net square metres, contracted value and discount percentage. Two roles: commercial, which reads everything, and operations, which reads the table with contracted value and discount percentage set to none.
Thirty operations users go into the operations role. They open the exhibitor universe and see companies, stands, shows and square metres across five years of history, which is what they asked for. The two money columns are absent, along with their names.
The commercial role keeps both columns and holds eleven people. One model, one refresh, one set of measures for everything both audiences share, and the difference between what the two groups can reach is two lines in a role definition.
Compare that with the alternatives. A second model without the money columns means two models, two refresh schedules, two places to land a definition change, and the near certainty that the two drift. A policy that says operations should not look at pricing means a control that exists only in an induction slide.
What breaks when a measure references a secured column?
Here is the part that turns a five minute change into a half day, and it catches almost everybody the first time.
Securing a column does not gracefully blank it. The documentation states that users without the required permission "receive a message that the field can't be found for all report visuals using that field". To an operations user that reads as a broken report, and the first three will raise tickets before anybody works out what happened.
The blast radius is wider than the visuals that show the column directly, because measures reference columns too. Suppose the model holds 34 measures and nine of them touch contracted value somewhere in their expression, including revenue per square metre and average discount. All nine become unavailable to the operations role, and the effect reaches well beyond the visuals showing the raw column. On a report page with 14 visuals, three of which use those measures, the operations user sees eleven working visuals and three error messages.
The workable answer is a separate report for the restricted audience, built only from objects that role can read, and published to that audience through its own app audience. That costs one more report and it removes the class of ticket entirely. Attempting to build one page that degrades politely for two roles is a fight against the mechanism, because the mechanism was designed to make the object undiscoverable and an elegant fallback would leak its existence.
Test the outcome deliberately rather than by opening the report yourself. The same discipline that proves a row filter returns the right rows applies here, and it belongs with the test matrix: open every report page as a member of the restricted role and record which visuals render.
Where the rule lives and how it ships
The definition sits in the semantic model, inside a role, alongside any row filters that role carries. It does not sit in the report, which is the property that makes it worth using: a report author cannot remove it, a copied report inherits it, and a new report built by somebody who has never heard of the rule is covered from the first visual.
Authoring happens either through a script written in the model definition language directly in the authoring environment, or through an external modelling tool. The script form is short enough to read in a code review, which is the argument for preferring it. A role, a model permission of read, a table permission, and the object permission set to none is four lines for the whole rule, and four lines is something a second person can check.
Because the rule travels with the model, treat it as part of the model's release rather than as configuration applied afterwards. Two habits help. Keep the role script in source control next to the rest of the model definition, so the current state of the rule is readable without opening a modelling tool. And verify the role list after any deployment that did not originate from the file holding the roles, because a model replaced from a source that never had the role will not have it, and nothing about that failure is visible in the report.
Who the rules apply to
The same trap that catches row filtering catches this. Object level security applies to viewers in a workspace, and the documentation is explicit that workspace members assigned admin, member or contributor roles have edit permission on the model and are therefore exempt.
Thirty operations users all added as contributors so they could refresh a dataflow will see contracted value regardless of the role definition. Getting the membership right first is a prerequisite rather than a refinement, and the practical version of that work sits in row level security for event portfolios, where the same exemption undermines the same effort.
A handful of features stop working on models carrying these rules. Quick insights visualisations, smart narrative visuals and the Excel data types gallery are all listed as unsupported. None of those is load-bearing for an operations report, and it is better to know before somebody builds a page around one.
Where this stops
Object level security removes a field. It does not obscure a quantity, and for an event portfolio the difference bites in a specific way.
An operations planner who cannot see contracted value can often infer a lot of it. Stand size, position, whether the company took a corner, and how many editions they have renewed for are all visible and all correlate with price. If the concern is a precise figure landing in the wrong conversation, removal works. If the concern is a rough sense of who pays most, removal achieves less than it appears to.
There is a middle option worth considering when the restricted audience genuinely needs relative magnitude. Publish a banded column instead of the raw one, computed upstream, so operations sees a size band while the exact figure stays behind the role. That is a modelling decision rather than a security feature, and it has the advantage of giving the restricted audience something usable instead of nothing.
The second limit is classification. Removing a column for one audience says nothing about what happens to the file when the other audience exports it, which is a labelling question and a separate control.
Start this week by opening your largest model and listing the columns that would embarrass you in an exhibitor's hands. For each one, count how many measures reference it. That count is the real cost of securing the object, it takes twenty minutes to produce, and on most portfolio models it is the number that decides whether you secure the column or split the report.
Questions people ask about object level security
- What is object level security in a semantic model?
- It is a rule attached to a model role that sets a table or column to be unreadable for members of that role. The object disappears from the field list along with its name and metadata, so a reader has no way to discover that it exists. It is defined in the model rather than in the report.
- Can row level security hide a column?
- No. Row filters decide which rows a reader receives, and a reader who receives a row receives every column on it. Hiding a field from some readers while showing it to others is a different mechanism that operates on the object itself rather than on the rows.
- What happens to a report that uses a secured column?
- Every visual referencing that field returns a message saying the field cannot be found, which reads to the user as a broken report. Measures whose expressions touch the secured column fail in the same way. Plan a separate report or page for the restricted audience instead of expecting one page to degrade gracefully.
Related reading
- Row level security for event portfolios where a director sees only their show
- Testing row level security before one exhibitor can see another exhibitor data
- Sensitivity labels on event reports so exports carry their classification out