Recover public folder deleted items with EWS Managed API and Exchange Online PowerShell

As we all know, when you want to recover public folder deleted items, you use the Outlook client - Recover deleted items option . However, this option is visible only if the public folder is hosting email items ("IPF.Note").

For scenarios where you plan to recover calendar, note, journal, task or contact items, you can use either a client, like MFCMAPI (more information, here: or use another protocol, like EWS, to achieve the same thing, in a programmatic way. This is mostly helpful when you don't use Outlook at all (thus, MFCMAPI is not an option) or when you have to recover a large number of deleted items. Enjoy!



 DISCLAIMER:  This application is a sample application. The sample is provided "as is" without warranty of any kind. Microsoft further 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 samples remains with you. in no event shall Microsoft or its suppliers 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 samples, even if Microsoft has been advised of the possibility of such damages). Because some states do not allow the exclusion or limitation of liability for consequential or incidental damages, the above limitation may not apply to you. 

# The script requires EWS Managed API 2.2, which can be downloaded here: 
# Make sure the Import-Module command matches the Microsoft.Exchange.WebServices.dll location of EWS Managed API, chosen during the installation

$PF = Read-Host -Prompt "Public Folder identity (e.g. \PF1, \PF1\SubPF1)"

Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"

$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -ArgumentList Exchange2013_SP1

#Provide the credentials 
$credential = Get-Credential -Message "Type the credentials of the mailbox that you intend to use to connect to the public folder"

$service.Credentials = new-object Microsoft.Exchange.WebServices.Data.WebCredentials -ArgumentList $credential.UserName, $credential.GetNetworkCredential().Password

#Exchange Online URL
$service.Url= new-object Uri("")

function Convert($c)


$aiItem = New-Object Microsoft.Exchange.WebServices.Data.AlternatePublicFolderId 

$aiItem.FolderId = $c

$aiItem.Format = [Microsoft.Exchange.WebServices.Data.IdFormat]::HexEntryId       

$convertedId = $service.ConvertId($aiItem, [Microsoft.Exchange.WebServices.Data.IdFormat]::EwsId)  

$folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId($convertedId.FolderId)

return $folderid;


$a = Get-PublicFolder $PF

$SourcePF = Convert($a.EntryId)

$PFDumpster = Convert($a.DumpsterEntryId)

$ItemView = new-object Microsoft.Exchange.WebServices.Data.ItemView(1000)



$FindItemResults = $service.FindItems($PFDumpster,$ItemView)

write-host "$($FindItemResults.TotalCount) item(s) has/have been found in the Dumpster folder" -ForegroundColor White

foreach($Item in $FindItemResults.Items)

if ($Item.ItemClass -match "IPM.Note" -or $Item.ItemClass -match "IPM.StickyNote" -or $Item.ItemClass -match "IPM.Activity")
{ $Message = [Microsoft.Exchange.WebServices.Data.EmailMessage]::Bind($service,$Item.Id); $Message.Move($SourcePF); continue; }

if ($Item.ItemClass -match "IPM.Appointment")
{ $Message = [Microsoft.Exchange.WebServices.Data.Appointment]::Bind($service,$Item.Id); $Message.Move($SourcePF); continue; }

if ($Item.ItemClass -match "IPM.Contact")
{ $Message = [Microsoft.Exchange.WebServices.Data.Contact]::Bind($service,$Item.Id); $Message.Move($SourcePF); continue; }

if ($Item.ItemClass -match "IPM.Task")
{ $Message = [Microsoft.Exchange.WebServices.Data.Task]::Bind($service,$Item.Id); $Message.Move($SourcePF); continue; }


$ItemView.offset += $FindItemResults.Items.Count

} while($FindItemResults.MoreAvailable -eq $true)

      else   { 
               Write-Host "The Dumpster folder is empty!" -ForegroundColor Yellow 


Note: In order to run the script above, you have to copy paste it into a Notepad file and save it with the extension ".ps1". Then, you have to connect to Exchange Online with PowerShell and run it: