Sunday, July 01, 2012

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    

2 comments:

Anonymous said...

Hi Glen,
I came acroos this script when I was searching how to delete mails in "Sync Issues" folder. as per microsoft this is client side folder and cannot be handled from server side. http://support.microsoft.com/kb/2686541

I just want know if this same script can be used fro Exchange 2007 SP3

I am not programmer, Iam Exchange Admin.

your comment will be really helpful for me as my client is chasing me on this like hell.

Saurabh Deshapnde

[saurabh.deshpande@orange.com]

Mathew John said...
This comment has been removed by the author.