Fix AD provider change detection for group membership operations#256
Conversation
Check membership before operations to accurately detect changes. This fixes PruneEntitlements always returning Changed=False when AD cmdlets are idempotent (newer ActiveDirectory module versions). Co-authored-by: ntt-matthias-fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com>
Add clearer explanations for why catch blocks proceed on error, ensuring legitimate operations aren't blocked by idempotency checks. Co-authored-by: ntt-matthias-fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes change detection in the Active Directory provider’s group membership operations so steps like PruneEntitlements correctly report Changed: True when group membership is actually modified, even on newer idempotent ActiveDirectory module versions.
Changes:
- Added a pre-check in
AddGroupMemberandRemoveGroupMemberto detect current membership state before callingAdd-ADGroupMember/Remove-ADGroupMember. - Updated return semantics to
$trueonly when membership changes and$falsefor no-op calls. - Implemented a “fail-forward” pattern: if the membership check fails, proceed to the cmdlet call for an authoritative error.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Code Coverage Report
|
- Extract TestGroupMembership helper to eliminate code duplication - Add Write-Verbose to catch blocks (fixes PSAvoidUsingEmptyCatchBlock) - Optimize RemoveGroupMember with Select-Object -First 1 - Add comprehensive unit tests for change detection behavior - Tests cover no-op, actual change, and fail-forward scenarios Co-authored-by: ntt-matthias-fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com>
There was a problem hiding this comment.
@copilot
I observed the following:
PS C:\Users\adm-mafl-server\Downloads\idle> $plan = New-IdlePlan -WorkflowPath .\leaver.psd1 -Request $request -Providers $providers
PS C:\Users\adm-mafl-server\Downloads\idle> $result = Invoke-IdlePlan -Plan $plan
PS C:\Users\adm-mafl-server\Downloads\idle> $result.Status
Blocked
PS C:\Users\adm-mafl-server\Downloads\idle> $result = Invoke-IdlePlan -Plan $plan
PS C:\Users\adm-mafl-server\Downloads\idle> $result.Status
Blocked
PS C:\Users\adm-mafl-server\Downloads\idle> $plan = New-IdlePlan -WorkflowPath .\leaver.psd1 -Request $request -Providers $providers
PS C:\Users\adm-mafl-server\Downloads\idle> $result = Invoke-IdlePlan -Plan $plan
PS C:\Users\adm-mafl-server\Downloads\idle> $result.Status
Completed The run was as following:
- first Invoke with
$result.Status = Blockedwas correct, as user was still in BYOD group. - before second invoke BYOD group was definetly renived at user -> direct invoke without new plan still had
$result.Status = Blocked. - Having a new plan and then invoke had status
$result.Status = Completed.
My suspicion is that data somewhat gets cached in between? Does that make sense? Is there anything internally that we can improve or is it just relying on more waiting time between changes. But having a new plan and invoke after direct change somehow implies it is maybe more on IdLE side then on server side?
... This is expected behavior, not a bug. The plan captures identity/entitlement data as a snapshot during New-IdlePlan (via ContextResolvers). This snapshot is stored in What's happening:
Why this design:
Recommendation: No code changes needed - this is working as designed per the planning/execution separation documented in |
Fix PruneEntitlements unstable behavior with AD provider
Problem Analysis
The PruneEntitlements step with AD provider always returns
Changed: Falseeven when the user has groups that should be removed.Root Cause: The
RemoveGroupMemberandAddGroupMembermethods in the AD adapter returned$truewhenever the AD cmdlets succeeded, relying on them to throw errors for no-op scenarios. However, in newer versions of the ActiveDirectory PowerShell module (or certain AD configurations),Remove-ADGroupMemberandAdd-ADGroupMemberare idempotent and don't error when removing a non-member or adding an existing member. This caused incorrectChangedstatus reporting.Solution Implemented
Modified membership operations in
/src/IdLE.Provider.AD/Private/New-IdleADAdapter.ps1:Extracted shared helper method
TestGroupMembership:$trueif user is a member,$falseif not$nullif check fails (signals fail-forward to authoritative operation)Select-Object -First 1to short-circuit for performance on large groupsRefactored AddGroupMember and RemoveGroupMember:
Added comprehensive unit tests (
New-IdleADAdapter.MembershipChecks.Tests.ps1):Changes Made
TestGroupMembershiphelper method (lines 670-703)AddGroupMembermethod (lines 705-736)RemoveGroupMembermethod (lines 738-770)Testing & Validation
Impact
This fix ensures that:
PruneEntitlementscorrectly reportsChanged: Truewhen groups are actually removedPruneEntitlementscorrectly reportsChanged: Falsewhen no changes are neededOriginal prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.