Skip to main content

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). . 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 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  (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 = ""
            $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

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

$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

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 = (Ge…

EWS Managed API and Powershell How-To series Part 1

I thought I'd start the year with a series of posts that goes back over the basics of using the EWS Managed API from Powershell and provides a modular remarked example that you can easily cut and paste to build your own scripts. Along the way in this series I'll show a whole bunch of examples around specific things.

As a starting point for versions this will be Powershell Version 2.0  and the EWS Managed API 1.1 (which will soon change to 1.2 once released)

The starting point for any EWS script your going to write is connecting to Exchange for which there are three important pieces of information you will need. Firstly you need to know the version of Exchange your running in this script its going to be held in the following variable

$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1

Other valid values for Exchange 2007 would be

$ExchangeVersion = [Microsoft.Exchange.WebServices.…

Writing a simple scripted process to download attachmentts in Exchange 2007/ 2010 using the EWS Managed API

Every complicated thing in life is made up of smaller simpler building blocks, when it comes to writing a script (or any code really) the more of these little building blocks you have to figure out the more the process of solving a problem can become bewildering. The Internet generally provides you with lots of half eaten sandwiches of information something someone else has taken a bite out but a lot of the time half done, and as with any code its usefulness declines over time as new and better API's and methods are derived. In this post I'm going to go through a simple scripted process that hopefully covers a few more of these smaller building blocks that you might face when asked to come up with a simple costless solution to perform an automated business function with a script.

So the process im going to look at is one that comes up a lot and that is you have an Email that comes into to certain mailbox every day with a certain subject in my case "Daily Export" this …
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.