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

Export calendar Items to a CSV file using Microsoft Graph and Powershell

For the last couple of years the most constantly popular post by number of views on this blog has been  Export calendar Items to a CSV file using EWS and Powershell closely followed by the contact exports scripts. It goes to show this is just a perennial issue that exists around Mail servers, I think the first VBS script I wrote to do this type of thing was late 90's against Exchange 5.5 using cdo 1.2. Now it's 2020 and if your running Office365 you should really be using the Microsoft Graph API to do this. So what I've done is create a PowerShell Module (and I made it a one file script for those that are more comfortable with that format) that's a port of the EWS script above that is so popular. This script uses the ADAL library for Modern Authentication (which if you grab the library from the PowerShell gallery will come down with the module). Most EWS properties map one to one with the Graph and the Graph actually provides better information on recurrences then...

Downloading a shared file from Onedrive for business using Powershell

I thought I'd quickly share this script I came up with to download a file that was shared using One Drive for Business (which is SharePoint under the covers) with Powershell. The following script takes a OneDrive for business URL which would look like https://mydom-my.sharepoint.com/personal/gscales_domain_com/Documents/Email%20attachments/filename.txt This script is pretty simple it uses the SharePoint CSOM (Client side object Model) which it loads in the first line. It uses the URI object to separate the host and relative URL which the CSOM requires and also the SharePointOnlineCredentials object to handle the Office365 SharePoint online authentication. The following script is a function that take the OneDrive URL, Credentials for Office365 and path you want to download the file to and downloads the file. eg to run the script you would use something like ./spdownload.ps1 ' https://mydom-my.sharepoint.com/personal/gscales_domain_com/Documents/Email%20attachments/filen...

Sending a MimeMessage via the Microsoft Graph using the Graph SDK, MimeKit and MSAL

One of the new features added to the Microsoft Graph recently was the ability to create and send Mime Messages (you have been able to get Message as Mime for a while). This is useful in a number of different scenarios especially when trying to create a Message with inline Images which has historically been hard to do with both the Graph and EWS (if you don't use MIME). It also opens up using SMIME for encryption and a more easy migration path for sending using SMTP in some apps. MimeKit is a great open source library for parsing and creating MIME messages so it offers a really easy solution for tackling this issue. The current documentation on Send message via MIME lacks any real sample so I've put together a quick console app that use MSAL, MIME kit and the Graph SDK to send a Message via MIME. As the current Graph SDK also doesn't support sending via MIME either there is a workaround for this in the future my guess is this will be supported.
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.