Friday, February 10, 2017

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


7 comments:

Laeeq Qazi said...

Hi Glen,
Good post to explain o365 Rest Api to access mailbox data, but this seems little difficult than ews api. Also, if no help to access delegated mailbox, then its not helpful in cases where an admin mailbox is used to access a set of mailboxes.

Elijah Gagne said...

Very interesting! I'm trying to give it a go. Here's what I'm doing:

. "C:\temp\RestHttpClientMod.ps1"
Get-AccessToken "elijah@company.com"

In the pop up window I'm getting back:

Sorry, but we're having trouble signing you in.
We received a bad request.

Additional technical information:
Correlation ID: 3b118dca-6a1f-4f5a-995d-01351f134770
Timestamp: 2017-02-13 01:46:42Z
AADSTS50011: Reply address 'urn:ietf:wg:oauth:2.0:oob' specified by the request is not a valid URL. Allowed schemes: 'http,https'

Should I be setting:

$configObj.redirectUrl = "urn:ietf:wg:oauth:2.0:oob"

to some different value or is there another obvious mistake? Thanks for any help.
-EWG

Glen Scales said...

I agreed re the EWS Managed API, I'm going to cover the later scenario in future versions of the script and a separate post.

Glen Scales said...

Did you create an Application registration of your own ? the out of band callback "urn:ietf:wg:oauth:2.0:oob" needs to be listed in the Callback address for your application. This isn't allowed in a WebAPI app by default (eg you would get that error in the Admin UI) unless you add Mobile support where it gets added automatically. If you create a Native App registration you should be able to just add it as the call back.

Elijah Gagne said...

Thanks Glen. I tried updating the web app's manifest JSON directly, but I could not find a way to get "urn:ietf:wg:oauth:2.0:oob" as a valid callback / replyURL. I was able to add it to the manifest, but I would still get the same error as above.

I created a second, Native, app. For that one, I could add "urn:ietf:wg:oauth:2.0:oob" to the list of Reply URLs in the web interface and using that client ID, I can get your PowerShell working.

Thanks much!

Unknown said...

Hi Glen , Do you have a script that can extract Calendar Objects and Permission in my exchange environment.

I've running around the web to find a script for it. it is soomewhat like this but i can't convert the scripts into powershell

https://gallery.technet.microsoft.com/office/Exchange-2010-Calendar-21695fde

Thank you in advance.

Thanks

Glen Scales said...

I have script for export calendar items http://gsexdev.blogspot.com.au/2015/03/export-calendar-items-to-csv-file-using.html

for permission I would suggest either exfolders or write your own using get-mailboxfolderpermissions