Tuesday, March 27, 2012

EWS Managed API and Powershell How-To Series Part 6 FreeBusy and OOF Operations

In this post I'm going to continue looking at the special operations in EWS that allows you to get and set the unconventional data that is stored in an Exchange mailbox like the FreeBusy information and the Out of Office Message. First off lets look at the FreeBusyTime.
 

Getting Freebusy time

Freebusy information is one of the more useful calendar features that Exchange offers in EWS the ability to access the freebusy information is provided by the GetUserAvailiblity operation. This operation can be useful in a number of scenarios especially when you may want to query a large number of calendars. One example is a freebusy board or meeting room website

When you make a FreeBusy request there are two constraints on the request that are outlined in http://technet.microsoft.com/en-us/library/hh310374.aspx  "By default, Exchange 2007 accepts availability requests for 42 days of free/busy information and Exchange 2010 may request 62 days of free/busy information. If the request exceeds the default 42 limit imposed by Exchange 2007, the request will fail." The other constraint in the number of users you can query at one time is limited to 100 per call.

The detail level of information that is returned by this operation is also controlled by the value of RequestedFreeBusyView that you set. Essentially two different data-sets could be returned by this operation the first is the MergedFreeBusyStatus which are the FreeBusy Slots from a mailbox by default in 30 minutes increments. The second dataset is a detailed array of calendar appointment during the freebusy period if you have requested Calendar Details and you have enough rights on the Calendar to see Subject and Location information (Otherwise you will just get Start and EndTimes).

Lets look at an example of getting the freebusy time with the EWS Managed API for one user is this example we look at the CalendarDetails array to show the information that can be returned.

  1. $StartTime = [DateTime]::Parse([DateTime]::Now.ToString("yyyy-MM-dd 0:00"))  
  2. $EndTime = $StartTime.AddDays(7)  
  3.   
  4. $drDuration = new-object Microsoft.Exchange.WebServices.Data.TimeWindow($StartTime,$EndTime)  
  5. $AvailabilityOptions = new-object Microsoft.Exchange.WebServices.Data.AvailabilityOptions  
  6. $AvailabilityOptions.RequestedFreeBusyView = [Microsoft.Exchange.WebServices.Data.FreeBusyViewType]::DetailedMerged  
  7.   
  8. $listtype = ("System.Collections.Generic.List"+'`'+"1") -as "Type"
  9. $listtype = $listtype.MakeGenericType("Microsoft.Exchange.WebServices.Data.AttendeeInfo" -as "Type")
  10. $Attendeesbatch = [Activator]::CreateInstance($listtype)
  11.   
  12. $Attendee = new-object Microsoft.Exchange.WebServices.Data.AttendeeInfo("user@domain.com")  
  13. $Attendeesbatch.add($Attendee)  
  14.   
  15. $availresponse = $service.GetUserAvailability($Attendeesbatch,$drDuration,[Microsoft.Exchange.WebServices.Data.AvailabilityData]::FreeBusy,$AvailabilityOptions)  
  16. foreach($avail in $availresponse.AttendeesAvailability){  
  17.     foreach($cvtEnt in $avail.CalendarEvents){  
  18.         "Start : " + $cvtEnt.StartTime  
  19.         "End : " + $cvtEnt.EndTime  
  20.         "Subject : " + $cvtEnt.Details.Subject  
  21.         "Location : " + $cvtEnt.Details.Location  
  22.         ""  
  23.     }  
  24. }  
One useful example is to do this for a Group of users so first you will need a Distribution Group and we will then use the ExandGroup EWS operation to get all the members of that group which will give us all the Email address's of the users that we will then get the FreeBusy information for. You also need some code to ensure you can relate the information in the CalendarDetails array to that of the request because by default in this array there is no information relating the recipient this data belongs to. (Not you can actually use the Distribution Groups address instead of doing the expand but if you do this the results do get merged into one Entry which is hard to separate).

  1. $GroupAddress = "testinggroup@domain.com"  
  2. $StartTime = [DateTime]::Parse([DateTime]::Now.ToString("yyyy-MM-dd 0:00"))  
  3. $EndTime = $StartTime.AddDays(7)  
  4.   
  5. $drDuration = new-object Microsoft.Exchange.WebServices.Data.TimeWindow($StartTime,$EndTime)  
  6. $AvailabilityOptions = new-object Microsoft.Exchange.WebServices.Data.AvailabilityOptions  
  7. $AvailabilityOptions.RequestedFreeBusyView = [Microsoft.Exchange.WebServices.Data.FreeBusyViewType]::DetailedMerged  
  8.   
  9. $listtype = ("System.Collections.Generic.List"+'`'+"1") -as "Type"
  10. $listtype = $listtype.MakeGenericType("Microsoft.Exchange.WebServices.Data.AttendeeInfo" -as "Type")
  11. $Attendeesbatch = [Activator]::CreateInstance($listtype)
  12.   
  13. $GroupMembers = $service.ExpandGroup($GroupAddress)  
  14. $adrHash = @{}  
  15. $adcnt = 0  
  16. foreach($Address in $GroupMembers.Members){  
  17.     $Attendee = new-object Microsoft.Exchange.WebServices.Data.AttendeeInfo($Address.Address)  
  18.     $Attendeesbatch.add($Attendee)  
  19.     $adrHash.add($adcnt,$Address.Address)  
  20.     $adcnt++  
  21.     $Address.Address  
  22. }  
  23. $adcnt = 0  
  24. $availresponse = $service.GetUserAvailability($Attendeesbatch,$drDuration,[Microsoft.Exchange.WebServices.Data.AvailabilityData]::FreeBusy,$AvailabilityOptions)  
  25. foreach($avail in $availresponse.AttendeesAvailability){  
  26.       
  27.     foreach($cvtEnt in $avail.CalendarEvents){  
  28.         "Mailbox : " + $adrHash[$adcnt]  
  29.         "Start : " + $cvtEnt.StartTime  
  30.         "End : " + $cvtEnt.EndTime  
  31.         "Subject : " + $cvtEnt.Details.Subject  
  32.         "Location : " + $cvtEnt.Details.Location  
  33.         ""  
  34.     }  
  35.     $adcnt++  
  36. }  
Out of Office Information

EWS provides access to the Out of Office information via GetUserOofSettings and SetUserOofSettings  Operations. Or one other way of displaying the OOF setting for a user which doesn't require rights on a users mailbox is to use MailTips. Using the OOF via GetUserOofSettings/SetUserOofSettings setting is pretty easy lets looks at the basics of Getting and Displaying the OOF settings.

  1. $oofSetting = $service.GetUserOofSettings("user@domain.com")  
  2. "OOF State : " + $oofSetting.State  
  3. "InternalReply : " + $oofSetting.InternalReply  
  4. "ExternalReply : " + $oofSetting.ExternalReply  
  5. "AllowExternalOof : " + $oofSetting.AllowExternalOof  
To set the OOF State and or the Message property you should first get the current setting modify the property you want to modify and then update the OOF.

eg to set the OOF of the currently logged on user you could use the following code.

  1. $MailboxName = "user@domain.com"  
  2. $oofSetting = $service.GetUserOofSettings($MailboxName)  
  3. $oofSetting.State.ToString()  
  4. $oofSetting.InternalReply = new-object Microsoft.Exchange.WebServices.Data.OofReply("Test 123")  
  5. $oofSetting.ExternalReply.ToString()  
  6. $oofSetting.State = [Microsoft.Exchange.WebServices.Data.OofState]::Enabled  
  7. $service.SetUserOofSettings($MailboxName$oofSetting);   

An example of using MailTips to get the OOF needs a little more code because there are no methods in the EWS Managed API to make use of the MailTips operations you need to use some Raw XML instead.

  1. $MailboxName = "user@domain.com"  
  2.   
  3. $expRequest = @"   
  4. <?xml version="1.0" encoding="utf-8"?>   
  5. <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">   
  6. <soap:Header><RequestServerVersion Version="Exchange2010_SP1" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" />   
  7. </soap:Header>   
  8. <soap:Body>   
  9. <GetMailTips xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">   
  10. <SendingAs>   
  11. <EmailAddress xmlns="http://schemas.microsoft.com/exchange/services/2006/types">$MailboxName</EmailAddress>   
  12. </SendingAs>   
  13. <Recipients>   
  14. "@     
  15.     
  16. foreach($mbMailbox in $Mailboxes){     
  17.     $expRequest = $expRequest + "<Mailbox xmlns=`"http://schemas.microsoft.com/exchange/services/2006/types`"><EmailAddress>$mbMailbox</EmailAddress></Mailbox>"      
  18. }     
  19.     
  20. $expRequest = $expRequest + "</Recipients><MailTipsRequested>OutOfOfficeMessage</MailTipsRequested></GetMailTips></soap:Body></soap:Envelope>"    
  21. $mbMailboxFolderURI = New-Object System.Uri($service.url)     
  22. $wrWebRequest = [System.Net.WebRequest]::Create($mbMailboxFolderURI)     
  23. $wrWebRequest.KeepAlive = $false;     
  24. $wrWebRequest.Headers.Set("Pragma""no-cache");     
  25. $wrWebRequest.Headers.Set("Translate""f");     
  26. $wrWebRequest.Headers.Set("Depth""0");     
  27. $wrWebRequest.ContentType = "text/xml";     
  28. $wrWebRequest.ContentLength = $expRequest.Length;     
  29. $wrWebRequest.Timeout = 60000;     
  30. $wrWebRequest.Method = "POST";     
  31. $wrWebRequest.Credentials = $cred    
  32. $bqByteQuery = [System.Text.Encoding]::ASCII.GetBytes($expRequest);     
  33. $wrWebRequest.ContentLength = $bqByteQuery.Length;     
  34. $rsRequestStream = $wrWebRequest.GetRequestStream();     
  35. $rsRequestStream.Write($bqByteQuery, 0, $bqByteQuery.Length);     
  36. $rsRequestStream.Close();     
  37. $wrWebResponse = $wrWebRequest.GetResponse();     
  38. $rsResponseStream = $wrWebResponse.GetResponseStream()     
  39. $sr = new-object System.IO.StreamReader($rsResponseStream);     
  40. $rdResponseDocument = New-Object System.Xml.XmlDocument     
  41. $rdResponseDocument.LoadXml($sr.ReadToEnd());     
  42. $RecipientNodes = @($rdResponseDocument.getElementsByTagName("t:RecipientAddress"))     
  43. $Datanodes = @($rdResponseDocument.getElementsByTagName("t:OutOfOffice"))     
  44. for($ic=0;$ic -lt $RecipientNodes.length;$ic++){     
  45.     if($Datanodes[$ic].ReplyBody.Message -eq ""){     
  46.         $RecipientNodes[$ic].EmailAddress + " : In the Office"    
  47.     }     
  48.     else{     
  49.         $RecipientNodes[$ic].EmailAddress + " : Out of Office"    
  50.     }     
  51. }     









Thursday, March 15, 2012

How To Series Sample 5 : Clearing the Suggested Contacts with EWS and Powershell

In Outlook 2010 "Suggested Contacts" is feature that automatically creates a Contact for each address you send an email to . If you need clear this folder for any reason you can do this in EWS in 2010 using the new Empty operation which allows you to easily delete all the Items within a folder (without the need to use a batch operations). Here's a example of how to clear the Suggested Contacts folder note its deletes stuff so be careful. For more information about the AutoComplete cache see http://gsexdev.blogspot.com.au/2013/10/exporting-suggested-contacts-owa-auto.html

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

  1. ## Load Managed API dll  
  2. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll"  
  3.   
  4. ## Set Exchange Version  
  5. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1  
  6.   
  7. ## Create Exchange Service Object  
  8. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)  
  9.   
  10. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials  
  11.   
  12. #Credentials Option 1 using UPN for the windows Account  
  13. $creds = New-Object System.Net.NetworkCredential("user@domain","password")   
  14. $service.Credentials = $creds      
  15.   
  16. #Credentials Option 2  
  17. #service.UseDefaultCredentials = $true  
  18.   
  19. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates  
  20.   
  21. [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}  
  22.   
  23. ## 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  
  24.   
  25. #CAS URL Option 1 Autodiscover  
  26. $service.AutodiscoverUrl("user@domain",{$true})  
  27. "Using CAS Server : " + $Service.url   
  28.    
  29. #CAS URL Option 2 Hardcoded  
  30.   
  31. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"  
  32. #$service.Url = $uri    
  33.   
  34. ## Optional section for Exchange Impersonation  
  35.   
  36. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, "email@domain.com")  
  37. $MailboxName = "user@domain"  
  38.   
  39. $folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)   
  40.   
  41. $fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1)  
  42. $SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,"Suggested Contacts")  
  43. $findFolderResults = $service.FindFolders($folderid,$SfSearchFilter,$fvFolderView)  
  44.   
  45. if($findFolderResults.Folders.Count -gt 0){  
  46.     "Emptying Suggested Contacts"  
  47.     $findFolderResults.Folders[0].Empty([Microsoft.Exchange.WebServices.Data.DeleteMode]::HardDelete, $true);  
  48. }  
  49. else{  
  50.     "Folder not found"  
  51. }  


Monday, March 12, 2012

EWS Managed API and Powershell How-To Series Part 5 Delegate Operations

This is part 5 of my ongoing EWS and Powershell How-To series in this part I'm going to be looking at the special EWS operations starting with the Delegate operations in this post.

The Tale of Two Delegates (it is the 200th Anniversary of dickens)

When you talk about delegates with EWS it can be a little confusing because it could be your talking about accessing another mailbox as a delegate for example . For example to access another mailboxes Inbox as a Delegate you would use

  1. $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxName)     
  2. $Inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)   
The other type of Delegates are the assigning of Mailbox permissions via the use of the Delegate Operations or to put it pictorially this function in Outlook

Delegates are always the best way of assigning shared rights to users Mailbox and Calendar folders because it always provides accountability to the user eg a user can always go in and see whom their delegates are where as the alternative of assigning rights via Add-MailboxPermission or modifying the folder permission directly may not be a easily visible and accountable to the user.

So first lets look at the code that can be used to get the delegates in a Mailbox

  1. #Get Delegates for Mailbox  
  2. $delegates = $service.getdelegates($MailboxName,$true)  
  3. #Enumerate Delegates  
  4. foreach($Delegate in $delegates.DelegateUserResponses){  
  5.     $Delegate.DelegateUser  
  6. }  
Pretty simple but the response itself is first worth looking at because it contains two distinct data elements, All the information about delegate permissions is held within the DelegateUserResponses collection and the information that controls the Radio buttons under the "Delivery meeting requests addressed to me and responses..." in the above screen shoot is held in a separate property called MeetingRequestsDeliveryScope. This is important when it comes to adding a new delegate.

Adding a New Delegate

  As I've just talked about when you go to add a new delegate you first should request the existing delegates on the Mailbox so you can determine the configuration of the MeetingRequestsDeliveryScope (Otherwise you will risk changing the current configuration) and you can also check if that delegate already exists and just needs to be modified instead. Lets look at a example of adding a delegate to a Mailbox this script will get the existing delegates first check to see if the delegate you want to add already existing and if not adds that delegate and make sure this delegate has Reviewer rights to the Inbox.

  1. $delegatetoAdd = "bob.test@domain.com"  
  2. $exists = $false  
  3.   
  4. $delegates = $service.getdelegates($MailboxName,$true)  
  5. foreach($Delegate in $delegates.DelegateUserResponses){  
  6.     $Delegate.DelegateUser.UserId.PrimarySmtpAddress  
  7.     if($Delegate.DelegateUser.UserId.PrimarySmtpAddress.ToLower() -eq $delegatetoAdd.ToLower()){  
  8.         #Change Inbox permissions  
  9.         $Delegate.DelegateUser.Permissions.InboxFolderPermissionLevel = [Microsoft.Exchange.WebServices.Data.DelegateFolderPermissionLevel]::Reviewer  
  10.         $service.UpdateDelegates($MailboxName,$delegates.MeetingRequestsDeliveryScope,$Delegate.DelegateUser)  
  11.         $exists = $true  
  12.         "Delegate Updated"  
  13.     }  
  14. }   
  15. if($exists -eq $false){  
  16.     $dgUser = new-object Microsoft.Exchange.WebServices.Data.DelegateUser($delegatetoAdd)  
  17.     $dgUser.ViewPrivateItems = $false  
  18.     $dgUser.ReceiveCopiesOfMeetingMessages = $false  
  19.     $dgUser.Permissions.CalendarFolderPermissionLevel = [Microsoft.Exchange.WebServices.Data.DelegateFolderPermissionLevel]::Editor  
  20.     $dgUser.Permissions.InboxFolderPermissionLevel = [Microsoft.Exchange.WebServices.Data.DelegateFolderPermissionLevel]::Reviewer  
  21.     $dgArray = new-object Microsoft.Exchange.WebServices.Data.DelegateUser[] 1  
  22.     $dgArray[0] = $dgUser  
  23.     $service.AddDelegates($MailboxName,$delegates.MeetingRequestsDeliveryScope,$dgArray)  
  24.     "Delegate Added"  
  25. }  

Removing a Delegate

To remove a delegate its pretty similar to adding a new one you should first get the existing delegates then use the removedelegates method eg 

  1. $delegateToRemove = "bob.test@domain.com"  
  2. $delegates = $service.getdelegates($MailboxName,$true)  
  3. foreach($Delegate in $delegates.DelegateUserResponses){  
  4.     $Delegate.DelegateUser.UserId.PrimarySmtpAddress  
  5.     if($Delegate.DelegateUser.UserId.PrimarySmtpAddress.ToLower() -eq $delegatetoAdd.ToLower()){  
  6.         $service.RemoveDelegates($MailboxName,$delegateToRemove)  
  7.         "Removing Delegate"  
  8.     }  
  9. }  
Reporting on Delegates

One of the more useful things to do with delegates is produce a report of the currently configured delegates on a mailbox . This can come in use if you want to send a report to a user whom may have no idea who currently has access to their mailbox. Here's a sample of producing a HTML report of the delegates on a mailbox.

  1. $rptCollection = @()  
  2. $delegates = $service.getdelegates($MailboxName,$true)  
  3. foreach($Delegate in $delegates.DelegateUserResponses){  
  4.     $rptObj = "" | select EmailAddress,Inbox,Calendar,Contacts,Tasks,Notes,Journal,MeetingMessages,ViewPrivateItems  
  5.     $rptObj.EmailAddress = $Delegate.DelegateUser.UserId.PrimarySmtpAddress  
  6.     $rptObj.Inbox = $Delegate.DelegateUser.Permissions.InboxFolderPermissionLevel  
  7.     $rptObj.Calendar = $Delegate.DelegateUser.Permissions.CalendarFolderPermissionLevel  
  8.     $rptObj.Contacts = $Delegate.DelegateUser.Permissions.ContactsFolderPermissionLevel  
  9.     $rptObj.Tasks = $Delegate.DelegateUser.Permissions.TasksFolderPermissionLevel  
  10.     $rptObj.Notes = $Delegate.DelegateUser.Permissions.NotesFolderPermissionLevel  
  11.     $rptObj.Journal = $Delegate.DelegateUser.Permissions.JournalFolderPermissionLevel  
  12.     $rptObj.ViewPrivateItems = $Delegate.DelegateUser.ViewPrivateItems  
  13.     $rptObj.MeetingMessages = $Delegate.DelegateUser.ReceiveCopiesOfMeetingMessages  
  14.     $rptCollection += $rptObj  
  15. }  
  16.   
  17. $tableStyle = @" 
  18. <style> 
  19. BODY{background-color:white;} 
  20. TABLE{border-width: 1px; 
  21.   border-style: solid; 
  22.   border-color: black; 
  23.   border-collapse: collapse; 
  24. } 
  25. TH{border-width: 1px; 
  26.   padding: 10px; 
  27.   border-style: solid; 
  28.   border-color: black; 
  29.   background-color:#66CCCC 
  30. } 
  31. TD{border-width: 1px; 
  32.   padding: 2px; 
  33.   border-style: solid; 
  34.   border-color: black; 
  35.   background-color:white 
  36. } 
  37. </style> 
  38. "@  
  39.     
  40. $body = @" 
  41. <p style="font-size:25px;family:calibri;color:#ff9100">  
  42. $TableHeader  
  43. </p>  
  44. "@  
  45.   
  46. $rptCollection | ConvertTo-HTML -head $tableStyle | Out-File c:\delgateReport.html  
When it all goes wrong

Delegates are one of the things for a number of reasons that can become corrupted or Invalid. The Delegate operations that the  EWS Managed API provides abstracts of the Raw Delegate information which is held in number of properties on the Local Freebusy information the Folder DACL and possibly a Inbox Rule if calendar forwards are enabled. To Access and fix this raw data if something isn't working have a look at this post 

Thursday, March 08, 2012

How To Series Sample 4 : Reporting on Items of a certain Type

This is the forth of my samples based on the EWS Managed API and Powershell how to series this script shows a method you can use to produce a report of all the Items within a Mailbox based on one particular Item type. Eg if you want to show the size and number of Items for a custom Message class eg "IPM.Note.Myclass" or a another third party item type like IPM.Note.EnterpriseVault.ShortCut etc.

This script first gets all the folders within a mailbox then gets all the Items of a particular ItemClass using a Searchfilter. Its then adds the number of Items and their size and produces a final CSV type report at the end showing the FolderPath and number and size details.

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

  1. ## Load Managed API dll  
  2. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll"  
  3.   
  4. ## Set Exchange Version  
  5. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1  
  6.   
  7. ## Create Exchange Service Object  
  8. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)  
  9.   
  10. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials  
  11.   
  12. #Credentials Option 1 using UPN for the windows Account  
  13. $creds = New-Object System.Net.NetworkCredential("user@domain.com","password")   
  14. $service.Credentials = $creds      
  15.   
  16. #Credentials Option 2  
  17. #service.UseDefaultCredentials = $true  
  18.   
  19. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates  
  20.   
  21. [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}  
  22.   
  23. ## 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  
  24.   
  25. $MailboxName = "user@domain.com"  
  26. #CAS URL Option 1 Autodiscover  
  27. $service.AutodiscoverUrl($MailboxName,{$true})  
  28. "Using CAS Server : " + $Service.url   
  29. $rptCollection = @()  
  30.   
  31. #Define ItemType  
  32. $ItemType = "IPM.Note.Exchange.ActiveSync.MailboxLog"  
  33.   
  34. $fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1000)  
  35. $fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep  
  36. $folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)   
  37.   
  38. $tfTargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)  
  39.   
  40. $sfItemSearchFilter = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass,$ItemType)  
  41.   
  42. $PR_Folder_Path = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(26293, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);    
  43.   
  44. $pfPropSet =  new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)   
  45. $fvFolderView.PropertySet = $pfPropSet  
  46. $fvFolderView.PropertySet.Add($PR_Folder_Path)  
  47.   
  48. #Define Function to convert String to FolderPath    
  49. function ConvertToString($ipInputString){    
  50.     $Val1Text = ""    
  51.     for ($clInt=0;$clInt -lt $ipInputString.length;$clInt++){    
  52.             $Val1Text = $Val1Text + [Convert]::ToString([Convert]::ToChar([Convert]::ToInt32($ipInputString.Substring($clInt,2),16)))    
  53.             $clInt++    
  54.     }    
  55.     return $Val1Text    
  56. }    
  57.   
  58. $findFolderResults = $null  
  59. do{  
  60.     $findFolderResults = $tfTargetFolder.FindFolders($fvFolderView)  
  61.     foreach($folder in $findFolderResults.Folders){  
  62.             "Processing Folder " + $folder.DisplayName  
  63.             $foldpathval = $null    
  64.             #Try to get the FolderPath Value and then covert it to a usable String     
  65.             if ($folder.TryGetProperty($PR_Folder_Path,[ref] $foldpathval))    
  66.             {    
  67.                 $binarry = [Text.Encoding]::UTF8.GetBytes($foldpathval)    
  68.                 $hexArr = $binarry | ForEach-Object { $_.ToString("X2") }    
  69.                 $hexString = $hexArr -join ''    
  70.                 $hexString = $hexString.Replace("FEFF""5C00")    
  71.                 $fpath = ConvertToString($hexString)    
  72.             }    
  73.             $ivItemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)  
  74.             $rptObj = "" | select FolderPath,NumberofItems,SizeofItems  
  75.             $rptObj.NumberofItems = 0  
  76.             $rptObj.FolderPath = $fpath  
  77.             $findItemsResults = $null  
  78.             do{  
  79.                 $findItemsResults = $folder.FindItems($sfItemSearchFilter,$ivItemView)  
  80.                 foreach($itItem in $findItemsResults.Items){  
  81.                     $rptObj.NumberofItems += 1  
  82.                     $rptObj.SizeofItems += [INT32]$itItem.Size  
  83.                 }  
  84.                 $ivItemView.offset += $findItemsResults.Items.Count  
  85.             }while($findItemsResults.MoreAvailable -eq $true)  
  86.             if($rptObj.NumberofItems -gt 0){  
  87.                 $rptCollection += $rptObj  
  88.             }  
  89.         }  
  90.     $fvFolderView.offset += $findFolderResults.Folders.Count  
  91. }while($findFolderResults.MoreAvailable -eq $true)  
  92. $rptCollection  
  93. $rptCollection | Export-Csv -NoTypeInformation c:\mbItemTypeReport.csv  


Tuesday, March 06, 2012

How To Sample 3 - Scanning all folders in a mailbox for updated Items in EWS

This is the third of my samples based on the EWS Managed API and Powershell how to series I've been writing so if your looking for background on this please refer to these other posts.

EWS has a number of good Synchronization operations which I'll be covering in future posts but i thought I'd go through another method you can use if your looking to find new Items or changed items in a Mailbox.

For example say you have a mailbox with a complicated folder structure with a number of Inbox rules that moves messages to different Folders and you want a script that that will look at all the folders in a Mailbox and only query those folders where the Items in that folder have been updated say since yesterday and return information about those items.

The property you need to use on a folder to know if the underlying Items have change is the PR_LOCAL_COMMIT_TIME_MAX which is one the properties using by ICS see . So we can basically use this property within a Searchfilter in EWS to determine if the underlying items in a folder has been updated after a particular date. To work out which Items are new you then need a conventional AQS item query to look for Items that where received after that said time

There are a few options in this script the first is the time you look at which is controlled by this variable

$queryTime = [system.DateTime]::Now.AddDays(-1)

(this should be self explanatory in terms of date)

The other things I've done is this script is to also query the Dumpster V2 so if Single Item Recovery is enabled in a Mailbox it makes sure it captures everything that has entered. This is done by the following lines

##Option add in the Deletions Folder
$delfolderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::RecoverableItemsDeletions,$MailboxName)
$DelFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$delfolderid)

and

$findFolderResults.Folders.Add($DelFolder)

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

  1. ## Load Managed API dll  
  2. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll"  
  3.   
  4. ## Set Exchange Version  
  5. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1  
  6.   
  7. ## Create Exchange Service Object  
  8. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)  
  9.   
  10. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials  
  11.   
  12. #Credentials Option 1 using UPN for the windows Account  
  13. $creds = New-Object System.Net.NetworkCredential("user@domain.com","password")   
  14. $service.Credentials = $creds      
  15.   
  16. #Credentials Option 2  
  17. #service.UseDefaultCredentials = $true  
  18.   
  19. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates  
  20.   
  21. [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}  
  22.   
  23. ## 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  
  24.   
  25. $MailboxName = "user@domain.com"  
  26. #CAS URL Option 1 Autodiscover  
  27. $service.AutodiscoverUrl($MailboxName,{$true})  
  28. "Using CAS Server : " + $Service.url   
  29.   
  30. #Define Query Time  
  31.   
  32. $queryTime = [system.DateTime]::Now.AddDays(-1)  
  33.   
  34. $PR_LOCAL_COMMIT_TIME_MAX = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x670A, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::SystemTime);  
  35.   
  36. $fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1000)  
  37. $fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep  
  38. $folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)   
  39. $SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsGreaterThan($PR_LOCAL_COMMIT_TIME_MAX,$queryTime)  
  40.   
  41. ##Option add in the Deletions Folder  
  42. $delfolderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::RecoverableItemsDeletions,$MailboxName)   
  43. $DelFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$delfolderid)  
  44.   
  45. $tfTargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)  
  46.   
  47. $findFolderResults = $tfTargetFolder.FindFolders($SfSearchFilter,$fvFolderView)  
  48. $findFolderResults.Folders.Add($DelFolder)  
  49.   
  50. $AQSString =  "System.Message.DateReceived:>" + $queryTime.ToString("MM/dd/yyyy")  
  51. $AQSString  
  52. foreach($folder in $findFolderResults.Folders){  
  53.     if($folder.TotalCount -gt 0){  
  54.         "Processing Folder " + $folder.DisplayName  
  55.         $ivItemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)  
  56.         $findItemsResults = $null  
  57.         do{  
  58.             $findItemsResults = $folder.FindItems($AQSString,$ivItemView)  
  59.             foreach($itItem in $findItemsResults.Items){  
  60.                 $itItem.Subject  
  61.             }  
  62.             $ivItemView.offset += $findItemsResults.Items.Count  
  63.         }while($findItemsResults.MoreAvailable -eq $true)  
  64.     }  
  65. }