Skip to main content

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









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-

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

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