diff --git a/module/Entra/AdditionalFunctions/Restore-EntraDeletedDirectoryObject.ps1 b/module/Entra/AdditionalFunctions/Restore-EntraDeletedDirectoryObject.ps1 new file mode 100644 index 000000000..96ac31906 --- /dev/null +++ b/module/Entra/AdditionalFunctions/Restore-EntraDeletedDirectoryObject.ps1 @@ -0,0 +1,46 @@ +function Restore-EntraDeletedDirectoryObject { + [CmdletBinding(DefaultParameterSetName = '')] + param ( + [Alias('ObjectId')] + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [System.String] $Id, + [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [switch] $AutoReconcileProxyConflict + ) + + PROCESS { + $params = @{} + $customHeaders = New-EntraCustomHeaders -Command $MyInvocation.MyCommand + $params["Uri"] = 'https://graph.microsoft.com/v1.0/directory/deletedItems/' + $params["Method"] = "POST" + if($null -ne $PSBoundParameters["Id"]) + { + $params["Uri"] += $Id+"/microsoft.graph.restore" + } + if($PSBoundParameters.ContainsKey("AutoReconcileProxyConflict")) + { + $params["Body"] = @{ + autoReconcileProxyConflict = $true + } + } + + Write-Debug("============================ TRANSFORMATIONS ============================") + $params.Keys | ForEach-Object {"$_ : $($params[$_])" } | Write-Debug + Write-Debug("=========================================================================`n") + + $response = Invoke-GraphRequest @params -Headers $customHeaders + if($response){ + $userList = @() + foreach ($data in $response) { + $userType = New-Object Microsoft.Graph.PowerShell.Models.MicrosoftGraphDirectoryObject + $data.PSObject.Properties | ForEach-Object { + $propertyName = $_.Name + $propertyValue = $_.Value + $userType | Add-Member -MemberType NoteProperty -Name $propertyName -Value $propertyValue -Force + } + $userList += $userType + } + $userList + } + } +} \ No newline at end of file diff --git a/module/Entra/customizations/Restore-EntraDeletedDirectoryObject.ps1 b/module/Entra/customizations/Restore-EntraDeletedDirectoryObject.ps1 deleted file mode 100644 index 31b784945..000000000 --- a/module/Entra/customizations/Restore-EntraDeletedDirectoryObject.ps1 +++ /dev/null @@ -1,77 +0,0 @@ -# ------------------------------------------------------------------------------ -# Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. -# ------------------------------------------------------------------------------ -@{ - SourceName = "Restore-AzureADMSDeletedDirectoryObject" - TargetName = $null - Parameters = $null - Outputs = $null - CustomScript = @' - PROCESS { - $params = @{} - $customHeaders = New-EntraCustomHeaders -Command $MyInvocation.MyCommand - if($PSBoundParameters.ContainsKey("Verbose")) - { - $params["Verbose"] = $PSBoundParameters["Verbose"] - } - if($null -ne $PSBoundParameters["Id"]) - { - $params["DirectoryObjectId"] = $PSBoundParameters["Id"] - } - if($PSBoundParameters.ContainsKey("Debug")) - { - $params["Debug"] = $PSBoundParameters["Debug"] - } - if($null -ne $PSBoundParameters["WarningVariable"]) - { - $params["WarningVariable"] = $PSBoundParameters["WarningVariable"] - } - if($null -ne $PSBoundParameters["InformationVariable"]) - { - $params["InformationVariable"] = $PSBoundParameters["InformationVariable"] - } - if($null -ne $PSBoundParameters["InformationAction"]) - { - $params["InformationAction"] = $PSBoundParameters["InformationAction"] - } - if($null -ne $PSBoundParameters["OutVariable"]) - { - $params["OutVariable"] = $PSBoundParameters["OutVariable"] - } - if($null -ne $PSBoundParameters["OutBuffer"]) - { - $params["OutBuffer"] = $PSBoundParameters["OutBuffer"] - } - if($null -ne $PSBoundParameters["ErrorVariable"]) - { - $params["ErrorVariable"] = $PSBoundParameters["ErrorVariable"] - } - if($null -ne $PSBoundParameters["PipelineVariable"]) - { - $params["PipelineVariable"] = $PSBoundParameters["PipelineVariable"] - } - if($null -ne $PSBoundParameters["ErrorAction"]) - { - $params["ErrorAction"] = $PSBoundParameters["ErrorAction"] - } - if($null -ne $PSBoundParameters["WarningAction"]) - { - $params["WarningAction"] = $PSBoundParameters["WarningAction"] - } - - Write-Debug("============================ TRANSFORMATIONS ============================") - $params.Keys | ForEach-Object {"$_ : $($params[$_])" } | Write-Debug - Write-Debug("=========================================================================`n") - - $response = Restore-MgDirectoryDeletedItem @params -Headers $customHeaders - $response | ForEach-Object { - if($null -ne $_) { - Add-Member -InputObject $_ -NotePropertyMembers $_.AdditionalProperties - Add-Member -InputObject $_ -MemberType AliasProperty -Name ObjectId -Value Id - Add-Member -InputObject $_ -MemberType NoteProperty -Name OdataType -value $_.AdditionalProperties['@odata.type'] - } - } - $response - } -'@ -} \ No newline at end of file diff --git a/module/EntraBeta/AdditionalFunctions/Restore-EntraBetaDeletedDirectoryObject.ps1 b/module/EntraBeta/AdditionalFunctions/Restore-EntraBetaDeletedDirectoryObject.ps1 new file mode 100644 index 000000000..a4d3d1648 --- /dev/null +++ b/module/EntraBeta/AdditionalFunctions/Restore-EntraBetaDeletedDirectoryObject.ps1 @@ -0,0 +1,45 @@ +function Restore-EntraBetaDeletedDirectoryObject { + [CmdletBinding(DefaultParameterSetName = '')] + param ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [System.String] $Id, + [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [switch] $AutoReconcileProxyConflict + ) + + PROCESS { + $params = @{} + $customHeaders = New-EntraBetaCustomHeaders -Command $MyInvocation.MyCommand + $params["Uri"] = 'https://graph.microsoft.com/beta/directory/deletedItems/' + $params["Method"] = "POST" + if($null -ne $PSBoundParameters["Id"]) + { + $params["Uri"] += $Id+"/microsoft.graph.restore" + } + if($PSBoundParameters.ContainsKey("AutoReconcileProxyConflict")) + { + $params["Body"] = @{ + autoReconcileProxyConflict = $true + } + } + + Write-Debug("============================ TRANSFORMATIONS ============================") + $params.Keys | ForEach-Object {"$_ : $($params[$_])" } | Write-Debug + Write-Debug("=========================================================================`n") + + $response = Invoke-GraphRequest @params -Headers $customHeaders + if($response){ + $userList = @() + foreach ($data in $response) { + $userType = New-Object Microsoft.Graph.Beta.PowerShell.Models.MicrosoftGraphDirectoryObject + $data.PSObject.Properties | ForEach-Object { + $propertyName = $_.Name + $propertyValue = $_.Value + $userType | Add-Member -MemberType NoteProperty -Name $propertyName -Value $propertyValue -Force + } + $userList += $userType + } + $userList + } + } +} \ No newline at end of file diff --git a/module/EntraBeta/customizations/Restore-EntraBetaDeletedDirectoryObject.ps1 b/module/EntraBeta/customizations/Restore-EntraBetaDeletedDirectoryObject.ps1 deleted file mode 100644 index f77d07465..000000000 --- a/module/EntraBeta/customizations/Restore-EntraBetaDeletedDirectoryObject.ps1 +++ /dev/null @@ -1,77 +0,0 @@ -# ------------------------------------------------------------------------------ -# Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. -# ------------------------------------------------------------------------------ -@{ - SourceName = "Restore-AzureADMSDeletedDirectoryObject" - TargetName = $null - Parameters = $null - Outputs = $null - CustomScript = @' - PROCESS { - $params = @{} - $customHeaders = New-EntraBetaCustomHeaders -Command $MyInvocation.MyCommand - if($PSBoundParameters.ContainsKey("Verbose")) - { - $params["Verbose"] = $PSBoundParameters["Verbose"] - } - if($null -ne $PSBoundParameters["Id"]) - { - $params["DirectoryObjectId"] = $PSBoundParameters["Id"] - } - if($PSBoundParameters.ContainsKey("Debug")) - { - $params["Debug"] = $PSBoundParameters["Debug"] - } - if($null -ne $PSBoundParameters["WarningVariable"]) - { - $params["WarningVariable"] = $PSBoundParameters["WarningVariable"] - } - if($null -ne $PSBoundParameters["InformationVariable"]) - { - $params["InformationVariable"] = $PSBoundParameters["InformationVariable"] - } - if($null -ne $PSBoundParameters["InformationAction"]) - { - $params["InformationAction"] = $PSBoundParameters["InformationAction"] - } - if($null -ne $PSBoundParameters["OutVariable"]) - { - $params["OutVariable"] = $PSBoundParameters["OutVariable"] - } - if($null -ne $PSBoundParameters["OutBuffer"]) - { - $params["OutBuffer"] = $PSBoundParameters["OutBuffer"] - } - if($null -ne $PSBoundParameters["ErrorVariable"]) - { - $params["ErrorVariable"] = $PSBoundParameters["ErrorVariable"] - } - if($null -ne $PSBoundParameters["PipelineVariable"]) - { - $params["PipelineVariable"] = $PSBoundParameters["PipelineVariable"] - } - if($null -ne $PSBoundParameters["ErrorAction"]) - { - $params["ErrorAction"] = $PSBoundParameters["ErrorAction"] - } - if($null -ne $PSBoundParameters["WarningAction"]) - { - $params["WarningAction"] = $PSBoundParameters["WarningAction"] - } - - Write-Debug("============================ TRANSFORMATIONS ============================") - $params.Keys | ForEach-Object {"$_ : $($params[$_])" } | Write-Debug - Write-Debug("=========================================================================`n") - - $response = Restore-MgBetaDirectoryDeletedItem @params -Headers $customHeaders - $response | ForEach-Object { - if($null -ne $_) { - Add-Member -InputObject $_ -NotePropertyMembers $_.AdditionalProperties - Add-Member -InputObject $_ -MemberType AliasProperty -Name ObjectId -Value Id - Add-Member -InputObject $_ -MemberType NoteProperty -Name OdataType -value $_.AdditionalProperties['@odata.type'] - } - } - $response - } -'@ -} \ No newline at end of file diff --git a/module/docs/entra-powershell-beta/Microsoft.Graph.Entra.Beta/Restore-EntraBetaDeletedDirectoryObject.md b/module/docs/entra-powershell-beta/Microsoft.Graph.Entra.Beta/Restore-EntraBetaDeletedDirectoryObject.md index 41dd257f4..0037832ba 100644 --- a/module/docs/entra-powershell-beta/Microsoft.Graph.Entra.Beta/Restore-EntraBetaDeletedDirectoryObject.md +++ b/module/docs/entra-powershell-beta/Microsoft.Graph.Entra.Beta/Restore-EntraBetaDeletedDirectoryObject.md @@ -28,6 +28,7 @@ Restore a previously deleted object. ```powershell Restore-EntraBetaDeletedDirectoryObject -Id + [-AutoReconcileProxyConflict] [] ``` @@ -75,6 +76,24 @@ This example shows how to restore a deleted object in Microsoft Entra ID. - `-Id` parameter specifies the Id of the directory object to restore. +### Example 2: Restoring a Soft-Deleted User and Removing Conflicting Proxy Addresses + +```powershell +Connect-Entra -Scopes 'User.ReadWrite.All' +Restore-EntraBetaDeletedDirectoryObject -Id 'dddddddd-3333-4444-5555-eeeeeeeeeeee' -AutoReconcileProxyConflict +``` + +```Output +Id DeletedDateTime +-- --------------- +dddddddd-3333-4444-5555-eeeeeeeeeeee +``` + +This example shows how to restore a deleted object in Microsoft Entra ID. + +- `-Id` parameter specifies the Id of the directory object to restore. +- `-AutoReconcileProxyConflict` parameter removes any conflicting proxy addresses while restoring a soft-deleted user whose one or more proxy addresses are currently used for an active user. + ## Parameters ### -Id @@ -93,6 +112,22 @@ Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` +### -AutoReconcileProxyConflict + +Specifies whether Microsoft Entra ID should remove conflicting proxy addresses when restoring a soft-deleted user, if any of the user's proxy addresses are currently in use by an active user. This parameter applies only when restoring a soft-deleted user. The default value is `false`. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName, ByValue) +Accept wildcard characters: False +``` + ### CommonParameters This cmdlet supports the common parameters: `-Debug`, `-ErrorAction`, `-ErrorVariable`, `-InformationAction`, `-InformationVariable`, `-OutVariable`, `-OutBuffer`, `-PipelineVariable`, `-Verbose`, `-WarningAction`, and `-WarningVariable`. For more information, see [about_CommonParameters](https://go.microsoft.com/fwlink/?LinkID=113216). diff --git a/module/docs/entra-powershell-v1.0/Microsoft.Graph.Entra/Restore-EntraDeletedDirectoryObject.md b/module/docs/entra-powershell-v1.0/Microsoft.Graph.Entra/Restore-EntraDeletedDirectoryObject.md index d9e5b63a2..a518c2a53 100644 --- a/module/docs/entra-powershell-v1.0/Microsoft.Graph.Entra/Restore-EntraDeletedDirectoryObject.md +++ b/module/docs/entra-powershell-v1.0/Microsoft.Graph.Entra/Restore-EntraDeletedDirectoryObject.md @@ -75,6 +75,24 @@ This example shows how to restore a deleted object in Microsoft Entra ID. - `-Id` parameter specifies the Id of the directory object to restore. +### Example 2: Restoring a Soft-Deleted User and Removing Conflicting Proxy Addresses + +```powershell +Connect-Entra -Scopes 'User.ReadWrite.All' +Restore-EntraDeletedDirectoryObject -Id 'dddddddd-3333-4444-5555-eeeeeeeeeeee' -AutoReconcileProxyConflict +``` + +```Output +Id DeletedDateTime +-- --------------- +dddddddd-3333-4444-5555-eeeeeeeeeeee +``` + +This example shows how to restore a deleted object in Microsoft Entra ID. + +- `-Id` parameter specifies the Id of the directory object to restore. +- `-AutoReconcileProxyConflict` parameter removes any conflicting proxy addresses while restoring a soft-deleted user whose one or more proxy addresses are currently used for an active user. + ## Parameters ### -Id @@ -93,6 +111,22 @@ Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` +### -AutoReconcileProxyConflict + +Specifies whether Microsoft Entra ID should remove conflicting proxy addresses when restoring a soft-deleted user, if any of the user's proxy addresses are currently in use by an active user. This parameter applies only when restoring a soft-deleted user. The default value is `false`. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName, ByValue) +Accept wildcard characters: False +``` + ### CommonParameters This cmdlet supports the common parameters: `-Debug`, `-ErrorAction`, `-ErrorVariable`, `-InformationAction`, `-InformationVariable`, `-OutVariable`, `-OutBuffer`, `-PipelineVariable`, `-Verbose`, `-WarningAction`, and `-WarningVariable`. For more information, see [about_CommonParameters](https://go.microsoft.com/fwlink/?LinkID=113216). diff --git a/test/module/Entra/Restore-EntraDeletedDirectoryObject.Tests.ps1 b/test/module/Entra/Restore-EntraDeletedDirectoryObject.Tests.ps1 index 8f8459e2e..4b65b85f7 100644 --- a/test/module/Entra/Restore-EntraDeletedDirectoryObject.Tests.ps1 +++ b/test/module/Entra/Restore-EntraDeletedDirectoryObject.Tests.ps1 @@ -23,11 +23,12 @@ BeforeAll { "preferredLanguage" = "EN" } "Parameters" = $args + "ObjectId" = "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb" } ) - } + } - Mock -CommandName Restore-MgDirectoryDeletedItem -MockWith $scriptblock -ModuleName Microsoft.Graph.Entra + Mock -CommandName Invoke-GraphRequest -MockWith $scriptblock -ModuleName Microsoft.Graph.Entra } Describe "Restore-EntraDeletedDirectoryObject" { Context "Restore-EntraDeletedDirectoryObject" { @@ -35,8 +36,13 @@ Context "Restore-EntraDeletedDirectoryObject" { $result = Restore-EntraDeletedDirectoryObject -Id "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb" $result | Should -Not -BeNullOrEmpty $result.Id | Should -Be "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb" - - Should -Invoke -CommandName Restore-MgDirectoryDeletedItem -ModuleName Microsoft.Graph.Entra -Times 1 + Should -Invoke -CommandName Invoke-GraphRequest -ModuleName Microsoft.Graph.Entra -Times 1 + } + It "Should return specific MS deleted directory object with AutoReconcileProxyConflict" { + $result = Restore-EntraDeletedDirectoryObject -Id "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb" -AutoReconcileProxyConflict + $result | Should -Not -BeNullOrEmpty + $result.Id | Should -Be "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb" + Should -Invoke -CommandName Invoke-GraphRequest -ModuleName Microsoft.Graph.Entra -Times 1 } It "Should fail when Id is empty" { { Restore-EntraDeletedDirectoryObject -Id } | Should -Throw "Missing an argument for parameter 'Id'*" @@ -47,20 +53,13 @@ Context "Restore-EntraDeletedDirectoryObject" { It "Result should contain Alias properties" { $result = Restore-EntraDeletedDirectoryObject -Id "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb" $result.ObjectId | should -Be "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb" - $result.OdataType | should -Be "#microsoft.graph.user" - } - It "Should contain DirectoryObjectId in parameters when passed Id to it" { - $result = Restore-EntraDeletedDirectoryObject -Id "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb" - $params = Get-Parameters -data $result.Parameters - $params.DirectoryObjectId | Should -Be "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb" - } - It "Should contain 'User-Agent' header" { + $result.AdditionalProperties."@odata.type" | should -Be "#microsoft.graph.user" + } + It "Should contain 'User-Agent' header" { $userAgentHeaderValue = "PowerShell/$psVersion EntraPowershell/$entraVersion Restore-EntraDeletedDirectoryObject" - $result = Restore-EntraDeletedDirectoryObject -Id "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb" - $result | Should -Not -BeNullOrEmpty + Restore-EntraDeletedDirectoryObject -Id "11112222-bbbb-3333-cccc-4444dddd5555" $userAgentHeaderValue = "PowerShell/$psVersion EntraPowershell/$entraVersion Restore-EntraDeletedDirectoryObject" - - Should -Invoke -CommandName Restore-MgDirectoryDeletedItem -ModuleName Microsoft.Graph.Entra -Times 1 -ParameterFilter { + Should -Invoke -CommandName Invoke-GraphRequest -ModuleName Microsoft.Graph.Entra -Times 1 -ParameterFilter { $Headers.'User-Agent' | Should -Be $userAgentHeaderValue $true }