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