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

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 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

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"    
  5. ## Set Exchange Version    
  6. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2    
  8. ## Create Exchange Service Object    
  9. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  11. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  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       
  18. #Credentials Option 2    
  19. #service.UseDefaultCredentials = $true    
  21. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  23. ## Code From  
  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  
  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  
  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  
  53. ## end code from  
  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    
  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    
  67.         #CAS URL Option 1 Autodiscover    
  68.         $service.AutodiscoverUrl($MailboxName,{$true})    
  69.         "Using CAS Server : " + $Service.url     
  71.         #CAS URL Option 2 Hardcoded    
  72.         #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  73.         #$service.Url = $uri      
  74.     }    
  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);    
  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  
  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  
  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.     }  
  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

Using the MSAL (Microsoft Authentication Library) in EWS with Office365

Last July Microsoft announced here they would be disabling basic authentication in EWS on October 13 2020 which is now a little over a year away. Given the amount of time that has passed since the announcement any line of business applications or third party applications that you use that had been using Basic authentication should have been modified or upgraded to support using oAuth. If this isn't the case the time to take action is now. When you need to migrate a .NET app or script you have using EWS and basic Authentication you have two Authentication libraries you can choose from ADAL - Azure AD Authentication Library (uses the v1 Azure AD Endpoint) MSAL - Microsoft Authentication Library (uses the v2 Microsoft Identity Platform Endpoint) the most common library you will come across in use is the ADAL libraries because its been around the longest, has good support across a number of languages and allows complex authentications scenarios with support for SAML etc. The

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 .  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

A walk-though using the Graph API Mailbox reports in Powershell

Quite recently the Reporting side of the Graph API has moved in GA from beta, there are quite a number of reports that can be run across various Office365 surfaces but in this post I'm going to focus on the Mailbox related ones. Accessing Office365 Reports using Powershell is nothing new and has been available in the previous reporting endpoint however from the end of January many of these cmdlets are now being depreciated in favour of the Graph API . Prerequisites  In comparison to using the Remote PowerShell cmdlets where only the correct Office365 Admin permissions where needed, to use the new Graph API reports endpoint you need to use OAuth for authentication so this requires an Application Registration  that is then given the correct oAuth Grants to use the Reports EndPoin
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.