Skip to content
⚠️ Before You Start
Risk: High
Duration: 30-90 minutes hands-on, plus indexer run time
Last Validated: Not yet validated (draft)
Kind: Runbook
Risk Note: High (delay or partial update keeps restricted content visible)

Apply an urgent permission change or revocation

Draft: not validated

Not yet dry-run. Do not use on a live system until the validation record is complete and signed off. Outline checked on 4 and 5 Nov 2026 (planned 2026-11-06). Author: Kranthi · dry-run: Kandiah Chockalingam [Pending] · sign-off: Yogeshwaran. Work items: RAG-04-T09, RAG-04-T10, RAG-04-T11, RAG-04-T12.

1. Summary

Use this runbook to apply an urgent source permission change to every affected Azure AI Search chunk, verify the revoked persona cannot retrieve it, and confirm a later indexer run does not restore access. Use the stable push-update path for custom security-filter fields; use preview Resync Indexers or Reset Documents, checked with a preview elevated-read count, only for a supported source with an approved fallback. Facts checked against official documentation on 4, 5 and 11 November 2026.

Risk: High (delay or partial update keeps restricted content visible). Typical duration: 30-90 minutes hands-on, plus indexer run time.

2. When to use and when not to

Use this runbook when:

  • A source permission change must reach the index urgently, including a leaver, legal hold, or accidental sharing.
  • You must remove indexed chunks for an approved content removal and the source and retention owners have confirmed the action.

Do not use it when:

  • You have not chosen the access-control pattern. Use PB-RAG-04-01 first.
  • You need to build or change the shared Entra ID security-filter retrieval path. Use PB-RAG-04-02.
  • You suspect or have confirmed a production permission leak. Start PB-RAG-04-04 for containment and evidence; run this playbook for the permission correction.
  • You must prove that a deletion or permission change reached every place (the source, the index and the application caches). Finish this runbook first, then use PB-RAG-09-05.
  • You need to change the index schema or rebuild the index. Use PB-RAG-03-01 or PB-RAG-09-01.

3. What you must know first

  1. A security filter is not authentication. Azure AI Search only compares text: the user and group IDs in the filter against the IDs on each chunk. The app must sign the user in and add the filter to every search. Every chunk must carry its permission fields. In this pack those fields are filterable but not retrievable (PB-RAG-04-02): search can filter on them, but never returns them. So take the current permissions from the source system, not from the index.
  2. A merge replaces the whole value of each field you send. For a revocation, send the complete, current group_ids and user_ids lists from the source, for every chunk. Never send only the removed ID.
  3. One file becomes many chunks. Each chunk has its own copy of the permissions. Find every chunk key before you change anything. Check how the index links chunks to their file (the index projection and parent key). Never guess the number of chunks from a sample.
  4. An indexer run is not always enough. An indexer run picks up only files whose last-modified time changed.
    • Azure Data Lake Storage (ADLS) Gen2: changing a file's ACL does not change its last-modified time, so the next run skips it.
    • SharePoint in Microsoft 365 (preview ACL ingestion): a change on one item with its own (unique) permissions is picked up on the next successful run, from REST API 2026-05-01-preview. A change on a parent (site, library, list or folder) is not.
    • In both cases, use preview Resync Indexers (permissions) or Reset Documents, then run the indexer.
  5. Resync Indexers is preview. The permissions option updates the permission fields only. Microsoft says preview features are not recommended for production. Use it only if the owner has approved the risk and a tested fallback exists.
  6. The next indexer run can overwrite a manual patch. Change the source first. Then check that the next indexer run keeps the new permissions.
  7. Get every chunk, not only the first page. Search returns results in pages, and skip cannot exceed 100,000. Use the script in Step 5. Never patch part of the list. The pack's chunk_id is not sortable, so you cannot page by key range instead.

4. Prerequisites and access

Item Detail
Search service Role-based access is turned on for the search service. By default a service accepts API keys only, and refuses requests that use roles until this is turned on.
Roles To update documents directly: Microsoft Entra ID with Search Index Data Contributor on the target index only. You can give it for one index with Azure CLI, PowerShell or REST, but not in the Azure portal. To resync, reset and run indexers: Search Service Contributor. This role can also read the service keys. For the native-path count (Step 16), the elevated-read query also needs Search Index Data Contributor, or a custom role with Microsoft.Search/searchServices/indexes/contentSecurity/elevatedOperations/read. Prefer an approved least-privilege custom role. Do not give broad access just to save time.
Tools An HTTP client for the Azure AI Search REST requests, Python 3 for the script in Step 5, and an approved place to save the chunk-key list and the source permission evidence.
Index fields Check the key field, the filterable parent field and the exact permission field names in the index definition. The examples use chunk_id, parent_id, group_ids and user_ids. Use the names your index really has.
Identity and test Get an approved Microsoft Entra token for https://search.azure.com/.default. Prepare two test personas (test accounts, never real users): one that should lose access and one that should keep it. Never put tokens in a file, ticket or chat.
Approval Record in the ticket: the source owner, the security approver, the rollback owner, the affected parent IDs and the expected permissions. Get a separate approval before you delete indexed content.
Source state Change and check the source permission before you update the index. Record the evidence and the UTC time.
Preview path Before using preview Resync Indexers, Reset Documents or the elevated-read query, confirm that the selected source and indexer support it and that the production fallback is approved.

Set these variables in the secured HTTP client: SEARCH_ENDPOINT, INDEX_NAME, ACCESS_TOKEN, PARENT_ID, PARENT_FIELD, KEY_FIELD, GROUP_FIELD, USER_FIELD, REVOKED_GROUP_ID, CHUNK_KEY, RETAINED_GROUP_ID, NOW_UTC, PAGE_SIZE, and SKIP. For the resync and reset paths, also set INDEXER_NAME and DOCUMENT_KEY. The script in Step 5 reads SEARCH_ENDPOINT, INDEX_NAME and ACCESS_TOKEN from environment variables. Use the field names your index really has. PAGE_SIZE can be at most 1,000 and SKIP at most 100,000 (see Sources).

5. Decide your approach

Change you need Path Notes
Revoke or amend identities in a custom security-filter index Stable document merge action Patch the complete permission arrays on every affected chunk after the source change.
A user left or was removed from a group, and the file's permissions did not change No index change for group-based permissions Remove the user from the groups, or disable the account, in Entra ID (Step 3). Clear the app's cached group memberships (Step 18), then run the persona test (Step 20). If the user is listed by name in user_ids, also patch as in Step 7. Do not remove the group from the file: everyone else in it would lose access.
A SharePoint item with its own (unique) permissions changed Run the indexer Picked up on the next successful run, from REST API 2026-05-01-preview. Earlier preview versions capture permissions only on the first ingestion.
Refresh native ACL/permission fields after a parent change (SharePoint site, library, list or folder) or an ADLS Gen2 ACL change Preview Resync Indexers Use options: ["permissions"], then run the indexer as documented. It checks every document but updates only the permission fields. Require explicit approval and a working fallback.
Refresh content and permissions for a few named documents Preview Reset Documents Send the document keys to resetdocs, then run the indexer. Only those documents are processed. Require explicit approval and a working fallback.
Remove the indexed content entirely Document delete action This is not a permission update. It removes the chunks from the index only, not the source file, and the index has no undo. Use only after source, retention, and security owners approve. Remove or exclude the source item too, or a later ingestion can add it again.
Investigate an active leak Incident containment Start PB-RAG-04-04. Do not rely on an index update alone to contain a live application leak.

With native permissions, Azure AI Search looks up the user's group membership through Microsoft Graph at query time. With custom security filters, the app looks it up and passes it in the filter, so a cached copy in the app can keep old groups.

6. Steps

Pick your path before you start (see Section 5). Follow only the steps for your path.

Your situation Steps
A user left or changed groups, and the file's permissions did not change 1 to 4, then 18 to 20, 22 and 23
Custom security-filter index (group_ids and user_ids on each chunk) 1 to 7, then 18 to 23
Native permissions, resync (preview) 1 to 5, 8 to 12, 16, then 18 to 23
Native permissions, Reset Documents (preview) 1 to 5, 13 to 16, then 18 to 23
Approved content removal 1 to 5, 17, then 18 to 23

Use both native paths only when the source's own procedure needs both. If you must fix the permissions now and remove the content later, finish the permission path first.

The ${PLACEHOLDERS} stand for your own values. Use the field names your index really has. Never send a partial chunk list.

Stage 1: Contain and prepare

Step 1. Open or update the incident or change ticket. Record the affected source items, the index, the parent IDs, the permissions you want, the source owner, the test personas, the approvers and the UTC start time. Verify: the ticket names the exact affected items, and who approves a restore or a deletion.

Containment can interrupt service

If a user may be able to see restricted content now, turn off the affected retrieval path, or switch it to the approved fail-closed mode, while you fix the permissions. Authorised users also lose access while it is off. Turn it back on only after Step 20 passes and the security approver signs off; see Section 8.

Step 2. Contain a suspected exposure. Follow the containment and evidence steps in PB-RAG-04-04. Verify: the affected app path returns no restricted content, and the ticket records the containment time and where the evidence is.

A source permission change can cut off access

The change is meant to remove access, but a wrong change in the source can also block people who should keep it. Have the source owner confirm the exact change first. If the result is wrong, correct the source and use the recovery steps in Section 8.

Step 3. Change the permission in the source system. Use the source system's approved procedure. For a leaver, remove the user from the groups, or disable the account, in Entra ID. Have the source owner confirm the result. Verify: the source shows the intended users and groups, and no longer gives access to the revoked persona.

Step 4. Confirm the index and your path. Pick your path from the table at the start of this section. Check the real key, parent and permission field names, and the indexer settings for your source. Verify: the ticket records the path, the exact field names and, for resync or reset, the Microsoft page for your source.

Stage 2: Find and preserve the indexed documents

Step 5. Find and save the key of every affected chunk. First set SEARCH_ENDPOINT, INDEX_NAME and ACCESS_TOKEN as environment variables, using the approved secret-handling method. Then run python [snippets/find_chunks.py](snippets/find_chunks.py) --parent-id "${PARENT_ID}" from this pack's folder. The script uses the fields parent_id and chunk_id. If the index uses other names, add --parent-field and --key-field. It asks only for the key and parent fields, because the permission fields are not retrievable. The script sends the request below once per page, until it has every key.

POST ${SEARCH_ENDPOINT}/indexes('${INDEX_NAME}')/docs/search.post.search?api-version=2026-04-01
Authorization: Bearer ${ACCESS_TOKEN}
Content-Type: application/json

{"search":"*","filter":"${PARENT_FIELD} eq '${PARENT_ID}'","select":"${KEY_FIELD},${PARENT_FIELD}","count":true,"top":${PAGE_SIZE},"skip":${SKIP}}

Run the script while the index is stable. Paging is not a snapshot. If documents are added, changed or deleted during the run, a chunk can be read twice or missed, and the total count is only approximate. So the script stops with an error if it reads a key twice, if the total count changes between pages, if the number of keys does not match the total count, or if it reaches the skip limit. Save its JSON output as access-controlled incident evidence.

Verify: the script ends without an error, and count equals expected_count in its output. If it stops with an error, wait until indexing stops and run it again. Do not patch until it passes. If it returns no chunks when you expected some, stop and check the parent ID and field names (Step 4). If it stops at the skip limit, do not patch: the file has more chunks than the script can list. Keep the retrieval path contained and tell the RAG Lead.

Step 6. Prepare the complete new permission lists. Take them from the source's effective permissions, including everyone who should keep access. Write them as Microsoft Entra object IDs in lower case, the same way PB-RAG-04-02 (Step 2) writes them. Never use display names: a name never matches the filter, so the people who should keep access would lose it. Record the old permissions from the source in the evidence, for rollback. Verify: the source owner confirms the new group_ids and user_ids values, and the security approver confirms the revoked persona is not in them.

Stage 3: Update permissions

Do only the steps for the path you chose in Step 4 (see the table at the start of this section).

A partial or wrong permission update can expose restricted content

Do not merge until the source is correct and you have every chunk key (Step 5). A wrong list can leave access in place, or remove access from people who should keep it. Restore only permissions the source owner confirms; see Section 8.

Step 7. Patch the permissions on every chunk (custom security-filter index). The request is also in snippets/merge-permissions.http; change its field names to match your index. Send one merge action for each key from Step 5, with the full lists from Step 6, not only the change. Set acl_synced_at to the current UTC time, so the alert in Section 7 sees the update.

POST ${SEARCH_ENDPOINT}/indexes('${INDEX_NAME}')/docs/search.index?api-version=2026-04-01
Authorization: Bearer ${ACCESS_TOKEN}
Content-Type: application/json

{"value":[{"@search.action":"merge","${KEY_FIELD}":"${CHUNK_KEY}","${GROUP_FIELD}":["${RETAINED_GROUP_ID}"],"${USER_FIELD}":[],"acl_synced_at":"${NOW_UTC}"}]}

Put one action per key in the value list. If the list is long, split it into several requests; check the current batch limits in the docs.

Verify: every item in the response has "status": true. Then count the chunks of this document that still let the revoked group in:

POST ${SEARCH_ENDPOINT}/indexes('${INDEX_NAME}')/docs/search.post.search?api-version=2026-04-01
Authorization: Bearer ${ACCESS_TOKEN}
Content-Type: application/json

{"search":"*","filter":"${PARENT_FIELD} eq '${PARENT_ID}' and ${GROUP_FIELD}/any(g: g eq '${REVOKED_GROUP_ID}')","select":"${KEY_FIELD}","count":true}

After all batches finish and the index is stable, @odata.count is 0. When a user was revoked directly, repeat the count with ${USER_FIELD} and the user's ID.

Preview permission resync has no production guarantee

Resync Indexers is preview, and Microsoft does not recommend preview features for production. Use it only when the source supports it, the owner approves the risk, and the stable patch or another fallback has been tested. See Section 8 if the resync gives wrong values.

Step 8. Ask the indexer to re-read permissions (preview resync). The permissions option updates the permission fields only. The same request is in snippets/resync-permissions.http.

POST ${SEARCH_ENDPOINT}/indexers('${INDEXER_NAME}')/search.resync?api-version=2026-08-01-preview
Authorization: Bearer ${ACCESS_TOKEN}
Content-Type: application/json

{"options":["permissions"]}

Verify: the response is 204 No Content. Do not start Step 9 before you get it.

Step 9. Start the first indexer run. Note the UTC time, then send:

POST ${SEARCH_ENDPOINT}/indexers('${INDEXER_NAME}')/search.run?api-version=2026-04-01
Authorization: Bearer ${ACCESS_TOKEN}

Verify: the response is 202 Accepted.

Step 10. Wait for the first run to finish. An indexer runs one job at a time. A second run fails while the first is still going. Check the status:

GET ${SEARCH_ENDPOINT}/indexers('${INDEXER_NAME}')/search.status?api-version=2026-04-01
Authorization: Bearer ${ACCESS_TOKEN}

Repeat while lastResult.status is inProgress. If it is transientFailure, stop and see Section 9.

Verify: lastResult.status is success, lastResult.itemsFailed is 0, and lastResult.startTime is later than the time you noted in Step 9. The time check makes sure you read this run, not an earlier one.

Step 11. Start the second indexer run. Note the UTC time, then send the run request from Step 9 again. Verify: the response is 202 Accepted.

Step 12. Wait for the second run to finish. Check the status as in Step 10. Verify: lastResult.status is success, lastResult.itemsFailed is 0, and lastResult.startTime is later than the time you noted in Step 11. Then go to Step 16.

Reset Documents is preview and re-reads the source documents

Reset Documents can change the indexed content and permissions of the documents you name. Use it only when the source needs it, the owner has approved the risk, and a tested fallback exists. If the result is wrong, correct the source and use the stable patch or the recovery steps in Section 8.

Step 13. Ask the indexer to re-read only the named documents (preview Reset Documents). Use the document keys from the index, not IDs guessed from the source.

POST ${SEARCH_ENDPOINT}/indexers('${INDEXER_NAME}')/search.resetdocs?api-version=2026-08-01-preview
Authorization: Bearer ${ACCESS_TOKEN}
Content-Type: application/json

{"documentKeys":["${DOCUMENT_KEY}"]}

Without overwrite=true, the keys you send can be added to keys already waiting from an earlier reset, so the run may re-read more documents. With overwrite=true, only your keys stay in the queue, and the keys someone else queued are dropped. Check with the indexer owner before you use it.

Verify: the response is 204 No Content.

Step 14. Run the indexer. Note the UTC time, then send the run request from Step 9. The run processes only the documents you named. Verify: the response is 202 Accepted.

Step 15. Wait for the run to finish. Check the status as in Step 10. Verify: lastResult.status is success, lastResult.itemsFailed is 0, and lastResult.startTime is later than the time you noted in Step 14.

Step 16. Count the chunks that still let the revoked group in (preview elevated read). Do not reuse the Step 7 count on a native index. A query without a user token skips every chunk that has permissions, so that count is always 0 and proves nothing. The preview header x-ms-enable-elevated-read: true makes the query see every chunk, whatever its permissions. Use it only for this check.

POST ${SEARCH_ENDPOINT}/indexes('${INDEX_NAME}')/docs/search.post.search?api-version=2026-08-01-preview
Authorization: Bearer ${ACCESS_TOKEN}
x-ms-query-source-authorization: ${ACCESS_TOKEN}
x-ms-enable-elevated-read: true
Content-Type: application/json

{"search":"*","filter":"${PARENT_FIELD} eq '${PARENT_ID}' and ${GROUP_FIELD}/any(g: g eq '${REVOKED_GROUP_ID}')","select":"${KEY_FIELD}","count":true}

Verify: @odata.count is 0. When a user was revoked directly, repeat the count with ${USER_FIELD} and the user's ID. For a SharePoint site group (preview), use the stored ID, which starts with spg:.

Deleting chunks has no index-level undo

The delete removes the chunks and their vectors from this index. To get them back, you must re-ingest from the source or rebuild the index. Do not delete when a permission change is enough; see Section 8.

Step 17. Delete the indexed chunks (approved content removal only). First confirm that the source file is deleted or excluded from ingestion, and that the key list from Step 5 passed its check. Then send one delete action per chunk key.

POST ${SEARCH_ENDPOINT}/indexes('${INDEX_NAME}')/docs/search.index?api-version=2026-04-01
Authorization: Bearer ${ACCESS_TOKEN}
Content-Type: application/json

{"value":[{"@search.action":"delete","${KEY_FIELD}":"${CHUNK_KEY}"}]}

Verify: every item in the response has "status": true, and a new search by parent ID returns no chunks. Confirm the source cannot add the file back on the next indexer run.

Stage 4: Verify and close

Step 18. Clear the affected application caches. Use your app's approved cache procedure. Clear the cached group memberships, retrieved chunks, citations and generated answers. For a leaver or a group change, also check where your app gets each user's groups (the sign-in token, Microsoft Graph or its own cache), and refresh it there. Verify: a new signed-in request does not get a cached copy of the restricted content.

Hiding or deleting saved answers changes user data

Saved conversations can be under legal hold or retention rules. Get the data owner's approval first. Hide the answers rather than delete them where the app allows it, so you can restore them if the change was wrong; see Section 8.

Step 19. Hide saved answers that quoted the affected document. A user can reopen an earlier conversation and see an answer given before the change. Find saved answers that cited the affected document, for example by its citation. Hide or delete them for the users who lost access, following your app's data-retention rules. If your app keeps no chat history, record that in the ticket and go to Step 20. Verify: the revoked persona opens an earlier conversation and does not see the restricted text.

Step 20. Test both denied and retained access. Sign in to the application as the revoked persona and the authorised control persona. Use test accounts, never real users. Test every retrieval surface: search, chat, citations, suggestions, exports and agent tools. Save the evidence in the ticket's evidence location. Verify: the revoked persona gets no affected chunk, citation or answer text. The control persona keeps only the access the source grants.

Step 21. Check the next scheduled indexer run. After the next run, repeat the count from Step 7 (custom index) or Step 16 (native index), and the persona test from Step 20. Verify: the count is still 0, the persona test still passes, and the indexer has not brought back the old permissions.

Step 22. Record how long the revocation took. Add these UTC times to the ticket: source change, containment, index update, persona test passed, and next indexer run checked. Verify: the ticket has the evidence, the final indexer status and the approval to turn the app path back on.

Step 23. Prove the change reached every place. Run PB-RAG-09-05 for the affected documents. It checks the source, the index and the application caches. Verify: every item in its checklist is done, and the ticket links the completed checklist.

7. Monitoring and alerts

Thresholds are team defaults, not platform limits. The revocation objective is 30 minutes from the source change (team default), the same target as the story's dry-run test. Tune the alerts against the service and application baseline before use.

Signal Source Alert when Action
Permission-change age for affected chunks acl_synced_at on each chunk (custom field in snippets/security-fields.json, set by Step 7), or an application audit event More than 30 minutes after the source change (team default) Contain the affected retrieval path and repeat the source-to-index sync.
Indexer execution status Azure AI Search indexer execution history Any permission resync or required indexer run fails (team default) Keep the affected path contained; correct the source/indexer issue and rerun.
Revoked-persona test result Application persona probe Any affected chunk, citation, or answer is returned (team default) Treat as a leak; follow PB-RAG-04-04 and maintain containment.

Send every alert to the RAG Lead (team default), the owner role of this runbook.

8. Rollback

Situation Rollback Time
Before a permission patch Nothing to undo yet. If the proposed lists are wrong, correct them from the source and get the source owner's confirmation again. Before the update; record the time taken.
Patch removed access from an authorised user Re-read the current source permissions and get the source owner's approval. Then merge the corrected full lists onto every affected chunk. Do not put back the old values from Step 6 without that approval. Until the source and persona checks pass.
Patch left access for the revoked persona Keep the app path contained. Run Step 5 again, check the source permissions, then send the full lists again (Step 7). Until the revoked-persona test passes.
Preview resync or Reset Documents gave wrong values Keep the app path contained. Correct the source permissions, then repeat the resync or reset, or use the approved fallback. These operations have no automatic undo. Until a new indexer run and the persona test pass.
Chunks were deleted by mistake Re-ingest the file from the source with the approved ingestion or rebuild procedure. If no tested restore exists, treat the deletion as permanent and escalate to the RAG Lead. Keep the app path off until the chunks are back. Depends on the source and pipeline; record the time taken.
Saved answers were hidden for a user who should keep access Unhide them after the source owner confirms the user's access. Answers that were deleted cannot be restored by this runbook; follow your app's data-recovery procedure, if it has one. Until the persona test passes; record actual elapsed time.
App path was turned off for containment Turn it back on only after the security approver confirms both persona tests pass and the evidence is recorded. After sign-off; record the time taken.

Roll back if any one of these happens: the revoked persona gets an affected result; a persona who should keep access loses it; a chunk is missing or has permissions the source owner did not approve; or an indexer run brings back revoked access.

9. Known issues and fixes

Symptom Likely cause Fix
The revoked persona still gets a chunk A chunk was missed, or the persona is still in another group on the list Run Step 5 again and check the persona's other groups. Correct the source, then send the full lists again (Step 7).
The Step 7 count stays above 0 after the merge The key list was incomplete, or some chunks use a different parent ID Run Step 5 again, check how the index links chunks to their file, and patch every key. Keep the app path contained until the persona test passes.
A later indexer run brings back the old permissions The source was not changed, or the indexer did not notice the change Correct the source first. Use resync (Step 8) if your source supports it, and check the next run.
SharePoint chunks keep the old permissions after a site, library, list or folder change A normal indexer run does not pick up permission changes on a parent scope Call preview Resync Indexers with options: ["permissions"], or Reset Documents with the affected keys, then run the indexer.
A resync request is refused The source or indexer does not support it, the feature is off, or your role cannot manage indexers Do not try other guessed requests. Check the API version and role for your source, or use the approved fallback.
A merge action fails The field name is not in the index, or the key is wrong Check the index definition and the key field. Fix the request and resend only the failed keys.
The app still shows the content, but the index count is 0 A cache in the app still holds old groups, chunks, citations or answers Clear the app caches (Step 18) and test again in a new signed-in session.
The revoked persona sees the restricted text in an earlier conversation Saved chat history still holds an answer given before the change Hide or delete those saved answers for users who lost access (Step 19).
A run request fails with "Another indexer invocation is currently in progress; concurrent invocations are not allowed." An indexer runs one job at a time, and a run is still going Check the status (Step 10). Wait until lastResult.status is no longer inProgress, then send the run again.
On a native index, the revoked-group count is 0, but the revoked persona still gets the chunk The count was run without elevated read. A query without a user token skips every chunk that has permissions, so the count is always 0 Run the count with the preview x-ms-enable-elevated-read: true header (Step 16). Keep the path contained until it is 0 and the persona test passes.

10. Cost and capacity notes

  • Indexer work competes with queries and can increase query latency. Check current service capacity and the indexing baseline before starting a broad resync.
  • Resync Indexers is preview and can cause the indexer to inspect and update permission data. Test the scope in the sandbox and provide an approved fallback before production use.
  • Check the current price before approval.

11. AWS and open-source equivalents

Platform Equivalent Key differences
Amazon Bedrock Knowledge Bases Sync the configured data source after the source document or metadata changes. A sync can re-ingest, re-chunk, and re-embed changed documents. Metadata-only optimisation has documented conditions. Do not assume it gives the same immediate ACL semantics as a direct Azure AI Search patch; verify the selected connector and access-filter design.
OpenSearch Update the permission field on every chunk of the document with the Bulk API (POST /_bulk): one update action per chunk, with the new lists in doc. Then test with the affected user. Same idea as the Azure merge: update changes only the fields you send, and fails if the document does not exist. Document-level security (DLS) is a different tool: a role rule that hides documents when the role reads. It does not restrict writes, and it is not a per-document permission update.

12. Checklist

Pre-flight

  • Ticket identifies the source item, affected parent IDs, requested ACL, and approvers (Step 1).
  • Suspected leak is contained and evidence is preserved (Step 2).
  • Source permission change is applied and verified (Step 3).
  • Correct ingestion path and exact index field names are confirmed (Step 4).
  • Full chunk-key list is saved securely (Step 5).
  • Complete new permission arrays are approved by the source owner (Step 6).
  • Preview resync, Reset Documents or elevated read has explicit approval and a tested fallback (Steps 8, 13 and 16); content deletion has separate approval (Step 17).

Post-change

  • The chosen path completed, and the count for the revoked group or user is 0 (Step 7 for a custom index, Step 16 for a native index).
  • Approved content removals have no remaining indexed chunks and cannot be re-ingested (Step 17).
  • Application caches are cleared, including cached group memberships (Step 18).
  • Saved answers that quoted the document are hidden for users who lost access, or the ticket says the app keeps no chat history (Step 19).
  • Revoked and retained personas pass the application tests (Step 20).
  • A later indexer run does not restore the old ACL (Step 21).
  • UTC times and the approval to turn the app path back on are recorded (Step 22).
  • PB-RAG-09-05 is completed for the affected documents (Step 23).

13. Validation record

Pending. Filled in by the dry-runner (see docs/validation-protocol.md).

Sandbox scenario: The sandbox index uses the custom security-filter fields from PB-RAG-04-02. It holds one test document with 12 chunks. Its source permissions allow a test group (the revoked persona) and a control group (the retained persona). A second engineer, not the author, removes the test group in the source, then follows Steps 1 to 7 and 18 to 23. Use sandbox test accounts only, and record in the Notes which accounts, app URL and questions were used, and where the evidence is saved. Deliberate rollback: in Step 7, the engineer first merges an ACL that also leaves out the control group. The control persona loses access. The engineer then restores it with the "Patch removed access from an authorised user" row in Section 8. If the sandbox has a native ACL indexer (ADLS Gen2 or SharePoint), also run Steps 8 to 12 and 16 for the same change. Otherwise, record the native paths as not dry-run.

Pass criteria:

  1. The script in Step 5 returns 12 keys, count equals expected_count, and there is no error.
  2. Every merge action returns "status": true, and the count in Step 7 is 0.
  3. The revoked persona gets no chunk, citation or answer text within 30 minutes of the source change (Step 20).
  4. The control persona still gets the document after the rollback (Step 20).
  5. After the next indexer run, the count is still 0 and the persona test still passes (Step 21).
  6. The hands-on and waiting time are within the stated duration.

These are sandbox test targets from story RAG-04-S3, not a platform guarantee. The dry-run records the real hands-on and waiting time. If it is outside these targets or the stated duration, update them before sign-off.

Record

Item Detail
Playbook PB-RAG-04-03 at commit [hash]
Dry-run by [Name] (must not be author or reviewer)
Date YYYY-MM-DD
Sandbox [Resource group or tag, tier and region]
Scenario [Scenario followed]
Time taken [Hands-on time] hands-on, [waiting time] waiting
Result [PASS / PASS WITH NOTES / FAIL]
Notes 1. [Step number: defect or explanation; blocking/non-blocking; fixed/logged]
Signed off by [Reviewer name and date]

14. Sources

Facts checked against official documentation on 4, 5 and 11 November 2026.

  • PB-RAG-04-01: choose the document access-control pattern.
  • PB-RAG-04-02: implement the shared security-filter retrieval path.
  • PB-RAG-04-04: contain and investigate suspected permission leakage.
  • PB-RAG-09-05: after this runbook, prove the change reached the source, the index and the caches.