Using PowerShell and the Office 365 REST API with OAuth.
Who is this blog post for:
Administrators who want to use PowerShell to query Office 365 Rest API using OAuth.
Who isn't this for?
Anyone who uses any other programming language -
Check out this getting started - it's perfect for you:
https://dev.office.com/getting-started/office365apis
So... Let's get started
This is also a good time to recommend a really useful tool written by my colleague that allows you to experiment with the OAuth API:
https://github.com/Microsoft/Office365APIEditor
I've also used a method to show a nice UI window for the login window I found through:
https://foxdeploy.com/2015/11/02/using-powershell-and-oauth/
I recommend reading this - the explanations here are very simple.
I'm basically covering just the implementation in that blog.
Code for the method is directly accessible here:
https://raw.githubusercontent.com/1RedOne/PSWordPress/master/Private/Show-oAuthWindow.ps1 but I made a few modifications to it.
Scripting Guy's blog regarding OAuth and Windows Live:
https://blogs.technet.microsoft.com/heyscriptingguy/2013/07/01/use-powershell-3-0-to-get-more-out-of-windows-live/
My motivation for writing this blog:
I've lately been hearing more and more about the Office 365 REST API from customers who are using it with OAuth and needed to get started with using it.
I also need to do quick verifications and script things quickly and for that PowerShell is just awesome.
PowerShell was missing from the getting-started guide and I've been wanting to try it out for a while.
Step 1:
The first thing we want to is to register a new app:
Sign in with your Office 365 account here.
https://dev.office.com/app-registration
Step 2:
In the App registration page go ahead and register an app.
App name - you can name it whatever you want.
App type - for the purpose of this blog - we can use Web App
Sign on and Redirect URI - for the purposes of this tutorial you can just leave them as is.
Click Register app.
You'll get a client-id and client-secret.
Copy paste everything on this page to a text file, we're going to use it later.
Time to start scripting!
Now that we have that, we can get started with the script.
First thing we want to do is define our very handy Show-OAuthWindow method:
Function Show-OAuthWindow
{
param(
[System.Uri]$Url
)
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object -TypeName System.Windows.Forms.Form -Property @{Width=440;Height=640}
$web = New-Object -TypeName System.Windows.Forms.WebBrowser -Property @{Width=420;Height=600;Url=($url ) }
$DocComp = {
$Global:uri = $web.Url.AbsoluteUri
if ($Global:Uri -match "error=[^&]*|code=[^&]*") {$form.Close() }
}
$web.ScriptErrorsSuppressed = $true
$web.Add_DocumentCompleted($DocComp)
$form.Controls.Add($web)
$form.Add_Shown({$form.Activate()})
$form.ShowDialog() | Out-Null
$queryOutput = [System.Web.HttpUtility]::ParseQueryString($web.Url.Query)
$output = @{}
foreach($key in $queryOutput.Keys){
$output["$key"] = $queryOutput[$key]
}
$output
}
What does it do? It just opens the URL we want in a handy little window. Then we use the response to parse a few parameters.
Below this, we can define our variables and the System.Web assembly.
Add-Type -AssemblyName System.Web
$client_id = "paste the client ID we generated"
$client_secret = "paste the client secret we got"
$redirectUrl = "the redirect URL we used when creating the app."
We can then create our login URL:
$loginUrl = "https://login.microsoftonline.com/common/oauth2/authorize?response_type=code&redirect_uri=" +
[System.Web.HttpUtility]::UrlEncode($redirectUrl) +
"&client_id=$client_id" +
"&prompt=login"
If you're wondering how I knew the format of the request, everything is documented here:
https://graph.microsoft.io/en-us/docs/authorization/app_authorization
Then we'll go ahead and use the method above:
$queryOutput = Show-OAuthWindow -Url $loginUrl
We need to enter the username and password we're going to use with this app.
Once we do that, we'll get a page asking us to approve the APP access to the permissions we specified above - so we can go ahead and approve that.
$queryOutput will have the following keys in the hashtable:
code
session_state
The code is what we're interested in - as we're going to go ahead and trade it for an access token.
How are we going to do that?
Well, we're going to make another request:
$AuthorizationPostRequest =
"grant_type=authorization_code" + "&" +
"redirect_uri=" + [System.Web.HttpUtility]::UrlEncode($redirectUrl) + "&" +
"client_id=$client_id" + "&" +
"client_secret=" + [System.Web.HttpUtility]::UrlEncode("$client_secret") + "&" +
"code=" + $queryOutput["code"] + "&" +
"resource=" + [System.Web.HttpUtility]::UrlEncode("https://outlook.office.com/")
Then we can go ahead and make the request:
$Authorization =
Invoke-RestMethod -Method Post `
-ContentType application/x-www-form-urlencoded `
-Uri https://login.microsoftonline.com/common/oauth2/token `
-Body $AuthorizationPostRequest
After execution $Authorization will have our very handy access_token - which we now have to add to every call we make.
Let's see an example:
$mail =
Invoke-RestMethod -Headers @{Authorization =("Bearer "+ $Authorization.access_token)} `
-Uri https://outlook.office.com/api/v2.0/me/messages `
-Method Get
Now we can look at our mail variable:
$mail.value | select -first 4 | select -ExpandProperty Subject
That's it!
If you have any questions, let me know :-)
Comments
- Anonymous
August 08, 2016
Thank you very much for breaking this down, checked a lot of different resources this evening and this is the first to get me connected to the v2 Outlook Calendar REST API via powershell.Since this is OAuth, I'm guessing $Authorization.access_token is temporary and will expire? I'm trying to script something that will periodically query room resource calendars and I'd like to bake the authentication into the script, without interactive login. I'm guessing OAuth likely isn't the most practical way to go about this, but your sample is the first explanation accessible enough for me. What would you recommend for a simple script authentication to get at the Calendar REST API? Thanks again!- Anonymous
August 09, 2016
Kevin, thanks for the comment! I definitely found there's a learning curve here, which is why I learned this while I was doing the blog.You're right - the access token will expire - which is the reason you get a refresh token, check out the responses.The refresh token allows you to request a new access token.Edit: You can see the request you need to make for that here:Renew expiring access token using refresh tokenhttp://graph.microsoft.io/en-us/docs/authorization/app_authorizationSo.. I've been thinking about that question myself for a while, I came up with a few answers.Of course each answer has its advantages and disadvantages, weigh these against what your requirements are (especially security).1) Use EWS rather than the rest API, I'm just putting it out there - EWS is still awesome and works well.2) Do the OAuth authentication once with a user manually, copy the access and refresh token. Create a scheduled task to refresh the token once every few days. This won't scale, but if you just need a single user it might be OK.3) Following your comment, I actually emailed the folks doing the v2.0 Auth endpoint.This is the suggestion I got back. It is more complicated, but will allow you to scale.There's a good explanation about this here:https://azure.microsoft.com/en-us/documentation/articles/active-directory-authentication-scenarios/#daemon-or-server-application-to-web-apiAnd some code samples: https://azure.microsoft.com/en-us/documentation/samples/?service=active-directory&term=daemonWith all of these you should make the proper security arrangements to prevent people from accessing credentials/tokens/certificates.Lastly, I wanted to point you to a tool my colleague is developing: https://github.com/Microsoft/Office365APIEditor - what I like about this is what I can see the requests being made and the source code.This tool, Fiddler and Postman are extremely useful here :-)HTHRon
- Anonymous