Tuesday, March 21, 2017

Using the Office365/Exchange 2016 REST API part 2 buiding an Admin Runner using AppOnly tokens

This is part 2 in my REST series in which we will look at AppOnly tokens. These are the Tokens you would look to use when you want to write an application or script that would access every mailbox in an Office365 Tenant.  For an Admin or DevOps person looking at what they might want to do with the new REST API this is useful when your looking to write something that will tweak a config setting on all Mailboxes to comply with a certain Organization policy (no matter how insane) or do some custom Item task that isn't supported in any of the Admin cmdlets.

To simplify AppOnly tokens as much as I can they are an Oauth Access token that are requested using Certificate Authentication. Then depending on what Application permission scopes have been allowed for the app in Azure eg

your script or app will be able to access that particular Mailbox data across all the Mailboxes within your tenant. In EWS if you understood how impersonation worked this is a kind of an equivalent but a lot better from a security perspective. Eg if your just writing a script that needs to keep contacts in sync in a Mailbox (which I've seen and done many times) then you can just assign the "Read and write contacts in all mailboxes" scope for your application and nothing else and that's all your application can do. With Impersonation you effectively gave full access to Mailbox and which could then be potentially exploited and was much derided by security people in general.

There is bit of setup to do before you can use this which can be little complicated but I'll try to simplify the best I can.

1. You need to first create a Web Application registration within your tenant using the Azure Management Console (note a native app won't work for this). https://docs.microsoft.com/en-us/azure/active-directory/active-directory-app-registration . For this type of application the SignIn and Auth url isn't important as we are going to be using certificate auth so you can just use something like eg

2. Configure the Application permissions for what you want your application to do eg the below screen again. For my test app I'm going to be using the "Read All Mailbox Setting" scope to create a script that read the Oof Message and timezone setting from all mailboxes passed in. Delegated permission aren't valid for this type of App as they won't be scoped for the this type of Access token.

Make sure you click save down the bottom to ensure the changes you make are committed

3. Create a self signed certificate that will be used to sign the JWT (Java Web Token) requests. To make this easy in my Rest module I've create a cmdlet to do this using the default New-SelfSignedCertificate Powershell cmd. However this will only work on Windows 10 because it requires the -provider switch which isn't available on early O/S. Alternatives you can use are MakeCert which there is an example of in https://msdn.microsoft.com/en-us/office/office365/howto/building-service-apps-in-office-365 or you could use OpenSSL

If you want to use my cmdlet you need to use it like

Invoke-CreateSelfSignCert -CertName "yourCertNameMakeitdescriptive" -CertFileName c:\temp\yourcertFile.pfx -KeyFileName c:\temp\KeyCreds.txt

where CertName should be self explanatory, CertFileName is the filename for your certificate (you could chose to leave It in the windows CertStore but I've exported it to file for flexibility). -KeyFileName is a temporary text file that will hold the configuration information that needs to be copied in to the Application manifest in Azure in step 4.

4. Update the Application manifest, from the Azure Console where you configuration the application permissions in step2 you need to select the download manifest from the Manage Manifest option at the bottom of the console eg

Once you have download the manifest you need to open that in a Text editor (or VSCode if you have it) and locate the keycredentials entry

If you then open the KeyFileName that was created with Invoke-CreateSelfSignCert in step 3 and cut and paste the all the content and replace the [] in Keycredentials in the manifest you downloaded eg it should look something like this

Make sure when your pasting the data you don't wipe the comma after the value as you will get a parse error when you try to upload the manifest. So once you save those change you click upload Manifest from the Manage Manifest button in the Azure console.

5. The last thing you want to do while in the Azure console is get your tenant ID information there are a number of ways to do this but the easiest is outlined in https://support.office.com/en-us/article/Find-your-Office-365-tenant-ID-6891b561-a52d-4ade-9f39-b492285e2c9b  (just look at the URL when you modifying the application). You will need this TenantId for the Module Setup

PowerShell Module Setup

In my REST Script module I have a config section at the top that holds the information needed for generating the token. For App only tokens you need to setup the following variables

function Get-AppSettings(){
            $configObj = "" |  select ResourceURL,ClientId,redirectUrl,ClientSecret,x5t,TenantId
            $configObj.ResourceURL = "outlook.office.com"
            $configObj.ClientId = "084adbb3-f70c-498f-97a3-464e1f444d9a"
            $configObj.TenantId = "1c3a18bf-da31-4f6c-a404-2c06c9cf5ae4"
            $configObj.ClientSecret = ""
            $configObj.x5t = "3Y5+FU8x2tZ8DudO479K71ILQF8="
            $configObj.ValidateForMinutes = 60
            return $configObj           

ResourceURL should be fine unless you want to access the Graph API

ClientId should be the ClientId of your Application in Manifest file you downloaded previously take the value from appId which should be the first line in the manifest

redirectURL isn't used for AppOnly token

TenantId should be set to the value you retrieved in step 5 above.

x5t should be set to the customKeyIdentifier that you pasted into the KeyCredentials in the manifest file.


Once you have all the configuration done to test if it is working you use the Get-AppOnlyToken cmdlet to create an app only token

$Token  = Get-AppOnlyToken -CertFileName c:\temp\certfile.pfx

If you then look at the contents of the $Token variable you should have either an error or a Token

Putting it to use

I've included one example report in the REST Module that will work with AppOnly tokens. This produces a report of Mailbox setting using the GetMailboxSetting op https://msdn.microsoft.com/office/office365/APi/mail-rest-operations#get-all-mailbox-settings

To run this you pass in a collection of Mailbox addresess you want to run the report against (eg you could produce this using Get-Mailbox) or use a CSV file or just using something like

$Mailboxes = @()
$Mailboxes += "one@domain.com"
$Mailboxes += "two@domain.com"

$report = Get-MailboxSettingsReport -Mailboxes $Mailboxes -CertFileName c:\temp\certfile.pfx
$report | Export-Csv -NoTypeInformation -Path c:\temp\mbreport.csv

The REST PowerShell module can be found here https://github.com/gscales/Powershell-Scripts/blob/master/RestHttpClientMod.ps1