Skip to main content

Using the Office365/Exchange 2016 REST API to access Mailbox data using PowerShell part 1

The Outlook REST API 's https://dev.outlook.com/ which are part of Office365 and Exchange 2016 is one of the ways new feature are being delivered for Mailbox clients which previously where delivered via EWS operations. They are also part of the Graph API https://graph.microsoft.io/en-us/docs which is Microsoft's envisioned unified data access API that has the ultimate goal of allowing you to access all your data endpoints via a single interface/endpoint.

In this series of posts I'm going to be looking at writing a PowerShell module that uses the REST API to access Mailbox data and some of the new Exchange features like Groups and the focused Inbox. To keep things simple and flexible I'm not going to use any helper libraries (like the ADAL library or the Outlook Services Client) which I hope will make the script as portable and easy to use as possible with the one downside of while making the code a little more complex  I'm going to use the System.Net.HttpClient classes for greater flexibility as apposed the native PowerShell Rest interfaces.

Getting started

Compared to EWS where there was very little up front configuration necessary to get going (eg in most case just supply a username and password) for the REST API's there is a little bit of configuration that needs to be done.
To use the new REST endpoint you need to use oAuth authentication which means instead of a username and password being included as a header which each request to the server like in Basic Authentication you use an Access Token which is only valid for an hour. A Refresh Token can be used to renew the Access Token when it expires. Tokens offer a big security advantage over using a UserName and Password but still should be treated as if they where a username and password in regards to storage and access as they can still be exploited in the same way. This is an extreme simplification of the oAuth, there is some good documentation sources but be careful of those that discuss Modern Authentication and the ADAL library as they tend to abstract away some the real technical side of understanding what's happening with Token Auth. Personally I like https://docs.microsoft.com/en-gb/azure/active-directory/develop/active-directory-authentication-scenarios and https://msdn.microsoft.com/en-us/office-365/get-started-with-office-365-management-apis as these look more at the underlying way the protocols work.

To use oAuth to authenticate you need to create an Application registration (which gives you the clientId) to use for your scripts or authorize somebody else's (which wouldn't be recommended).  There are plenty of good walk throughs on creating app registrations using the Azure console this one is quite good https://github.com/jasonjoh/office365-azure-guides/blob/master/RegisterAnAppInAzure.md . For scripting generally you want to create a Native App registration and use the Out of Band Call-back urn:ietf:wg:oauth:2.0:oob . One of the big advantages of using oAuth with the new REST interfaces is the authentication scopes which allow you to restrict an application/script to just being able to access the resources you want. Eg if this app is going to just access contacts data then you just enable the authentication scope that allows access to contacts data without allows access to any other Mailbox items.

Authenticating as a User or Application

In EWS and MAPI authentication is always done in the context of the User if you want to access a Mailbox other then that of security context you are using then Delegation would allow that or you could configure Application Impersonation using RBAC which means you could impersonate the owner of any mailbox you wanted to access. There is no Impersonation in the REST API but in Azure you can use what they term the Daemon or Server Application scenario or App-Only tokens which are documented  https://docs.microsoft.com/en-gb/azure/active-directory/develop/active-directory-authentication-scenarios#daemon-or-server-application-to-web-api and https://msdn.microsoft.com/en-us/office/office365/howto/building-service-apps-in-office-365 . In the current interaction of the module I don't cover this Authentication scenario but will in future posts and interactions.

What you get out of the app registration process is a ClientId to use in your script.

Down to coding

For the Authentication code in my module I've used the pretty cool  Show-AuthWindow function from https://foxdeploy.com/2015/11/02/using-powershell-and-oauth/ and https://blogs.technet.microsoft.com/ronba/2016/05/09/using-powershell-and-the-office-365-rest-api-with-oauth/ which does the job of presenting the Azure logon box, any user\tenant consents that are necessary and return back an Auth code that can then be used to get an Access Token. With my implementation I've put all the configurable variables into a separate function to call eg

function Get-AppSettings(){
        param( 
        
        )  
  Begin
   {
            $configObj = "" |  select ResourceURL,ClientId,redirectUrl
            $configObj.ResourceURL = "outlook.office.com"
            $configObj.ClientId = "5471030d-f311-4c5d-91ef-74ca885463a7"
            $configObj.redirectUrl = "urn:ietf:wg:oauth:2.0:oob"
            return $configObj            
         }    
}

This makes the ClientId, ResourceURL and redirect easy to configure

The rest of the code is pretty straight forward setting up and using the HTTPClient object to make the necessary REST GET's and POSTs. I've included a number of functions that use the MailboxSetting https://msdn.microsoft.com/office/office365/APi/mail-rest-operations#GetAllMailboxSettings you can break those down into Getting the Oof (or Automatic Replies), timezone etc. Because these are just simple http GET's the code to get the information and parse the JSON results is pretty simple. I've included a sample Get-ArchiveFolder function that demonstrates stacking requests to Get the new Archive Folder https://support.office.com/en-us/article/Archive-in-Outlook-2016-for-Windows-25f75777-3cdc-4c77-9783-5929c7b47028?ui=en-US&rs=en-US&ad=US which was introduced recently. To get the Id of an Archive Folder in a Mailbox you make a request to https://outlook.office.com/api/v2.0/Users('$MailboxName')/MailboxSettings/ArchiveFolder and then you use the result returned to get the Folder in question which might be useful if you tracking usage stats or want to copy an item into the archive.  I've put a copy of the module here (which is a work in progress) https://github.com/gscales/Powershell-Scripts/blob/master/RestHttpClientMod.ps1


Popular posts from this blog

Export calendar Items to a CSV file using EWS and Powershell

Somebody asked about this last week and while I have a lot of EWS scripts that do access the Calendar I didn't have a simple example that just exported a list of the Calendar events with relevant information to a CSV file so here it is. I've talked on this one before in this howto  but when you query the calendar folder using EWS you need to use a CalendarView which will expand any recurring appointments in a calendar. There are some limits when you use a calendarview in that you can only return a maximum of 2 years of appointments at a time and paging will limit the max number of items to 1000 per call. So if you have a calendar with a very large number of appointments you need to break your query into small date time blocks. In this example script I'm just grabbing the next 7 days of appointments if you want to query a longer period you need to adjust the following lines (keeping in mind what I just mentioned) #Define Date to Query $StartDate = (Get-Date) $EndDate

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/filena

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.