IDENTITY GOVERNANCE · LAB
Review a group with real context, close the loop, and fix a removal that comes back
Build a reviewer worksheet for Photo moderation from real data, decide rule-granted rows once and manual grants one by one, confirm a removal in the directory, review a role, and fix access that a rule keeps restoring.
Partly readyUses your lab tenant
The lesson
Builds on: Why access needs governance, Sources of truth.
New to the labs? Start with the lab toolkit and the shared cast and names every lab uses.
Partly ready. Most of this lab runs today. Steps that wait on platform features are marked, and Missing infrastructure says what they need.
- G23 Access requests and approvals, access reviews, JIT or temporary access
Your progress
Press Start before you begin. Only events your tenant records after that count, in the order below. Checking reads your tenant's Audit, so you need Audit read access in it.
Sign in to start this lab and check your progress. Log in or create an account.
A rule grants the group to one department
Recorded as
scim.group.patchsucceeded forlab-provisioning.Confirm the removal by reading the group back
Recorded as
scim.groups.searchsucceeded forlab-scim-reader.Change a role after reviewing its content
Recorded as
tenant.roles.updatesucceeded.Fix the source that kept restoring access
Recorded as
scim.user.patchsucceeded forlab-hr-feed.
Setup
You are the owner of Photo moderation, like Morgan Hale reviewing who can update oncology records. Its members arrived in different ways, which is what makes the review worth doing.
Open a bash shell and set the variables and helpers from the directory lab, plus
HR_ID,HR_SECRET,READER_IDandREADER_SECRET(read -rsfor secrets).Make sure
Photo moderationholds Cora and Ava, both added by hand in earlier labs. Create[email protected](Mod Test) in the portal and add it to the group. Do not sign in as it.Pick one department from the simulated workforce as
DEPTand look up the IDs you need.
export DEPT=<a department from the simulated workforce>
export MOD=$(scim -G "$SCIM/Groups" --data-urlencode 'filter=displayName eq "Photo moderation"' | jq -r '.Resources[0].id')
export CORA=$(scim -G "$SCIM/Users" --data-urlencode 'filter=userName eq "[email protected]"' | jq -r '.Resources[0].id')
Press Start on the lab page.
Walkthrough
Grant by rule. Rule R4 says everyone active in
DEPTbelongs inPhoto moderation. Apply it with a small function you will run again later.
apply_r4() { scim -G "$SCIM/Users" --data-urlencode "filter=active eq true and $ENT:department eq \"$DEPT\"" --data-urlencode attributes=id \
| jq '{schemas:["urn:ietf:params:scim:api:messages:2.0:PatchOp"],Operations:[{op:"add",path:"members",value:[.Resources[] | {value: .id}]}]}' \
| scim -X PATCH "$SCIM/Groups/$MOD" --data-binary @- -o /dev/null -w 'R4 applied: %{http_code}\n'; }
apply_r4
Build the worksheet. For every member, gather:
Department and job:
scim "$SCIM/Users/<id>" | jq "{displayName, title, dept: .\"$ENT\".department}"How granted: search Audit for the group ID.
lab-provisioningin step 1 means rule R4; you or Ben in the portal means a manual grant.Last used: the Last sign-in column on Users.
Peers: how many people in the same department hold the group, against the department's size.
Fill one row per member:
| Person | Department and job | How granted | Last used | Peers with this access | Decision and reason |
|---|
Why it matters: this is "Making a decision". Last use, peers and how the access was granted turn a name on a list into something a reviewer can judge.
Rule-granted rows are one decision. Confirm R4 once: should everyone in
DEPTmoderate photos? Then check that each of those people's department value is right. The rows themselves need no individual decision.
Why it matters: removing a rule-granted row would last only until the rule ran again. Reviewing through the rule makes a long list short.
Decide the manual grants.
lab-tmp-modtest: manual, never signed in, no peers, no reason anyone can give, so remove. Ava: manual, recently used, moved to moderation in the changing jobs lab, so keep and record the reason. Cora: manual, used, the team's longest member, so keep. Record each decision with reason and time.
cat > review.json <<'EOF'
{"review": "Photo moderation", "reviewer": "group owner", "items": [
{"person": "[email protected]", "decision": "remove", "reason": "No reason, never used, no peers"},
{"person": "[email protected]", "decision": "keep", "reason": "Moved to moderation; uses it daily"},
{"person": "[email protected]", "decision": "keep", "reason": "Moderation team member"}]}
EOF
Close the loop. Remove the test account through the provisioning client, then confirm it from the directory with the read-only client.
export MT=$(scim -G "$SCIM/Users" --data-urlencode 'filter=userName eq "[email protected]"' | jq -r '.Resources[0].id')
jq -n --arg u "$MT" '{schemas:["urn:ietf:params:scim:api:messages:2.0:PatchOp"],Operations:[{op:"remove",path:("members[value eq \""+$u+"\"]")}]}' \
| scim -X PATCH "$SCIM/Groups/$MOD" --data-binary @- -o /dev/null -w '%{http_code}\n'
READ_TOKEN=$(token_for "$READER_ID" "$READER_SECRET" "$SCIM_SCOPE.read")
curl -s -G -H "Authorization: Bearer $READ_TOKEN" "$SCIM/Groups" \
--data-urlencode "filter=displayName eq \"Photo moderation\" and members[value eq \"$MT\"]" | jq .totalResults
The answer is 0. Add the Audit event ID of the removal to the item in review.json.
Why it matters: this is "Closing the loop". A decision to remove creates work, and the item is complete only when the access is gone, the removal is confirmed in the target, and the result sits next to the decision.
Review a role, and review by manager. List the holders of
Help deskon Users. Go through its permissions: does Ben still needtenant.users.locknow that urgent departures go through the source? If not, remove it from the role and record why. Then list Cora's reports withfilter=$ENT:manager eq "$CORA"and write the list of their groups for Cora to confirm.
Why it matters: this is "Who reviews what". The role's owner judges what the role allows; a manager judges whether each report's job still needs what they hold.
Removed access comes back. Remove one R4 member from
Photo moderationbecause "they do not need it", using the step 5 PATCH with their ID. Runapply_r4. They are back.
Why it matters: when removed access returns after the next sync, a rule or a source still grants it, and the fix belongs there.
Fix it at the source: as lab-hr-feed, move that person to another department, then run apply_r4 again and remove them from the group. This time they stay removed.
export HR_TOKEN=$(token_for "$HR_ID" "$HR_SECRET" "$SCIM_SCOPE.users $SCIM_SCOPE.groups")
jq -n --arg ent "$ENT" '{schemas:["urn:ietf:params:scim:api:messages:2.0:PatchOp"],Operations:[{op:"replace",path:($ent+":department"),value:"<another department>"}]}' \
| curl -s -X PATCH "$SCIM/Users/<their id>" -H "Authorization: Bearer $HR_TOKEN" -H "Content-Type: application/scim+json" --data-binary @- -o /dev/null -w '%{http_code}\n'
If the department was right and the rule was wrong, the fix would be a change to R4, decided by its owner.
Check yourself for rubber-stamping. Note how long your decisions took and how many were "keep". Count which rows you could not decide without asking someone.
Why it matters: this is "Rubber-stamping". A review that removed nothing deserves a second look, and silence should never count as approval.
Planned walkthrough
Once G23 exists, the review is a tenant campaign.
Start a review campaign for
Photo moderation, owner-reviewed, due in five days. Rows granted by a rule appear as one item for the rule; manual grants with no recorded reason are listed first and marked.You cannot review your own access; such rows route to another reviewer.
Keeping a marked row requires a reason. An undecided row escalates to the reviewer's manager at the deadline instead of counting as keep.
A remove decision triggers the removal, reads the group back, and stores the confirmation with the decision.
A move recorded by HR opens a small review for the person's new manager with only the access the move did not recalculate.
Check your work
Press Check my progress. The checks look for, in order:
scim.group.patchsucceeded bylab-provisioning(step 1)scim.groups.searchsucceeded bylab-scim-reader(step 5)tenant.roles.updatesucceeded (step 6)scim.user.patchsucceeded bylab-hr-feed(step 7)
Your review.json holds a decision and reason for every manual row, and the removal's Audit event ID.
Cleanup
Remove the R4 members from
Photo moderationif you no longer want them there, and deletelab-tmp-modtest.Keep
review.json. The evidence lab counts its decisions.
Missing infrastructure
G23: there are no review campaigns by manager, entitlement or application, no reviewer assignment that excludes self-review, no deadlines with escalation, no required reasons for keeping marked items, no stored decisions linked to confirmed removals, and no event-triggered reviews after a move.