Skip to main content

Reporting and Clearing the SyncIssues, Conflicts, LocalFailures and ServerFailures using EWS and Powershell

The SyncIssues, Conflicts, LocalFailures and ServerFailures folders in a Mailbox are folders that "contain logs and items that Microsoft Outlook has been unable to synchronize with your Microsoft Exchange Server" which are described thoroughly in http://office.microsoft.com/en-us/outlook-help/synchronization-error-folders-HP001040042.aspx.

From a operational perspective over a period of time these folders can fill up with items for a number of reasons or because of certain problems or third party products. So in this post I'm going to look at how you can use EWS to report and delete the content in these folders.

Getting Access to the Folders
There are two ways you could access these folders in EWS the first would be to do a conventional search for the displayname of the folder's using a FindFolder operation and a couple of Shallow traversals starting at the MsgFolderRoot. Or the other way which can deal with Localized foldernames and also reduces the number of operations is to use the PR_ADDITIONAL_REN_ENTRYIDS property http://msdn.microsoft.com/en-us/library/cc842240.aspx on the Root folder (or the Non_IPM_Subtree). 

The PR_ADDITIONAL_REN_ENTRYIDS is a multivalued Binary Array extended Mapi property which contains the HexEntryID for each of the Folders. To make use of these Id's you need to first convert the BinaryArray value to a String Hex value with BitConverter Class. Then use the EWS ConvertID operation to convert the Hexid to an EWSId the you can Bind to the Folder.

The Script as posted uses EWS Impersonation

If you want to customize which mailboxes it reports on then just change the Get-Mailbox line

Get-Mailbox -ResultSize Unlimited | ForEach-Object{  

eg if you want to limit to only checking one server you could use

Get-Mailbox -ResultSize Unlimited -Server servernameblah | ForEach-Object{

You could do similar with other filter properties such as Database or OU

The script produces a CSV report of the Size and Item Count of each of these folders, if you want to delete all the Items within these folders you need to add one line to the script that will delete the Content of the folder that you want to effect eg for the SyncIssueFolder add


$SyncIssueFolder.Empty([Microsoft.Exchange.WebServices.Data.DeleteMode]::HardDelete, $true);

after the size report section. You need to add a separate Empty for Each folder you want to clear. On 2007 you can't use the Empty method so would need add code to enumerate the Items so you can do a batch delete I covered this in the Folders HowTo http://gsexdev.blogspot.com.au/2012/01/ews-managed-api-and-powershell-how-to_23.html

I've put a download of this script here the code itself looks like

  1. $rptCollection = @()  
  2. ## Load Managed API dll    
  3. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\1.2\Microsoft.Exchange.WebServices.dll"    
  4.     
  5. ## Set Exchange Version    
  6. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2    
  7.     
  8. ## Create Exchange Service Object    
  9. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  10.     
  11. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  12.     
  13. #Credentials Option 1 using UPN for the windows Account    
  14. $psCred = Get-Credential    
  15. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  16. $service.Credentials = $creds       
  17.     
  18. #Credentials Option 2    
  19. #service.UseDefaultCredentials = $true    
  20.     
  21. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  22.     
  23. ## Code From http://poshcode.org/624  
  24. ## Create a compilation environment  
  25. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  26. $Compiler=$Provider.CreateCompiler()  
  27. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  28. $Params.GenerateExecutable=$False  
  29. $Params.GenerateInMemory=$True  
  30. $Params.IncludeDebugInformation=$False  
  31. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  32.   
  33. $TASource=@' 
  34.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  35.     public class TrustAll : System.Net.ICertificatePolicy { 
  36.       public TrustAll() {  
  37.       } 
  38.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  39.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  40.         System.Net.WebRequest req, int problem) { 
  41.         return true; 
  42.       } 
  43.     } 
  44.   } 
  45. '@   
  46. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  47. $TAAssembly=$TAResults.CompiledAssembly  
  48.   
  49. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  50. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  51. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  52.   
  53. ## end code from http://poshcode.org/624  
  54.     
  55. ## Set the URL of the CAS (Client Access Server) to use two options are availbe to use Autodiscover to find the CAS URL or Hardcode the CAS to use    
  56.     
  57.      
  58.       
  59.       
  60.   
  61. Get-Mailbox -ResultSize Unlimited | ForEach-Object{     
  62.     $MailboxName = $_.PrimarySMTPAddress.ToString()    
  63.     "Processing Mailbox : " + $MailboxName    
  64.     if($service.url -eq $null){    
  65.         ## Set the URL of the CAS (Client Access Server) to use two options are availbe to use Autodiscover to find the CAS URL or Hardcode the CAS to use    
  66.     
  67.         #CAS URL Option 1 Autodiscover    
  68.         $service.AutodiscoverUrl($MailboxName,{$true})    
  69.         "Using CAS Server : " + $Service.url     
  70.             
  71.         #CAS URL Option 2 Hardcoded    
  72.         #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  73.         #$service.Url = $uri      
  74.     }    
  75.     
  76.     $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  77.     $PR_MESSAGE_SIZE_EXTENDED = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(3592,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Long);    
  78.   
  79.     $PR_ADDITIONAL_REN_ENTRYIDS = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x36D8, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::BinaryArray);   
  80.     $Propset = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)  
  81.     $Propset.add($PR_ADDITIONAL_REN_ENTRYIDS)  
  82.     $Propset.add($PR_MESSAGE_SIZE_EXTENDED)  
  83.     #Sync Folders  
  84.   
  85.     $folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root,$MailboxName)     
  86.     $RootFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid,$Propset)  
  87.     $objVal = $null  
  88.   
  89.     function ConvertFolderid($hexId){  
  90.         $aiItem = New-Object Microsoft.Exchange.WebServices.Data.AlternateId    
  91.         $aiItem.Mailbox = $MailboxName    
  92.         $aiItem.UniqueId = $hexId  
  93.         $aiItem.Format = [Microsoft.Exchange.WebServices.Data.IdFormat]::HexEntryId;    
  94.         return $service.ConvertId($aiItem, [Microsoft.Exchange.WebServices.Data.IdFormat]::EWSId)   
  95.     }  
  96.   
  97.     if($RootFolder.TryGetProperty($PR_ADDITIONAL_REN_ENTRYIDS,[ref]$objVal)){  
  98.         if($objVal[0] -ne $null){  
  99.             $rptobj = "" | Select MailboxName,SyncIssuesCount,SyncIssuesSize,ConflictsCount,ConflictsSize,LocalFailuresCount,LocalFailuresSize,ServerFailuresCount,ServerFailuresSize  
  100.             $rptobj.MailboxName = $MailboxName  
  101.             $cfid = ConvertFolderid([System.BitConverter]::ToString($objVal[0]).Replace("-",""))  
  102.             if($cfid.UniqueId -ne $null){  
  103.             $ConflictsFolderId = new-object Microsoft.Exchange.WebServices.Data.FolderId($cfid.UniqueId.ToString())  
  104.             $ConflictsFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$ConflictsFolderId,$Propset)  
  105.             $ConflictsFolder.DisplayName  
  106.             $folderSize = $null  
  107.             if($ConflictsFolder.TryGetProperty($PR_MESSAGE_SIZE_EXTENDED,[ref]$folderSize)){  
  108.                 $rptobj.ConflictsCount = $ConflictsFolder.TotalCount  
  109.                 $rptobj.ConflictsSize = [Math]::Round($folderSize/1MB)   
  110.                 "ItemCount  : " + $ConflictsFolder.TotalCount  
  111.                 "FolderSize : " + [Math]::Round($folderSize/1MB) + " MB"  
  112.             }  
  113.             $siId = ConvertFolderid([System.BitConverter]::ToString($objVal[1]).Replace("-",""))  
  114.             $SyncIssuesFolderID = new-object Microsoft.Exchange.WebServices.Data.FolderId($siId.UniqueId.ToString())  
  115.             $SyncIssueFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$SyncIssuesFolderID,$Propset)  
  116.             $SyncIssueFolder.DisplayName  
  117.             if($SyncIssueFolder.TryGetProperty($PR_MESSAGE_SIZE_EXTENDED,[ref]$folderSize)){  
  118.                 $rptobj.SyncIssuesCount = $SyncIssueFolder.TotalCount  
  119.                 $rptobj.SyncIssuesSize = [Math]::Round($folderSize/1MB)  
  120.                 "ItemCount  : " + $SyncIssueFolder.TotalCount  
  121.                 "FolderSize : " + [Math]::Round($folderSize/1MB) + " MB"  
  122.             }  
  123.             $lcId = ConvertFolderid([System.BitConverter]::ToString($objVal[2]).Replace("-",""))  
  124.             $localFailureId = new-object Microsoft.Exchange.WebServices.Data.FolderId($lcId.UniqueId.ToString())  
  125.             $localFailureFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$localFailureId,$Propset)  
  126.             $localFailureFolder.DisplayName  
  127.             if($localFailureFolder.TryGetProperty($PR_MESSAGE_SIZE_EXTENDED,[ref]$folderSize)){  
  128.                 $rptobj.LocalFailuresCount = $localFailureFolder.TotalCount  
  129.                 $rptobj.LocalFailuresSize = [Math]::Round($folderSize/1MB)  
  130.                 "ItemCount  : " + $localFailureFolder.TotalCount  
  131.                 "FolderSize : " + [Math]::Round($folderSize/1MB) + " MB"  
  132.             }  
  133.             $sfid = ConvertFolderid([System.BitConverter]::ToString($objVal[3]).Replace("-",""))  
  134.             $ServerFailureId = new-object Microsoft.Exchange.WebServices.Data.FolderId($sfid.UniqueId.ToString())  
  135.             $ServerFailureFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$ServerFailureId,$Propset)  
  136.             $ServerFailureFolder.DisplayName  
  137.             if($ServerFailureFolder.TryGetProperty($PR_MESSAGE_SIZE_EXTENDED,[ref]$folderSize)){  
  138.                 $rptobj.ServerFailuresCount = $ServerFailureFolder.TotalCount  
  139.                 $rptobj.ServerFailuresSize = [Math]::Round($folderSize/1MB)  
  140.                 "ItemCount  : " + $ServerFailureFolder.TotalCount  
  141.                 "FolderSize : " + [Math]::Round($folderSize/1MB) + " MB"  
  142.             }  
  143.             $rptCollection += $rptobj  
  144.             }  
  145.         }  
  146.     }  
  147. }  
  148. $rptCollection  
  149. $rptCollection | Export-Csv -NoTypeInformation -Path c:\temp\SyncFolderReport.csv    

Popular posts from this blog

Testing and Sending email via SMTP using Opportunistic TLS and oAuth in Office365 with PowerShell

As well as EWS and Remote PowerShell (RPS) other mail protocols POP3, IMAP and SMTP have had OAuth authentication enabled in Exchange Online (Official announcement here ). A while ago I created  this script that used Opportunistic TLS to perform a Telnet style test against a SMTP server using SMTP AUTH. Now that oAuth authentication has been enabled in office365 I've updated this script to be able to use oAuth instead of SMTP Auth to test against Office365. I've also included a function to actually send a Message. Token Acquisition  To Send a Mail using oAuth you first need to get an Access token from Azure AD there are plenty of ways of doing this in PowerShell. You could use a library like MSAL or ADAL (just google your favoured method) or use a library less approach which I've included with this script . Whatever way you do this you need to make sure that your application registration  https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-

The MailboxConcurrency limit and using Batching in the Microsoft Graph API

If your getting an error such as Application is over its MailboxConcurrency limit while using the Microsoft Graph API this post may help you understand why. Background   The Mailbox  concurrency limit when your using the Graph API is 4 as per https://docs.microsoft.com/en-us/graph/throttling#outlook-service-limits . This is evaluated for each app ID and mailbox combination so this means you can have different apps running under the same credentials and the poor behavior of one won't cause the other to be throttled. If you compared that to EWS you could have up to 27 concurrent connections but they are shared across all apps on a first come first served basis. Batching Batching in the Graph API is a way of combining multiple requests into a single HTTP request. Batching in the Exchange Mail API's EWS and MAPI has been around for a long time and its common, for email Apps to process large numbers of smaller items for a variety of reasons.  Batching in the Graph is limited to a m

How to test SMTP using Opportunistic TLS with Powershell and grab the public certificate a SMTP server is using

Most email services these day employ Opportunistic TLS when trying to send Messages which means that wherever possible the Messages will be encrypted rather then the plain text legacy of SMTP.  This method was defined in RFC 3207 "SMTP Service Extension for Secure SMTP over Transport Layer Security" and  there's a quite a good explanation of Opportunistic TLS on Wikipedia  https://en.wikipedia.org/wiki/Opportunistic_TLS .  This is used for both Server to Server (eg MTA to MTA) and Client to server (Eg a Message client like Outlook which acts as a MSA) the later being generally Authenticated. Basically it allows you to have a normal plain text SMTP conversation that is then upgraded to TLS using the STARTTLS verb. Not all servers will support this verb so if its not supported then a message is just sent as Plain text. TLS relies on PKI certificates and the administrative issue s that come around certificate management like expired certificates which is why I wrote th
All sample scripts and source code is provided by for illustrative purposes only. All examples are untested in different environments and therefore, I cannot guarantee or imply reliability, serviceability, or function of these programs.

All code contained herein is provided to you "AS IS" without any warranties of any kind. The implied warranties of non-infringement, merchantability and fitness for a particular purpose are expressly disclaimed.