Friday, September 07, 2018

Getting FreeBusy information in the Graph API using the getSchedule action and putting it to use in PowerShell

One of the recent additions to the Graph API for Exchange in Office365 has been getSchedule action https://developer.microsoft.com/en-us/graph/docs/api-reference/beta/api/calendar_getschedule . What this action allows to do is get the FreeBusy information from one or more mailboxes in your Office365 environment. If your new to Exchange (or for those that have forgotten) FreeBusy information is essential what is used to produce the Schedule Assistant in Outlook and equivalent in OWA


The above is built by Outlook using the EWS GetUserAvaliblity operation which is what the getSchedule action in the Graph is the equivalent of. GetUserAvaliblity has been in use in Outlook since Exchange 2007 where we went from using Public Folders to hold the freebusy information in previous versions to the Public Folder free utopia we have today. In the preceding years features such as Suggested Meeting times have been added to this EWS operation this feature is available in the Graph via the Graph in the findmeeting action .

If you are using the Graph API
getSchedule is a  good a solution for solving one of the more frequently asked questions of how you can get the availability of multiple meeting rooms without needing to query every Meeting Rooms calendars (or a simular question for a group of users).

Limits

There are some limits to be aware of when querying FreeBusy data which is you can query up to 62 days worth of FreeBusy information (eg the period between the Start and EndTimes). The other limit you should adhere to is no more the 100 Mailboxes per request to keep below the throttling limits.

Making the query

The REST request you need to make to get the FreeBusy information should look something like the following

{
    "schedules":  [
                      "gscales@datarumble.com",
                      "mec@datarumble.com"
                  ],
    "endTime":  {
                    "dateTime":  "2018-09-08T10:21:49",
                    "timeZone":  "AUS Eastern Standard Time"
                },
    "startTime":  {
                      "dateTime":  "2018-09-07T10:21:49",
                      "timeZone":  "AUS Eastern Standard Time"
                  },
    "availabilityViewInterval":  15
}

Time zones are an issue that usually trips up a lot of people, especially if your dealing with mailboxes in multiple locations. Its important to note the timezone specified in the query only relates to the query period your using. The timezone that you get back in the results will depend on the request header Prefer: outlook.timezone in the Request. So if your setting the TimeZone in the query and your getting back a different unexpected TimeZone In the results you need to look at the Prefer: outlook.timezone that's being used (or the absence of that header will mean you will get UTC back).

The availabilityViewInterval affects the timeslots that come back  in the availabilityView generally you would use either 15,30 or 60 minutes but you can have a value as high as 1440 (which represents 1 day) and as low a 6 minutes. If you set the availabilityViewInterval to 1440 then just having one appointment in a day will mean that day will appear as Busy.

Results 

The results you get back from this action is first the availabilityView which depending on the interval you specified will be splices of availability between then Start and End Times you specified. eg



Also if you have the FreeBusy,Subject and location permission on the Target calendars (or higher) you will get an array of calendar appointments (as long as they aren't private) back for each Target Mailbox. eg


In EWS you also got back the HexEntryId of the appointment object meaning you could then retrieve more information for a specific Appointment without first doing a search which seems to be missing in the Graph operation at the moment. Lets hope that comes back as its an important function and the workaround is messy and will cause a lot of development pain for people 🙏🙏.

Using this in PowerShell

I've added the ability to use this action to my Exch-Rest PowerShell library which is what the above screen shots where produced using this module is available from the PowerShell Gallery https://www.powershellgallery.com/packages/Exch-Rest and GitHub https://github.com/gscales/Exch-Rest.

The Module has the following Cmdlet

Get-EXRSchedule

this takes an array of Mailboxes you want to run it against so to use it first create a blank array

$Mailboxes = @()

then populate that with the Mailboxes you want to run against eg

$Mailboxes += "gscales@datarumble.com"
$Mailboxes += "mec@datarumble.com"

Then run the cmdlet with the Start and EndTime and availability period you want by default these aren't need and it will return the freebusy information for the next 24 hours in 15 minutes intervals eg

Get-EXRSchedule -Mailboxes $mailboxes 

However usually you would want to run something like the following

$StartTime = [DateTime]::Parse((Get-Date).ToString("yyyy-MM-dd HH:00:00"))
$EndTime = [DateTime]::Parse((Get-Date).AddHours(6).ToString("yyyy-MM-dd HH:00:00"))
Get-EXRSchedule -Mailboxes $mailboxes -Start $StartTime -EndTime $Endtime -availabilityViewInterval 15

Which would get the freebusy information for the next 6 hours in 15 minute increments

The Module is useful for getting the data so lets look at a few fun ways you can use it. eg if you want to have the results stored in a collection you could export as CSV or Graph you could use something like the following

$rptCollection = @()
$StartTime = [DateTime]::Parse((Get-Date).ToString("yyyy-MM-dd HH:00:00"))
$EndTime = [DateTime]::Parse((Get-Date).AddHours(6).ToString("yyyy-MM-dd HH:00:00"))
$avResults = Get-EXRSchedule -Mailboxes $mailboxes -Start $StartTime -EndTime $Endtime -availabilityViewInterval 15
foreach($fbResult in $avResults){
 $CurrentTime = $StartTime 
 $fbResult.availabilityView.ToCharArray() | ForEach-Object{
  $rptobj = "" | select Time,User,FreeBusyStatus
  $rptobj.Time = $CurrentTime.ToString("HH:mm")
  $rptobj.User = $fbResult.scheduleId
  switch($_){
     0 {$rptobj.FreeBusyStatus = "Free"}
     1 {$rptobj.FreeBusyStatus = "Tentative"}
     2 {$rptobj.FreeBusyStatus = "Busy"}
     3 {$rptobj.FreeBusyStatus = "Out of Office"}
     4 {$rptobj.FreeBusyStatus = "Working Elsewhere"}
  }
   $rptCollection += $rptobj
  $CurrentTime = $CurrentTime.AddMinutes(15)
 }
}
return $rptCollection 


Or we can create a FreeBusy board using this information that would look like the following


Where I'm using both the avaiblitiyview data to create the Table and also the calendar details so when you hover over one of the busy table cells it will show the subject of that meeting. I've put the code up for the freebusy board here https://github.com/gscales/Powershell-Scripts/blob/master/fbBoardGraph.ps1 .

Hire me - If you would like to do something similar to this or anything else you see on my blog I'm currently available to help with any Office365, Exchange or Active Directory related development work or scripting, please contact me at gscales@msgdevelop.com(nothing too big or small).

No comments: