Public/Restore-GkDeletedObject.ps1
|
function Restore-GkDeletedObject { <# .SYNOPSIS Restore a soft-deleted directory object from the 30-day recycle window. .DESCRIPTION Calls POST /directory/deletedItems/{id}/restore. This is the write counterpart to Get-GkDeletedItem, and the recovery path for anything Remove-GkStaleGuest -Delete or Disable-GkStaleDevice -Delete removed by mistake. Restoring a user also restores their mailbox, license assignments and group memberships. A user whose userPrincipalName or proxy addresses now collide with a live object cannot be restored until the conflict is resolved — Graph rejects it, and the error says which. State-changing: supports -WhatIf / -Confirm and prompts by default. Scopes depend on the object type and only the one in use is validated: User.DeleteRestore.All for users, Group.ReadWrite.All for groups, Application.ReadWrite.All for applications and service principals, AdministrativeUnit.ReadWrite.All for administrative units. .PARAMETER Id One or more deleted object IDs. Accepts pipeline input, including by the Id property so Get-GkDeletedItem output can be piped straight in. .PARAMETER Type The directory object type being restored. Determines which scope is validated. Accepts pipeline input by the ObjectType property, so Get-GkDeletedItem supplies it automatically. .EXAMPLE Get-GkDeletedItem -Type User -ExpiringInDays 3 | Restore-GkDeletedObject -WhatIf Preview restoring every deleted user about to be purged. .EXAMPLE Restore-GkDeletedObject -Id $objectId -Type User Restore one user (prompts for confirmation). .EXAMPLE Get-GkDeletedItem -Type Group | Where-Object DisplayName -like 'Project*' | Restore-GkDeletedObject -Confirm:$false .OUTPUTS PSGraphKit.RestoreResult #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] [OutputType('PSGraphKit.RestoreResult')] param( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [string[]] $Id, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [Alias('ObjectType')] [ValidateSet('User', 'Group', 'Application', 'ServicePrincipal', 'AdministrativeUnit')] [string] $Type ) begin { # Validate lazily: -Type binds per pipeline item, so the scope check belongs in process. $validated = @{} } process { if (-not $validated.ContainsKey($Type)) { Test-GkConnection -FunctionName 'Restore-GkDeletedObject' -Variant $Type -Caller $PSCmdlet | Out-Null $validated[$Type] = $true } foreach ($oid in $Id) { if ([string]::IsNullOrWhiteSpace($oid)) { continue } if (-not $PSCmdlet.ShouldProcess($oid, "Restore deleted $Type")) { continue } $enc = [uri]::EscapeDataString($oid) $outcome = 'Restored' $errMsg = $null $displayName = $null try { $resp = Invoke-GkGraphRequest -Raw -Method POST -Uri "/directory/deletedItems/$enc/restore" ` -CallerFunction 'Restore-GkDeletedObject' $displayName = [string](Get-GkDictValue $resp 'displayName') } catch { $outcome = 'Failed' $errMsg = $_.Exception.Message Write-Warning "Failed to restore $Type '$oid': $errMsg" } [pscustomobject]@{ PSTypeName = 'PSGraphKit.RestoreResult' Id = $oid ObjectType = $Type DisplayName = $displayName Action = 'Restore' Outcome = $outcome Error = $errMsg } } } } |