Consider an illustration built from documented indexer behavior. A project ends, and somebody removes a colleague from the SharePoint document library that held its contracts, which is the correct action in SharePoint. The next day that colleague asks the internal assistant a question and gets back a paragraph from one of those contracts, cited. SharePoint revoked the access; the search index was still applying older permission metadata. The library says one thing and the index says another, because the index holds a copy of the permissions as they stood the last time something told it to look.
Retrieval runs against an index, and an index is a second store with its own copy of who may read what. Getting the retrieval layer right comes first; keeping its permission copy current is the part that is easy to miss when revoking access in the source system looks like the end of it.
What the Index Knows About Who Can Read
The copy is literal. In Azure AI Search the permissions land as ordinary fields on each document: a user field and a group field, both Collection(Edm.String), both filterable, and both left non-retrievable so the identifiers never come back in a result. An ADLS Gen2 source adds a third field for the container role scope. The index carries permissionFilterOption enabled, which is the switch that makes the service consult any of it.
At query time the caller brings a Microsoft Entra token in the x-ms-query-source-authorization header. The service pulls the user, group and scope claims out of that token, compares them against the permission metadata already stored in the index, and returns only the documents whose stored metadata grants that caller access. Two gates sit in that sentence: the application needs Search Index Data Reader (or Contributor) to reach the index at all, and then the token decides which of its documents come back.
The comparison step is where the gap opens. Nothing in the query path calls SharePoint or the storage account to ask what the permissions are today; the service evaluates the permission metadata already stored in the index, which is whatever was written there the last time the indexer looked.
Permission Refresh Is Not Content Refresh
The ADLS Gen2 indexer reference states the boundary. Enabling access control enrichment on an indexer works automatically in two situations and no others: the very first full indexer run, when every permission that exists at that moment is captured, and brand-new documents added after the feature was switched on. Everything else is a decision somebody has to make and then keep making.
Change a permission on a document already in the index and the same page says what happens: the change does not appear in the search index unless you tell the indexer to crawl the permission metadata again. Three mechanisms do that, sized to the change. Touching the Last-Modified timestamp on a blob refreshes that one document on the next run. Calling resetdocs with a list of document keys covers dozens to thousands. Calling resync with the permissions option covers the whole data source, and its asymmetry matters: it refreshes the access metadata and leaves the content untouched, so a permissions-only refresh does not reingest the documents.
The warning under that table is direct: if you change permissions on indexed documents and do not trigger one of those mechanisms, the search index continues serving outdated ACL or RBAC data. The results look normal, and they can include documents the caller has already lost access to in the source.
SharePoint has moved, and the move is the reason to check which API version your indexer actually calls. From the 2026-05-01-preview REST API (the current preview, checked on September 25, 2026, is 2026-08-01-preview), access control changes on items with unique permissions are detected and refreshed on every successful indexer run, using SharePoint change tokens the same way content changes are picked up. That refreshes the item's stored permission metadata on a successful indexer run. The caller loses access only when no remaining grant permits it.
It does not cover the case in the opening illustration. Changes inherited from a parent scope - site, library, list, or folder - are not picked up automatically, and the SharePoint indexer documentation gives that its own table row, with resync using the permissions option or resetdocs on the affected keys named as the remedy. Removing somebody from a library is a parent-scope change, and so is tightening a folder.
The API version matters in the other direction too. The same page says that in preview API versions earlier than 2026-05-01-preview, access control lists are captured only on the first ingestion of each item, and later permission changes require explicit reindexing. An indexer pinned to an older preview version behaves that way even for items with unique permissions.
Each of these mechanisms has to be run by something: a named job that refreshes permission metadata on its own cadence and records a last-success time a support person can read without opening the Azure portal.
Which Enforcement Fits What You Already Run?
The service documents four approaches. As of September 25, 2026, native POSIX-like access control lists and role scopes, Microsoft Purview sensitivity labels, and SharePoint access control lists are all preview features, which Microsoft says are not covered by a service-level agreement and not recommended for production workloads. Security filters are the fourth, and the overview page describes them as API-agnostic, generally available, and based on simple string matching.
A security filter is exactly what its name says. You store a group or user identifier as a string on the document, and at query time you pass the caller identity into an OData filter that drops anything failing to match. The reference states the limit: there is no authentication or authorization through the security principal, which is just a string used in a filter expression. The filter is a retrieval rule the application has to attach to every relevant query, with a caller identity it can trust. On any code path that forgets it, documents the caller is not entitled to see become eligible for the results.
If you build one, build it with search.in from the first commit. The security filter reference is specific about the alternative: a disjunction of equality expressions, where the list contains hundreds or thousands of values, slows down query response time by many seconds, and it says to expect subsecond response times with search.in. The search.in reference adds that latency still grows as the number of values grows, and a function call also counts as a single clause against the filter-size limit. Measure it with the group counts your own users actually carry.
Native ingestion earns its preview API when the content already sits in ADLS Gen2 or SharePoint and the identities are already Microsoft Entra. Filters earn theirs when the identities live in your own database, in a customer portal or an operations system with its own roles, because there is no Entra group to ingest, and creating Entra groups to mirror those roles is a separate identity project.
What Support Checks When a Document Is Missing
Two documented gaps can withhold documents a person is entitled to read. The first is chunking. If a skillset splits documents for vectorization, every chunk must carry the ACL fields, because permission filters apply per document and a chunk is a document. When that skillset runs with projectionMode set to skipIndexingParentDocuments, the indexer field mappings for those fields are bypassed entirely and the values have to be projected onto each chunk. Miss it and the ACL fields on each chunk land empty. The access resolution rules treat an empty userIds or groupIds array as granting nothing through that field only: a user who matches another field, such as a group ID or an RBAC scope, still gets access. So an empty field does not by itself fail closed. Where every grant for those chunks was meant to arrive through the projected fields and none did, the chunks match nobody.
The second is group nesting. A Microsoft Entra group nested inside a SharePoint site group does not expand during resolution, and the documentation states that results depending on that relationship are filtered out. When either gap withholds a document, it reaches support as an assistant that cannot find a document the person can open in their browser, and the answer text does not show why.
The documentation's verification step is to set retrievable to true on the user and group fields, run an elevated-read query, confirm the collections are populated on every chunk, then set retrievable back to false. Microsoft says to do that during development; on a production index it belongs in a restricted diagnostic procedure, because it exposes identifiers the index normally hides. Diagnostic logging into Azure Monitor can turn one complaint into a pattern, but only for queries that ran after it was enabled.
The runbook page support reads carries four fields: which index answered, which API version the indexer calls, when permission metadata last resynced successfully, and whether the document in question carried access fields on every chunk. Its acceptance check is a parent-scope revocation: remove a test account from a library holding a test document, run the permission refresh job, and confirm that a query with that account's token no longer returns the document or any of its chunks.
Related Reading
For what changes when the embedding step moves into the database itself, read SQL Server 2025 as an AI Platform, Not Just a Vector Store.