Public/Wait-midPointShadow.ps1
|
function Wait-midPointShadow { <# .SYNOPSIS Waits until a midPoint user's account exists on a resource, optionally with expected attribute values. .DESCRIPTION Polls Get-midPointShadow until a live shadow on the resource exists and every attribute in -Attribute has the expected value, then returns the shadow. Stops with an error after -TimeoutSeconds, showing what was found last. Attribute names are the resource attribute names without namespace prefix (e.g. mail, not ri:mail). A multi-valued attribute matches when it contains all expected values. String comparison is case-insensitive. .PARAMETER User The user: name, OID, or an object from Get-midPointUser. .PARAMETER Resource The resource: name, OID, or an object with an Oid property. .PARAMETER Attribute Expected attribute values, e.g. @{ mail = 'jack@example.com'; department = 'IT' }. Use $null as value to wait until an attribute is empty. .PARAMETER Kind Only shadows of this kind (account, entitlement, generic). .PARAMETER Intent Only shadows with this intent. .PARAMETER NoFetch Compare midPoint's cached values instead of reading the resource each time. .PARAMETER AllowPending Also accept an account that midPoint has requested but not yet created, e.g. on a manual resource (open case) or while an approval is pending. Attributes are then compared with the requested values. .PARAMETER TimeoutSeconds How long to wait. Default 120. .PARAMETER IntervalSeconds Time between checks. Default 5. .EXAMPLE Wait-midPointShadow jack -Resource 'Some Resource' .EXAMPLE Wait-midPointShadow $u -Resource 'Some Resource' -Attribute @{ mail = $u.EmailAddress } -TimeoutSeconds 300 #> [CmdletBinding()] [OutputType('midPointTools.Shadow')] param( [Parameter(Mandatory, Position = 0, ValueFromPipeline)] [Alias('Identity')] [object] $User, [Parameter(Mandatory, Position = 1)] [object] $Resource, [hashtable] $Attribute, [ValidateSet('account', 'entitlement', 'generic')] [string] $Kind, [string] $Intent, [switch] $NoFetch, [switch] $AllowPending, [ValidateRange(1, 86400)] [int] $TimeoutSeconds = 120, [ValidateRange(1, 3600)] [int] $IntervalSeconds = 5 ) begin { # Resolve once, so a wrong resource name fails immediately instead of after the timeout. $resourceOid = Resolve-midPointObjectOid -InputObject $Resource -Type resources } process { $userOid = Resolve-midPointObjectOid -InputObject $User -Type users $label = Get-midPointIdentityLabel $User $params = @{ User = $userOid; Resource = $resourceOid; NoFetch = $NoFetch } if ($Kind) { $params.Kind = $Kind } if ($Intent) { $params.Intent = $Intent } $resourceLabel = Get-midPointIdentityLabel $Resource $activity = "Waiting for the account of '$label' on '$resourceLabel'" $started = [datetime]::UtcNow $deadline = $started.AddSeconds($TimeoutSeconds) $lastState = 'no account found' try { while ($true) { $all = @(Get-midPointShadow @params) $shadows = @($all | Where-Object { $_.Exists -or ($AllowPending -and $_.IsPending) }) foreach ($shadow in $shadows) { $mismatch = @(Get-midPointAttributeMismatch -Shadow $shadow -Expected $Attribute) if ($mismatch.Count -eq 0) { return $shadow } $lastState = 'account found, but ' + ($mismatch -join '; ') } if ($shadows.Count -eq 0 -and @($all | Where-Object IsPending).Count -gt 0) { $lastState = 'the account is only requested (pending ' + ((@($all | Where-Object IsPending)[0].PendingOperation) -join ', ') + '), e.g. an open case on a manual resource or a pending approval; use -AllowPending to accept it' } $now = [datetime]::UtcNow if ($now -ge $deadline) { break } $elapsed = [int]($now - $started).TotalSeconds Write-Progress -Activity $activity -Status "$lastState ($elapsed of $TimeoutSeconds s)" -PercentComplete ([Math]::Min(100, 100 * $elapsed / $TimeoutSeconds)) Write-Verbose "$activity ($lastState)" Start-Sleep -Seconds $IntervalSeconds } } finally { Write-Progress -Activity $activity -Completed } $record = [System.Management.Automation.ErrorRecord]::new( [System.TimeoutException]::new("Timeout after $TimeoutSeconds s waiting for the account of '$label' on resource '$resourceLabel': $lastState."), 'midPointShadowWaitTimeout', [System.Management.Automation.ErrorCategory]::OperationTimeout, $User) $PSCmdlet.ThrowTerminatingError($record) } } |