Public/Add-NSPEntraPairDecision.ps1
|
function Add-NSPEntraPairDecision { <# .SYNOPSIS Records one operator decision in a queue, and keeps the earlier ones. Offline. .DESCRIPTION selected names an AD user: one of the pair's candidates with -AdObjectGuid, or a user from a search with -ADUser, whose domain must be one of the queue's. It is refused on a loaded queue that has not been revalidated, for accounts that are diagnostic only, and for an AD user already selected elsewhere in the queue or already linked, because one AD user must never map to several cloud accounts. skipped, flagged and deferred clear any selection. Once a write has been attempted the decision is closed: the cloud user may hold the anchor, and Sync-NSPEntraPairingQueue reopens the pair if it does not. The desired immutable ID is filled only under a verified source anchor. Without one, the decision is still recorded, and the pair cannot be written. .EXAMPLE Add-NSPEntraPairDecision -Queue $queue -CloudObjectId $id -Decision selected -AdObjectGuid $guid .EXAMPLE Add-NSPEntraPairDecision -Queue $queue -CloudObjectId $id -Decision flagged -Note 'Shared account?' #> [CmdletBinding(DefaultParameterSetName = 'NoSelection')] param( [Parameter(Mandatory)]$Queue, [Parameter(Mandatory)][string]$CloudObjectId, [Parameter(Mandatory)][ValidateSet('selected', 'skipped', 'flagged', 'deferred')][string]$Decision, [Parameter(Mandatory, ParameterSetName = 'Candidate')][guid]$AdObjectGuid, [Parameter(Mandatory, ParameterSetName = 'Search')]$ADUser, [string]$Note ) $pair = @($Queue.pairs | Where-Object cloudObjectId -eq $CloudObjectId) if ($pair.Count -ne 1) { throw "The queue has $($pair.Count) pairs for cloud object $CloudObjectId." } $pair = $pair[0] if ($pair.executionState -in @('writing', 'applied', 'unconfirmed', 'different', 'unknown', 'linked')) { throw "This pair's write is $($pair.executionState), so its decision cannot change. Run Sync-NSPEntraPairingQueue to read what the cloud user holds." } $previous = [pscustomobject]@{ decision = $pair.decision; adObjectGuid = $pair.adObjectGuid; decidedAtUtc = $pair.decidedAtUtc; decidedBy = $pair.decidedBy; note = $pair.note } $selection = @{ adObjectGuid = $null; adSid = $null; dn = $null; upn = $null; enabled = $null; evidence = @(); immutableId = $null } if ($Decision -eq 'selected') { if ($PSCmdlet.ParameterSetName -eq 'NoSelection') { throw 'selected needs -AdObjectGuid or -ADUser.' } if ($Queue.needsRevalidation) { throw 'This queue was loaded and has not been revalidated. Run Sync-NSPEntraPairingQueue first.' } if ($pair.status -notin @('Suggested', 'Ambiguous', 'OneToMany', 'NotFound')) { throw "This pair is $($pair.status), which is diagnostic only. It cannot be selected." } if ($PSCmdlet.ParameterSetName -eq 'Candidate') { $chosen = @($pair.candidates | Where-Object adObjectGuid -eq ([string]$AdObjectGuid)) if ($chosen.Count -ne 1) { throw "AD user $AdObjectGuid is not a candidate for this pair. Pass it with -ADUser after a search." } $selection = @{ adObjectGuid = $chosen[0].adObjectGuid; adSid = $chosen[0].adSid; dn = $chosen[0].distinguishedName; upn = $chosen[0].userPrincipalName; enabled = $chosen[0].enabled; evidence = @($chosen[0].evidence); immutableId = $chosen[0].desiredImmutableId } } else { if ([string]$ADUser.DomainDNSRoot -notin @($Queue.domains)) { throw "AD user $($ADUser.ObjectGUID) is in domain $($ADUser.DomainDNSRoot), which is not in this queue." } $immutableId = $null if ($Queue.sourceAnchor.status -eq 'verified') { $immutableId = ConvertTo-NSPEntraImmutableId -ADUser $ADUser -Strategy $Queue.sourceAnchor.strategy } $selection = @{ adObjectGuid = [string]$ADUser.ObjectGUID; adSid = [string]$ADUser.ObjectSID; dn = [string]$ADUser.DistinguishedName; upn = [string]$ADUser.UserPrincipalName; enabled = [bool]$ADUser.Enabled; evidence = @('operator-search'); immutableId = $immutableId } } $target = $selection.adObjectGuid if ($target -in @($Queue.linkedAdObjectGuids)) { throw "AD user $target is already linked to a cloud account." } $other = @($Queue.pairs | Where-Object { $_.decision -eq 'selected' -and $_.adObjectGuid -eq $target -and $_.cloudObjectId -ne $CloudObjectId }) if ($other.Count) { throw "AD user $target is already selected for cloud object $($other[0].cloudObjectId)." } } $pair.history = @($pair.history) + $previous $pair.decision = $Decision $pair.adObjectGuid = $selection.adObjectGuid $pair.adSid = $selection.adSid $pair.adDistinguishedNameAtReview = $selection.dn $pair.adUserPrincipalNameAtReview = $selection.upn $pair.adEnabledAtReview = $selection.enabled $pair.matchEvidence = @($selection.evidence) $pair.desiredImmutableId = $selection.immutableId $pair.decidedAtUtc = [datetime]::UtcNow.ToString('o') $pair.decidedBy = [Environment]::UserName $pair.note = $Note $pair } |