DeleteSearchFolders.psm1

<#
. [COPYRIGHT]
. © 2011-2017 Microsoft Corporation. All rights reserved.
.
. [DISCLAIMER]
. This sample script is not supported under any Microsoft standard support
. program or service. The sample scripts are provided AS IS without warranty of
. any kind. Microsoft disclaims all implied warranties including, without
. limitation, any implied warranties of merchantability or of fitness for a
. particular purpose. The entire risk arising out of the use or performance of
. the sample scripts and documentation remains with you. In no event shall
. Microsoft, its authors, or anyone else involved in the creation, production,
. or delivery of the scripts be liable for any damages whatsoever (including,
. without limitation, damages for loss of business profits, business
. interruption, loss of business information, or other pecuniary loss) arising
. out of the use of or inability to use the sample scripts or documentation,
. even if Microsoft has been advised of the possibility of such damages.
.
. [AUTHOR]
. Jason Parker, Senior Consultant
.
. [CONTRIBUTORS]
.
.
. [MODULE]
. DeleteSearchFolders.psm1
.
. [VERSION]
. 1.0
.
. [VERSION HISTORY / UPDATES]
. 1.0 - Jason Parker
. Original Release
.
#>


Function Global:_GetImpersonator {
    <#
    .SYNOPSIS
    Creates an Exchange Web Service object used for impersonating a user's Exchange mailbox.
 
    .DESCRIPTION
    Used to create an impersonated service object using Exchange Web Services for the purpose of accessing information from an Exchange mailbox.
 
    .PARAMETER Identity
    The SMTP Address of the mailbox to be impersonated.
 
    .PARAMETER ImpersonatorAccountName
    The UserPrincipalName of the account that has Application Impersonation Rights to the Exchange environment where the user's mailbox resides.
 
    .PARAMETER ImpersonatorAccountPassword
    Password of the ImpersonatorAccountName
 
    .PARAMETER Uri
    The URL for the Exchange Web Service. If not used, will use AutoDiscover to locate the URL.
 
    .EXAMPLE
    Creates an object called $EWSService used to Impersonate jdoe@contoso.com using admin@contoso.com as the account with Impersonation rights.
 
    $EWSService = _GetImpersonator -Identity jdoe@contoso.com -AccountImpersonatorName admin@contoso.com -AccountImpersonatorPassword ********
 
    .NOTES
    - Requires Exchange Web Service Managed API 2.2
    - Supports the use of -Verbose output
 
    [IMPERSONATION PERMISSIONS]
    From the Exchange Management Shell, run the New-ManagementRoleAssignment cmdlet to add the permission to impersonate to the specified user:
    New-ManagementRoleAssignment –Name:impersonationAssignmentName –Role:ApplicationImpersonation –User:ServiceAccount
    #>

    [CmdletBinding()]
    Param (
        [String]$Identity = $(
            $PSCmdlet.ThrowTerminatingError(
                [System.Management.Automation.ErrorRecord]::new(
                    [System.Management.Automation.PSArgumentNullException]::New("Identity"),
                    "RequiredParameterNotDefined",
                    [System.Management.Automation.ErrorCategory]::InvalidArgument,
                    $Identity
                )
            )
        ),
        [System.String]$ImpersonatorAccountName = $(
            $PSCmdlet.ThrowTerminatingError(
                [System.Management.Automation.ErrorRecord]::new(
                    [System.Management.Automation.PSArgumentNullException]::New("ImpersonatorAccountName"),
                    "RequiredParameterNotDefined",
                    [System.Management.Automation.ErrorCategory]::InvalidArgument,
                    $ImpersonatorAccountName
                )
            )
        ),
        [System.String]$ImpersonatorAccountPassword = $(
            $PSCmdlet.ThrowTerminatingError(
                [System.Management.Automation.ErrorRecord]::new(
                    [System.Management.Automation.PSArgumentNullException]::New("ImpersonatorAccountPassword"),
                    "RequiredParameterNotDefined",
                    [System.Management.Automation.ErrorCategory]::InvalidArgument,
                    $ImpersonatorAccountPassword
                )
            )
        ),
        [System.URI]$Uri
    )
    BEGIN {
        TRY {
            Write-Verbose ("Validating Installation of EWS Managed API")
            $EWSRegistryPath = Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\Exchange\Web Services' | Sort-Object Name -Descending | Select-Object -First 1 -ExpandProperty Name
            $EWSInstallDirectory = (Get-ItemProperty -Path Registry::$EWSRegistryPath).'Install Directory'
            $EWSVersion = $EWSInstallDirectory.SubString(($EWSInstallDirectory.Length - 4),3)
            $EWSDLL = $EWSInstallDirectory + "Microsoft.Exchange.WebServices.dll"
    
            If (Test-Path $EWSDLL) {
                If ($EWSVersion -lt 2.0) {
                    $PSCmdlet.ThrowTerminatingError(
                        [System.Management.Automation.ErrorRecord]::New(
                            [System.ArgumentOutOfRangeException]::New("EWS Version is too old, Please install EWS Managed API 2.0 or later"),
                            "EWSVersionOutOfDate",
                            [System.Management.Automation.ErrorCategory]::InvalidResult,
                            $EWSVersion
                        )
                    )
                }
                Else {
                    Write-Verbose ("EWS Managed API 2.0 or later is INSTALLED")
                    Import-Module $EWSDLL
                }
            }
            Else {
                $PSCmdlet.ThrowTerminatingError(
                    [System.Management.Automation.ErrorRecord]::New(
                        [System.IO.FileNotFoundException]::New("Unable to find EWS Managed API DLL"),
                        "FileNotFound",
                        [System.Management.Automation.ErrorCategory]::ObjectNotFound,
                        $EWSDLL
                    )
                )
            }
        }
        CATCH {$PSCmdlet.ThrowTerminatingError($PSItem)}
    }
    PROCESS {
        TRY {
            Write-Verbose ("Building SSL Trust Policy")
            $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider
            $Compiler=$Provider.CreateCompiler()
            $Params=New-Object System.CodeDom.Compiler.CompilerParameters
            $Params.GenerateExecutable=$False
            $Params.GenerateInMemory=$True
            $Params.IncludeDebugInformation=$False
            $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null

            $TASource=@'
                namespace Local.ToolkitExtensions.Net.CertificatePolicy {
                    public class TrustAll : System.Net.ICertificatePolicy {
                        public TrustAll() {
                        }
                        public bool CheckValidationResult(System.Net.ServicePoint sp,
                            System.Security.Cryptography.X509Certificates.X509Certificate cert,
                            System.Net.WebRequest req, int problem) {
                            return true;
                        }
                    }
                }
'@


            $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
            $TAAssembly=$TAResults.CompiledAssembly
            $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
            [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll

            Write-Verbose ("Creating EWS Service Object")
            [Microsoft.Exchange.WebServices.Data.ExchangeService]$Service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1);
    
            if ([String]::IsNullOrEmpty($Uri)) {
                Write-Verbose ("Attempting to locate Service URL from Autodiscover")
                $Service.AutodiscoverUrl($Identity,{$True})
            }
            else {$Service.Url = $Uri.AbsoluteUri}

            $Service.Credentials = New-Object Net.NetworkCredential($ImpersonatorAccountName, $ImpersonatorAccountPassword)
            Write-Verbose ("Attemping to Impersonate {0}" -f $Identity)
            $Service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $Identity)
    
            #Increase the timeout to cater for large mailboxes
            $Service.Timeout = 150000
            Return $Service
        }
        CATCH {$PSCmdlet.ThrowTerminatingError($PSItem)}
    }    
}

Function Global:_GetSearchFolders {
    <#
    .SYNOPSIS
    Gets specific Exchange Search folders from an Exchange Web Service impersonation service with the option to delete the Search Folders.
 
    .DESCRIPTION
    There are times when Exchange Search folders prevent an Exchange mailbox from being migrated. Use this function to search for a specific folder by its name (partial) and delete it. This function requires a connection to Exchange using Web Services Impersonation.
 
    .PARAMETER Service
    Required parameter which contains the Exchange Web Service impersonation details.
 
    .PARAMETER NameToSearch
    Specifiy the full or partial name for the Search folder to find. Do not include any wildcard characters.
 
    .PARAMETER Delete
    Switch parameter used to delete the Search folders found.
 
    .EXAMPLE
    Search for and delete Search folders with the name like "OwaFV15" using an Exchange Web Service impersonation service called $EWSService
 
    _GetSearchFolders -Service $EWSService -NameToSearch OwaFV15 -Delete
 
    .NOTES
    - Requires Exchange Web Service Managed API 2.2
    - Use the _GetImpersonator function to create the $EWSService object
    - Supports the use of -Verbose output
 
    [IMPERSONATION PERMISSIONS]
    From the Exchange Management Shell, run the New-ManagementRoleAssignment cmdlet to add the permission to impersonate to the specified user:
    New-ManagementRoleAssignment –Name:impersonationAssignmentName –Role:ApplicationImpersonation –User:ServiceAccount
    #>

    [CmdletBinding(SupportsShouldProcess,ConfirmImpact="High")]
    Param(
        $Service = $(
            $PSCmdlet.ThrowTerminatingError(
                [System.Management.Automation.ErrorRecord]::new(
                    [System.Management.Automation.PSArgumentNullException]::New("Service"),
                    "RequiredParameterNotDefined",
                    [System.Management.Automation.ErrorCategory]::InvalidArgument,
                    $Service
                )
            )
        ),
        [String]$NameToSearch,
        [Switch]$Delete
    )
    TRY {
        Write-Verbose ("Building Folder Search View")
        [Microsoft.Exchange.WebServices.Data.FolderView]$View = New-Object Microsoft.Exchange.WebServices.Data.FolderView([System.Int32]::MaxValue)
        $View.PropertySet = New-Object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly)
        $View.PropertySet.Add([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName)
        $View.PropertySet.Add([Microsoft.Exchange.WebServices.Data.FolderSchema]::ChildFolderCount)
        $View.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep

        Write-Verbose ("Collecting Search Folders")
        [Microsoft.Exchange.WebServices.Data.FindFoldersResults]$Folders = $Service.FindFolders([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::SearchFolders,$View)
        Write-Verbose ("Found {0} Search folders" -f $Folders.Folders.count)
        $MatchingFolders = $Folders | ? {$_.DisplayName -like "$NameToSearch*"}
        If ($MatchingFolders.Count -gt 0) {
            Write-Verbose ("{0} Search folders matching '{1}*'" -f $MatchingFolders.Count,$NameToSearch)
            If ($Delete) {
                If ($PSCmdlet.ShouldProcess("$($MatchingFolders.Count) Folders","Delete Search Folders")) {
                    Foreach ($Folder in $MatchingFolders) {
                        Write-Verbose ("Deleting {0}" -f $Folder.DisplayName)
                        $Folder.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::SoftDelete)
                    }
                }
                Else {
                    Write-Warning ("User cancelled the operation!")
                    Return
                }
            }
            Else {Return $MatchingFolders}
        }
        Else {Write-Warning ("No Search folders found matching '{0}*'" -f $NameToSearch)}
    }
    CATCH {$PSCmdlet.ThrowTerminatingError($PSItem)}
}