Monday, September 24, 2018

Microsoft Teams Tab Application that uses the Graph API and Exchange FreeBusy Information

In my last post I went over the new Graph function called getSchedule and how this can be used to get the FreeBusy information for a User (or Meeting Room, Resource Mailbox etc) and display that as a table like the schedule assistant in Outlook.

In this post I'll show you how you can do the same thing in a Teams tab application which will give you an output something like the following

The steps this app takes to display this is firstly

Step 1.

Authentication - The app authenticates to the Microsoft Graph API using the javascript ADAL using the silent Authentication flow for tab applications which is explained in detail here .

Step 2

Get the Group Members - Once it has an Access-Token it then Gets the members of the current Team from the Graph by first getting the Id from the Context of the team currently in focus from the Teams Client SDK and then making a request to the Groups Endpoint


Schedule Request - Make a schedule request of the Members of the Team (note this operation is only in Beta at the moment). This returns the FreeBusy information for the members of the team in 30 minute timeslots for the working hours period I'm displaying. Note the Timezone from the browser (or Teams Client) is used here. Incorrect Time zone of timezone differences are something that can affect the results of this operation.


Build the HTML to Display- Build the above FreeBusy table from the Results


Get the UesrPhotos -   Backfill the user photos asynchronously using the Graph photos endpoint.

That's it for the rundown on the functional side of how this Tab app works for more of a description on how Tab apps work in general have a look at my last post which goes a bit more in depth on this. I've rewritten most of the code from that post so its now ES6 and implements both promises and async functions in JavaScript which eliminants a lot of clutter,logic fog and call-backs code the plagued the original (if you still using IE none this will work but if your still using IE your probably using Microsoft comic chat).

I've also implemented a config file in

So anything that needed to be hardcoded in the app is in this file which is basically

        clientId : "2cff1b6d-82d4-4dc0-9ac4-7f35b06cc64a"  - this the Azure App registration which you should create your own of that will list

        redirectUri : "/TeamsFB/app/silent-end.html" - From the Azure App registration

        authwindow :  "/TeamsFB/app/auth.html" - For the Slient Auth functions

        hostRoot: "" - For the relative references in the js files

this makes it easy if you want to change the host or use ngrok to host it, eg just change hostRoot, Azure RedirectURI to the ngrok address and you should be good to go.

Permissions for Azure App registration

Because this app accesses various graph endpoints different oauth Grants need to be allowed eg
  • Group members User.Read and Group.Read.All
  • FreeBusyInformation   Calendar.Read
  • UserPhotos User.ReadBasic.All
Installation and Pre-Req

Azure app registration - you need to have an app registration that is Authorised within you tenant (see my last post for more details)

Side Loading - To use custom tab applications you first need to enable side loading of Apps in the Office365 Admin portal ref .The important part is  "Sideloading is how you add an app to Teams by uploading a zip file directly to a team. Sideloading lets you test an app as it's being developed. It also lets you build an app for internal use only and share it with your team without submitting it to the Teams app catalog in the Office Store. "

As this is a custom application you need to use the "upload a custom app" link which is available when you click ManageTeam-Apps tab see

(Note if you don't see  the "upload a custom app" check that you have side loading of apps enabled in your tenant config)

What you upload here is a Zip file containing your manifest file and two images that you manifest refers to for eg
   "icons": {
    "outline": "Outline32.png",
    "color": "Colour192.png"

For this sample this is located in

These icons are what is used in the UI to represent your application.

All the code for this sample is available in GitHub here

Its also hosted in GitHub pages if you have a development tenant and want to test it (you will need to first use the admin consent URL listed above).

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


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":  [
    "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.


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 and GitHub

The Module has the following Cmdlet


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 += ""
$Mailboxes += ""

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

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 too big or small).