Subscribing to Teams Presence with Graph API using Power Platform

Since the support for getting Teams Presence via Microsoft Graph API came into public preview earlier this year, https://docs.microsoft.com/en-us/graph/api/presence-get?view=graph-rest-beta&tabs=http, many where seeing interesting scenarios for automating Microsoft 365 using this presence status. In fact many, myself included, created different solutions for setting light bulbs etc to reflect the Teams presence. If busy, then red lights, if available green lights, and so on. I have this setup myself using Philips Hue lights, and have created a PowerApp using Flows to control my lights:

There has been one problem though, getting Teams Presence has only been possible by continuously querying the API to check if the status has changed. This can be scheduled of course, but running this every minute or so seems like an excessive way to achieve the goal: being able to see if the Teams Presence has changed!

Until just a few days ago that is, where I noticed that Teams Presence now is in Preview for subscribing to change notifications in Microsoft Graph: https://docs.microsoft.com/en-us/graph/webhooks#supported-resources

In this blog post I wlll show how to create a subscription for change notifications for Teams Presence for a specified user, and the requirements for doing this. And I will use Power Platform and Power Automate Flows to achieve this.

Let’s start with the Requirements..

First of all, to be able to set up a change notification for a resource, you need the same API permission as the resource require itself, for Teams Presence this is one of the following:

  • Presence.Read, for being able to read your own users presence status.
  • Presence.Read.All, for being able to read all users in your organization’s presence status.

Currently in the Beta endpoint, getting presence is only supported for Delegated permissions using work account, so you will have to to this with a logged in user.

The other important requirement is that you need a webhook uri for where the change notification can send a POST request for the resource that has been changed. This webhook uri can be on your preferred platform, many are using Azure Functions for this, but it can also be a third party platform. I will do it using Power Automate, another alternative could be using Azure Logic Apps also.

This webhook uri need to be able to:

  • Process the initial creation of the change notification subscription. Microsoft Graph expects a text response with a validation token for it to be successful.
  • Process every change to the resource that you subscribe to, in this example a Teams Users Presence Status.

We will get into the details on this, but for now, we will start explore using Graph Explorer.

Explore Presence and Subscriptions with Graph Explorer

Go to the Microsoft Graph Docs site and find Graph Explorer there, or just go to https://aka.ms/ge, and log in with your work account.

Type the following GET query: https://graph.microsoft.com/beta/me/presence

You need to be a Teams user to run this, but if permissions are consented, you should be able to see your own presence status like my example below. If you are getting a permission error, make sure that one of the permissions for Presence.Read or Presence.Read.All er consented. This can already been done by your administrator, if not you need to consent to them yourself.

Great, let’s check another users’ presence status. PS! The permissions don’t require admin consent, and using delegated permissions every user can check other users presence status, but you need Presence.Read.All to do that.

You need the other users id property, so you can check that first using for example <a href=”https://graph.microsoft.com/v1.0/users/https://graph.microsoft.com/v1.0/users/<someotherusersUpn>/?$select=id.

And then query for the other user’s presence: <a href=”https://graph.microsoft.com/beta/users/https://graph.microsoft.com/beta/users/<userid>/presence

Another way to query for your or other users presences, that are more aligned to the change notifications is using the /beta/communications/presences endpoint: <a href=”https://graph.microsoft.com/beta/communications/presences/https://graph.microsoft.com/beta/communications/presences/<userid&gt;:

If you can run all these queries successfully, then you are ready to proceed to change notifications subscriptions.

You can check if you have any active subscriptions for change notifications using a GET query to: https://graph.microsoft.com/v1.0/subscriptions or https://graph.microsoft.com/beta/subscriptions, depending on if you are working with preview resources. If you have no active subscriptions this should return a successful but empty response like below:

The next part would be to create a subscription for change notifications for presence, but first we need to prepare the Flow for receiving the webhook uri.

Create a Power Automate Flow for Change Notification Webhook

Log in with your work account to flow.microsoft.com, and create a new flow of type automated and blank, skip the first part of using pre-selected triggers. Search for the Request type trigger that is called “When a HTTP request is received”:

Next add a Initialize Variable action for setting the ClientState which we will use later for the creating the subscription. Set the variable to anything you want, give a name to the Flow and then save. When saved the HTTP POST URL will show the value you later will use as the webhook uri for the change notifications:

The next part is the most important one, and that require a little knowledge of the creation process for change notifications subscriptions. This is documented here https://docs.microsoft.com/en-us/graph/webhooks#managing-subscriptions, but the high level steps are:

  1. When the Subscription is created, a POST request is sent with Content-Type text/plain and a query parameter that contains a validationToken. Microsoft Graph expects a 200 OK response and a text/plain body that contains the validationToken to be able to successfully create the subscription.
  2. Immediately after creation, and for every change notification, a POST request is sent with Content-Type application/json. Microsoft Graph expects a 202 Accepted response to this, if not it will continuously try to send the same change notification. You should also check the ClientState for verification, and of course include your own business logic for what to do in the Flow for when changes occur.

So your Flow needs to address these requirements, and there can be different ways to achieve this, but this is how I did it in my setup:

After the initialize variable, I created a Switch control action, using Content-Type from the HTTP Request trigger. In this way I can separate the logic in my flow for the initial creation and validation, and for the updates.

Validating the Subscription

If the Content-Type is text/plain; charset=utf-8, I will try to get the validation token sent by Microsoft Graph, and create a response that returns status 200 OK and the validation token back in a text/plain body.

I used the Compose data operation, and a custom expression that retrieves the validationToken from the query parameters. The expression I use is: @triggerOutputs()[‘queries’][‘validationToken’].

The Request Response action returns a status code of 200, set the Content-Type to text/plain and for Body select the Output from the Compose Validation Token.

The above steps should be sufficient for creating the subscription, it is recommended to validate the ClientState also, in my case I have done that in the next step.

Processing and Responding to Change Notifications

The change notifications will come as Content-Type “application/json; charset=utf-8”, so I add a case for that under my Switch. Microsoft Graph expects a 202 Accepted Response, it is recommended to respond this early in your flow, so that any later actions or your own business logic generates errors that prevents this response.

In the Response action I just return 202 Accepted. I have then added a Parse Json action to make it easier to reference the values later in the flow:

You can generate the schema from an existing sample, from the Microsoft Graph docs, referred above, or you can just use the following sample from me here:

{  “value”: [    
{      “subscriptionId”: “<subscriptionid>”,      
“clientState”: “MyGraphExplorerSecretClientState”,      
“changeType”: “updated”,      
“resource”: “communications/presences(‘<userid>’)”,      
“subscriptionExpirationDateTime”: “2020-07-11T20:04:40.9743268+00:00”,      
“resourceData”: {        
“@odata.type”: “#Microsoft.Graph.presence”,        
“@odata.id”: “communications/presences(‘<userid>’)”,        
“id”: null,        
“activity”: “Away”,        
“availability”: “Away”      },      
“tenantId”: “<tenantid>”    }  
]}

Now I can begin my business logic. As the value from the notification body is of type array, an Apply to each block will be needed, it will be automatically added if you select any of the attributes in the resulting actions. I will first verify that the clientState is matching the variable i defined in the beginning of the flow. This is a safestep that only the Graph subscription I created can call this Flow.

In the next part I have added a new Switch control, where I retrieve the presence status that have changed.

And this is where you add your own business logic, so I will stop here. In my own example (see PowerApp for Hue Lights above) I will control my lights based on presence updates, but this is a theme for a upcoming blog post.

With the Flow ready, we can now create the Subscription.

Creating Microsoft Graph Change Notification for Teams Presence

I will now create the Subscription in Graph Explorer, using a POST request to https://graph.microsoft.com/beta/subscriptions. In this POST request you will need a request body that contains the following:

Here is a quick explanation of the above values:

  • changeType: can be “updated”, “created”, “deleted”. Only “updated” is relevant for presence changes.
  • clientState: For verification in the webhook that the call is coming from the expected source, consider this a sensitive value.
  • notificationUrl: This is the webhook uri, and for my Flow this will be HTTP POST URL shown earlier.
  • resource: This is the user I want to get presence updates from. Note that you can get multiple user presences also using /communications/presences?$filter=id in ({id},{id}…) (https://docs.microsoft.com/en-us/graph/api/resources/webhooks?view=graph-rest-beta)
  • expirationDateTime: Every subscription only lasts for a specific time depending on resource type. The subscription needs to be renewed before expiry. For Teams Presence subscriptions only last one hour, same as chatMessage. See details for different types of resources: https://docs.microsoft.com/en-us/graph/api/resources/subscription?view=graph-rest-beta#properties. If you select an expiry time that is larger than supported, it will reset to one hour max.

Let’s run this query to create the subscription, if everything works, I will now get a 201 response that the subscription is created. Note the expiration time that is one hour from now:

This wouldn’t work if my Flow had failed, so let’s look into the run history on that. I see that I have 2 recent successful runs:

Let’s look into the first one. I see that the request is coming in as a text/plain with a validationToken, which I then return to MIcrosoft Graph:

Let’s look into the second run. This returns the first presence update, I can see that the user is “away”:

Now, let’s test this. With my user I go to Teams client, it should automatically change my presence from away to available or busy depending on my calendar, or I can set the status manually myself:

Checking the Flow runs again, I can indeed see that the Flow has been triggered via the Change Notification:

If I look into the details, the status update is Available:

If I bring up the Flow in a bigger picture I can indeed see that my logic ends up where the switch case checks for available status:

My Flow will now be triggered for every change as long as the subscription don’t expire.

Summary

In this blog post I have shown how you can create a Microsoft Graph Subscription for Change Notifications to selected users Teams Presence, using Power Automate to validate subscriptions and process the changes.

There are more work to do, as I need some logic for renewing and managing my subscriptions. But that will be the topic for the next blog post coming soon!

Thanks for reading, hope it has been useful 🙂

Remote Authentication and Controlling Philips Hue API using Postman

A while back I bought my first starter kit of Philips Hue lights and bridge, and since then I’ve bought several more bulbs, lamps, lights strips and more. One of the things I quickly got interested in was how to use the very well developed Hue API (https://developers.meethue.com/develop/hue-api/). You need to register and login with a Hue Developer Account, which is free, to be able to explore the API documentation, and for following the steps I describe in this blog post.

If you want to have a very quick look at the Hue API, you can start with this link which does not require a Hue Developer Account: https://developers.meethue.com/develop/get-started-2/. If you are familiar with Postman for exploring API’s, which may have brought you to this blog post to begin with, you should read on as I will describe and share some of the work I have been doing.

When I started exploring the API, I did what most people do: I searched online for tips and how-to’s. I found the following useful Postman Collection on GitHub: https://github.com/moldedbits/philips-hue-postman. So I downloaded that collection, and after configuring my Postman environment with the IP address to the bridge, and pushed the linked button to create a username, all described above on the get started link, I was good to go!

However, this let’s me explore the API only locally, requiring me to run the commands from a local device. This is where I should let you know my real reasons for wanting to use the Hue API, as I work with Microsoft and Cloud solutions daily. I wanted to look into scenarios where I can manipulate my Hue Lights dependent on events from the Microsoft Platform. It could be updating the lights to reflect my presence in Teams, it could be blinking the lights if some alerts happened, and so on. I want to be able to use Azure Functions, Power Platform, PowerShell etc. for controlling my Hue API.

To be able to do that, I need to be able to run Hue API commands remotely, and luckily the Hue API support that as well: https://developers.meethue.com/develop/hue-api/remote-api-quick-start-guide/.

Before I go into the details of this blog post on how I set up remote authentication and control of my Hue API using Postman, I first wanted to share a proof and sneak peak of a coming blog post that I have succeeded in what I’ve wanted, I can now authenticate and control my lights using for example Power Platform:

It was really the foundation of what I learned using Postman to do Remote Authentication to Hue API that made me able to create the solution above, so lets get into the details.

Create a New Remote Hue API app

The first thing you need to do after you have create a Hue Developer account, is to create a new Remote Hue API app here: https://developers.meethue.com/my-apps/.

You need to specify the following required fields:

  • App name: A display name for your remote app
  • Callback URL: This is needed for the Oauth2 consent and returning an authorization code. Specify https://app.getpostman.com/oauth2/callback here.
  • Application description: A short description for your remote app.
  • Optionally you can specify contact details.

After submitting your new app is ready, and also an AppId, ClientId and ClientSecret will be provided, for example:

You are now ready to start setting up your Postman client. If you don’t have Postman installed you can get it at https://www.postman.com/downloads/.

Create Postman Environment and Collection

In the Postman client I have saved all my variables and requests against the Hue API in an environment and collection. At the end of this blog post, I will share with you the link to a GitHub repository where you download these samples for you to start with yourself.

As I will be using several variables with my Postman queries against the Hue API, I’ve created an environment like the following:

Most of these variables are empty to begin with, but you can start by filling out:

  • ClientId: From the app registration at your Hue Developer Account.
  • ClientSecret: From the app registration.
  • AppId: From the app registration.
  • DeviceId: This is the id of the client you will be running queries from. It could be anything like the name of the computer, or the environment you are running from. I just chose my surname and postman.
  • DeviceName: Anything Display Name for the device you run the queries from.

The rest of the variables will be empty for now, but they will be calculated and updated based on responses later.

Also, since collections and variables can be synced and shared with other users or devices, I keep the sensitive variables like client id and secret only in current value column.

After creating my environment, I’ve created a collection with folders for my requests, for example like the following:

We can now proceed to start with Remote Authentication to Hue API.

Remote Authentication to Hue API

Hue Remote API use Oauth2 authorization code flow to get an access token that has to be included in the HTTP Authorization Header for every request to the API. We will use the app registration we did earlier to authenticate with the developer account and grant application access, and with the resulting authorization code we can request the access token to be used in the requests.

The documentation for Remote API is here: https://developers.meethue.com/develop/hue-api/remote-authentication/, and I will walk through the following requests in my Postman Collection:

Get Authorization Code

To get an Authorization code you need to construct an Url like the following:

https://api.meethue.com/oauth2/auth?clientid=ClientId&response_type=code&state=anystring&appid=AppId&deviceid=DeviceId&devicename=DeviceName

You can use Postman to construct the Url like the following, but you cannot really run the query from here as the Postman browser does not support the response:

You can use the code view on the right to get a preview of the request, and the copy it from there and paste it to your browser:

When you execute the authorization code url in your browser, you will be asked to log in with your Hue Developer account, if you aren’t already, and then you need to grant permission to your app registration:

After successfully granting permission, you will be redirected to the Callback Url you specified earlier, and from there you can get the authorization code:

Copy that code and paste it in the environment variable for AuthorizationCode as shown below:

Now that we have the Authorization Code, we can proceed requesting the Access Token. There are two methods for getting an access token:

  • Basic Authentication, which sends your client id and secret as base64 encoded strings in the authorization header.
  • Digest Authentication, which use a more secure challenge-response handshake that handle the credentials more securely.

See documentation for more details on whether to use basic or digest. I will show both in the following.

Get Access Token with Basic Authentication

To get an access token with basic authentication, create a POST request to the token endpoint, specifying the following query parameters:

  • code=authorization code you received in the previous step, using the environment variable
  • grant_type=authorization_code

In addition to that you need to set the Authorization Header to type Basic Authm and specify the ClientId and ClientSecret as shown below:

Postman has this great feature called tests that you can use to run javascript commands before (pre-request) or after queries. You can use this to save response details and update your environment variables. In my example I’m saving the response which would include the access and refresh tokens, and update my environment variables with that:

If I now run this request (and you haven’t waited to long to the authorization code to expire), you should get a successful response:

These tokens expire after the specified amount in seconds. For the Access Token this is 168 hours = 7 days. The Refresh Token expire after 112 days.

This means that after 7 days, you will need to use the refresh token to get a new access token. And when you do that, you will get a new access token valid for 7 more days, and a new refresh token again valid for another 112 days. This means in theory that as long as you refresh the access token regularly, this can last “forever” for your client.

You can also verify that the environment variables for access token and refresh token has been updated based on my javascript from above:

Refresh Token with Basic Authentication

If your access token has expired, you will get a response like the following:

To refresh the access token, you need to use the oauth2/refresh endpoint and with the query parameter grant_type?refresh_token:

The same as getting access token with basic authentication, also when refreshing you need to specify the authorization header to use basic auth, and with client id and secret as username and password:

In Headers, specify that the Content-Type is application/x-www-form-urlencoded:

And in the Body of the request, specify the environment variable for refresh token as form parameter like this:

Under Tests I have the same javascript as shown earlier, which lets me save the refreshed access token and refresh token to my environment again. When I run the request to refresh the access token, I should get a successful response like the following:

Now that I have an access token and refresh token, I’m all set for doing the necessary remote config for getting a username to use in the requests, but first I will show how to do Digest Authentication. If you want you can skip right to the Remote Configure Link Button and Create User section below.

Get Access Token with Digest Authentication

When you want to get an access token using Digest Authentication, you will need to start over again with getting an authorization code as described earlier. After that I will just request an access token with a POST request to the oauth2/token endpoint with the query parameters of code=authorization code & grant_type=authorization_code. This is basically the same request as I used with basic authentication, but with one important difference: I will not specify the Authorization Header to be basic auth.

When I run this request without a authorization header, I will get a 401 unauthorized response like the following. In the response Header and the WWW-Authenticate key I will get the first part of the challenge-response needed for Digest Authentication, the value called Nonce:

As with previous requests, I run a javascript test that extracts the response, in this case it is a little more complicated as i need to examine the response header and split so that I can get the Nonce value. This is so saved to the Nonce environment variable like this:

Now that I have the Nonce I can proceed requesting an access token with Digest Authentication. This time I go to the request Headers and add an Authorization Header with a value as shown below:

This requires a little more explanation. The Authorization header consist of the following:

Digest username=”{{ClientId}}”, realm=”oauth2_client@api.meethue.com”, nonce=”{{Nonce}}”, uri=”/oauth2/token”, response=”{{ResponseMD5Hash}}”

, where Nonce is the value I retrieved from the WWW-Authenticate response header in the first request, and the response is a calculated MD5 Hash as explained in the documentation linked earlier from Hue Remote API. But how did I get this value calculated? This time around I’m using a pre-request script to calculate that, based on the formula from the documentation (marked in green comment below):

So this little pre-request script calculates and sets the ResponseMD5Hash environment variable, so that I have that available when constructing the Authorization Header for Digest authentication.

Before I proceed with requesting an access token using Digest Authentication, I need to put together a Test script that do 2 tests:

  1. Check if there is a WWW-Authenticate response header, and if so save the Nonce to environment variable.
  2. Check if there is a successful json response and if so save the access and refresh tokens to environment variables.

My script now looks like this:

This means that I will need to run the request twice in succession, the first time I will get a 401 Unauthorized response, in which I will get the Nonce:

The second time I will get the 200 OK and a JSON response:

I now have an access token using Digest Authentication:

Refresh Token with Digest Authentication

When the Access Token expire, I will need to use the Refresh Token to get a new Access Token with Digest Authentication. Refreshing using Digest is based on the same challenge-response flow as getting the access token.

First I need to construct a POST request to the oauth2/refresh endpoint like below:

Then I have to add to the Header Content-Type: application/x-www-form-urlencoded, and the Authorization using Digest like shown below:

This Digest Authorization Header is the same as with requesting the access token, with one important difference, uri is “/oauth2/refresh“.

Next, in the Body I need to specify the refresh token:

In the Pre-request Script, make sure to use “/oauth2/refresh” when calculating the md5 hash:

And the Tests scripts are the same as with getting an access token:

Remember that refreshing the access token using Digest will follow the same pattern as getting the access token, you will need to run the refresh request twice in succession for getting a 401 Unauthorized and then 200 OK like shown above.

Get Access Token with Postman Oauth2 Authorization

With the collection of requests shown above, you should now be able to use the access token and refresh when needed for as long time as you need to. Before I proceed to configuration and accessing my lights, I will add one more method of getting access token with Postman: Using the built-in Oauth2 Authorization method.

Lets start by creating the following GET request, this is just a simple request to get configuration from my HUE bridge:

Next, go to Authorization, select Oauth 2.0 and add Authorization data to Request Headers as shown below. Then click on the Get New Access Token button:

In the following dialog, give a name for your Token, and make sure that grant type is Authorization Code. Select Authorize using browser, and note the Callback URL. You need to copy this URL. Construct an Auth URL using your environment variables like I have shown below, the Access Token URL, specify ClientId and ClientSecret variables, type any string for state, and make sure that Client Authentication is Basic Auth header:

Now, before you click on Request Token, you need to go back to your Hue Developer Account and change the Callback URL to the one above:

When you have done that, you can click on Request Token, which will launch you default browser:

You need to be logged in with your Hue Developer account and grant permission:

After that you will get the following message when redirected:

You might have some issues with pop up blocker or redirects:

If everything works as expected, your Postman client will receive the callback and complete then authentication:

And you should receive your Access Token:

Note the little warning about partially support:

Click the Use Token button, which should fill in the Access Token field. Now click Send on the request:

This should now response with some config details of your Hue Bridge, like the name and versions. If you go to Headers and show the hidden Headers you will see that Authorization Header has been set with Bearer and your Access Token.

So going forward, and for the rest of this blog post, you now have two options for using Access Token:

  1. Use the Access Token from environment variables that I have shown how you can retrieve with Basic or Digest Authentication, and Refresh when needed.
  2. Use the Oauth2 Authorization for every request (or set it on the parent level of your collection of requests). When the Access Token expire, it won’t refresh automatically, so you need to Request a new one.

Remote Configure Link Button and Create User

Now for the next part we will have to remote configure the link button, so that the bridge will accept creating a new user for us to be able to control the lights.

Enable Link Button

Users of Hue will remember that you from time to time that you need to push the link button on your bridge when adding new users. With Remote API we can do this without having to run around the house 😉

Create a PUT request like the following. On Headers specify Authorization and value to be Bearer {{AccessToken}}, which will get your environment variable from the previously retrieved access token. PS! For all the following requests: Always include this Authorization: Bearer {{AccessToken}} header!

Specify Content-Type: application/json:

Next, set the Body to {“linkbutton”: true}:

And then Send the Request, you should get a success response like below:

You now have 30 seconds to add your user..

Add Whitelist Identifier

Next, create a POST request to the Bridge endpoint that specify devicetype in the Body, like shown below, where devicetype is a named description of your client and app, for example:

Run the request, but if 30 seconds have passed, you will get this response:

In that case, just run the PUT request again to enable the link button, and try again. When successful you should get a response like the following:

This username have to be included in every Hue Remote API request later, so make sure to save it in your environment variables:

Get All Configuration

Your starting point now for all requests are https://api.meethue.com/bridge/{{username}}/ (and remember to include the Authorization: Bearer {{AccessToken}} header always).

If I want to list all configuration, I can run this GET request:

Which will provide me with all the configuration details of my Bridge:

We are now ready to start controlling the Lights and more!

Control Lights Remotely

If will now show you some sample requests for controlling lights via Hue Remote API:

List All Lights

If I want to list all my lights, I can just run a GET request: https://api.meethue.com/bridge/{{username}}/lights/:

The response will show all lights, numbered from 1 to count of lights connected to your bridge. I’ve collapsed some details in the response above, but you can see from my light number 12 that the state is “on”, you can see the brightness, color and more.

PS! Every light is a new typed object, and not an array, which I find a bit unfortunate, so you need to be aware of that if you want to loop through your lights for example.

If you want to get a specific light, just add the light number at the end of the GET request URI, like this: https://api.meethue.com/bridge/{{username}}/lights/12

Turn a Light On/Off

You can control the state of a specific light by doing a PUT request to your specified light number, for example light number 12: https://api.meethue.com/bridge/{{username}}/lights/12/state

You need a Request Body that tells what the light state should be. For example to turn the light off, just include a body with {“on”:false}, the response will confirm success (and you should be able to visually confirm as well ;):

Likewise, to turn on again:

Controlling Light State and Colors

Now you can really start playing with your lights with custom states, you can change brightness, go from cold white to warm white, or maybe set a color scene for the millions of colors available if your light supports color. More info can be found here:

https://developers.meethue.com/develop/get-started-2/core-concepts/#controlling-light

Light color are primarly controlled by the CIE color space, where you specify an array of two values between 0 and 1 expressed as x and y.

Source: https://developers.meethue.com/develop/get-started-2/core-concepts/#controlling-light

Here is an example where I both control brightness (1-254) and a custom color:

Now, you can just experiment and learn for yourself. A good tip is to use your Hue App on your mobile phone to set the color you like, and then use GET to get the light state color settings.

Summary and Github Repo

I hope you have learnt a lot about Hue Remote API when reading this, I certainly learnt a lot exploring and documenting this via Postman. This understanding has provided me with the knowledge and tools to look into automation and integration scenarios with the Microsoft solutions I use daily in my job. I’ve already been doing this with PowerShell, Azure Functions, Logic Apps and Power Automate to name a few. There is a blog post coming soon on Power Platform and how I did that to control my lights based on Teams Presence and more.

So whether you are a developer that loves to write code, or maybe you are more into the low-code, no-code solutions, I hope this has been helpful.

I promised I will share my Postman Collection and Requests for Hue Remote API, so here it is: https://github.com/JanVidarElven/HueRemoteAPI-Postman-Collections

Knock your self (or your lights) out, and please feel free to comment or contribute 🙂

Oauth Authentication to Microsoft Graph with PowerShell Core

In this short blog post I will show you a really simple way to get started authenticating with Oauth to Microsoft Graph using PowerShell Core.

As you might know, PowerShell Core is the open-source, cross-platform (Windows, macOS, and Linux) edition of PowerShell, with the latest version today being PowerShell 7.0 ( https://docs.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-70?view=powershell-7). The latest version of Windows PowerShell is 5.1, and runs side-by-side with PowerShell Core.

I’ve been using Windows PowerShell mostly previously, as I’m running Windows 10 and use a lot of PowerShell modules that variously depend on SDKs or other Windows Components, but I’m increasingly using PowerShell Core in scenarios for example with Azure Functions PowerShell ( https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-powershell). My computer now runs Visual Studio Code and Windows Terminal with different Profiles, so that I can easily change between environments and run side-by-side:

This blog post isn’t about the details of PowerShell though, so lets get back to the topic at hand. When running PowerShell against Microsoft Graph you would run requests using the command Invoke-RestMethod. The thing that caught my attention was the added parameters of specifying Authentication and Token with the request: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-restmethod?view=powershell-7#parameters.

Let’s compare the “old” way and the “new” way, but first lets get an Access Token for Microsoft Graph.

Get Access Token using Graph Explorer

If you want to learn Microsoft Graph and start with some sample queries, Graph Explorer should be your main starting point: https://developer.microsoft.com/en-us/graph/graph-explorer/preview. The new preview version of the Graph Explorer lets you get a copy of your Access Token if you sign in with your account as shown below, you can copy it to your clipboard:

This Access Token will run queries as the logged on user (yourself) using the consented permissions in Graph Explorer, so you should only use this token for your own sample queries and not share it with anyone else.

Preferably you would get an Access Token by registering your own App Registration in Azure AD and authenticate using one of the following authentication flows:

  • Authorization Code Flow (Delegated User – Web Apps and APIs)
  • Device Code Flow (Delegated User – Native Clients, recommended for PowerShell)
  • Client Credentials Flow (Application Permissions – Scripts and Background Processes that don’t require user interactivity)

But for now I would use use the Access Token from Graph Explorer for the following samples.

The “old” way, using Authorization Header with Bearer Token

This method works with both Windows PowerShell and PowerShell Core. After aquiring an Access Token you would need to specify an Authorization Header with a Bearer Token for every request. In this example I get the Access Token from the Clipboard, as I copied that from the Graph Explorer.

$accessToken = Get-Clipboard
Invoke-RestMethod -Method Get -Uri "https://graph.microsoft.com/v1.0/me" -Headers @{"Authorization"="Bearer $accessToken"}

This should return a response like below:

The “new” way, using Authentication and Token Parameters

This method only works on PowerShell Core. The Access Token needs to be a secure string, so you need to convert it first like the following:

$accessToken = ConvertTo-SecureString (Get-Clipboard) -AsPlainText -Force

Another way would be to prompt for the Access Token and paste it with the following command:

$accessToken = Read-Host -AsSecureString -Prompt "Enter/Paste Oauth2 Token"

And then you would run the request specifying the Authentication parameter to be Oauth, and the Token parameter to be the secure string:

Invoke-RestMethod -Method Get -Uri "https://graph.microsoft.com/v1.0/me" -Authentication OAuth -Token $accessToken

This should return a response like below:

Summary

So in summary you can start using the Authentication and Token parameters in PowerShell Core, it would add / override the Authorization Header and Bearer Token for you. There can also be some added security gains as the Token is submitted as a securestring, which means you can try to keep it out of code as clear text.

How I as a Soccer Coach….

…..moved trainings and meetings online using the modern collaboration tools I know and love!

When I’m not working or fulfilling my community activities as MVP, I spend many evenings and weekends at the soccer field, where I’m coaching and managing a soccer team consisting of 14 year old boys.

Due to the Coronavirus situation, as in many countries also Norway has closed it schools, many businesses are either closed or working from home, and in general we are all following the rules of isolating to make sure the virus doesn’t spread. And of course, this has also resulted in closing all grass roots football, for the time being at least to the end of April. I quickly understood that I needed to think in new ways..

So I decided to use the tools I have at hand, and I created virtual follow ups using tools like Microsoft Teams, Forms, Power Automate and SharePoint Online among a few to help support the boys doing self practice and have Virtual team Meetings..

This blog post is a technical version of a LinkedIn article I wrote in Norwegian, https://www.linkedin.com/pulse/hvordan-jeg-som-fotballtrener-jan-vidar-elven/, explaining more the reasons and why I set this up. In this post I will go more into the technical setup, and share some resources for those that want to learn or maybe do something similar themselves. Some of the screenshot images are in Norwegian, but you should be able to understand from my comments.

It all started with Microsoft Teams.. and a Form!

I knew already from before that Microsoft Teams would be more central in my daily work, but I also observed that my son and his classmates, that also plays on the aforementioned soccer team, use Teams themselves now for digital schooling at home. They have daily Teams meetings, as well as some online classes and homework delivery.

So I thought that I wanted to set up a player meeting on Teams, and later a meeting with their parents. This way we could have a social and digital arena to meet each other, as well as talk with the boys how we wanted them to do training with self practice at home. So the first thing was to invite to online meetings.

I knew that the boys already used Teams in school, but I needed to collect their school e-mail addresses. I also suspected several of the parents use Teams at their workplace, but not everyone, so I also needed to get an overview on that. So I decided to create a questionnaire in Microsoft Forms:

How I set it up:

I created a Form with the following inputs:

  • Name (Text). The person filling in the form.
  • Using Teams from before? (Choice). Yes, No and Don’t Know option for parents to answer if they use Teams already.
  • Parents, Teams e-mail address (Text). Their existing Teams work e-mail address or personal e-mail address.
  • Players, Teams e-mail address (Text). The Teams e-mail address they use at school.

In addition I used the club logo and customized the theme colors for the Form. Then I selected Share settings and selected so that “Anyone with the link can respond”. This will mean that all responses are anonymous, so that’s why I require that they type their name in the first input. Then I distributed the Forms link to the parents.

After I received all the responses, I created a Teams meeting invite to the players for the players online meeting, and a Teams meeting invite to the parents for the parents online meeting.

We were now ready for our inaugural online meetings!

Organizing Self Practice Trainings

Normally our soccer team practice at least 3 times a week, in addition to playing games or other activities. So before we had the online player meeting, a self practice training plan was created, focusing on 3 weekly trainings:

  • Conditioning (interval runs, with or without ball, dribbles, jumps and obstacles etc.)
  • Technique, Agility and Strength (ball possession, passing, runs, sprints, physical strength)
  • Endurance (long hikes and outdoor activity with family)

These training should be completed after plan where the boys will get approved attendance for each training they complete. To get a training approved they needed to self register a training form, as well as document by sharing pictures, video, screenshots of activity etc.

The players were shown examples of activities and drills they could complete themselves or with help of family. With this organization and training plan, the only thing that was missing was a system for registering self practice and how I could follow up on that.

I decided to create another Form. In this Form which the boys got a shared link to, they could register their name, date, self assessment, what kind of activity they did and type, provide a description and optionally register number of minutes and kilometres. They were also asked to send in documentation with video, pictures, etc.

This worked really great and next week we already had a lot of responses for completed trainings:

And from the media I received I could really see that the boys were doing their self practice:

How I set it up:

I created a Form with the following inputs:

  • Name (Text). The player name filling in the form.
  • Activity Type (Choice). Conditioning, Technique + Strength, or Endurance.
  • Date of Activity (Date).
  • Self Assessment (Rating). 1-5 stars where they could evaluate their own session.
  • Type of Condition (Choice). If they did conditioning work, what kind of interval (4×4, 60×60, 15×15).
  • Technique + Strength (Text, Long). Comment field for explaining how they did technique and strength work.
  • Endurance (Text, Long). Comment field for explaining what kind of long activity they did.
  • Number of Kilometres (Text, Restricted to Number). Optionally how many kilometres they practiced.
  • Number of Minutes (Text, Restricted to Number). Optionally how many minutes the activity lasted.
  • Sent media of activity (Choice). Yes or No for if they have sent picture, video or screenshot to our team e-mail address.

Also in this Form I used the club logo and customized the theme colors. Then I selected Share settings and selected so that “Anyone with the link can respond”.

PS! I was looking into a File Upload response in the Form, but this cannot be added to a Form that are shared externally. That is why I needed the boys, or their parents, to send their media files to our e-mail address in addition to the Form registration.

Following up on Registered Self Practice Trainings

With all those great responses coming in, I could look through the responses in Office Forms, and download an Excel copy of the responses, but I needed something more to follow up the trainings. So I created a private Team in Microsoft Teams:

Next I wanted to get all the responses from Forms into a SharePoint List. I created the List into the Teams SharePoint Site so that I could get all the registered self practice trainings in one place, and be able to do edits if needed. I also added some extra columns for approve the training and if there was sent media documentation:

Now the only thing I needed was some kind of automation that could bring every response from Forms over to this list: Enter Power Automate!

With the help of Power Automate I created the following Flow to automate that every time a new response is submitted in the Form, this would trigger the Flow:

Next I needed to do some magic on the number inputs, I’ll get back to that in the “How I did it:” section, but the Flow then created a new SharePoint List Item:

How I did it:

The first thing I did was to create the Team that would also host the SharePoint Online Site for my list. The Team was created in my own tenant as a private team, and I elected not to invite any player, or parent, to the Team as guests at this point.

The SharePoint list was created with the following columns to reflect the Form:

  • Renamed Title column to Name
  • Type of Activity (Choice, with the same values as from the Form)
  • Date (Date and Time, Include Time: No)
  • Self Assessment (Number)
  • Type of Fitness/Conditioning Activity (Choice, with same values as from the Form)
  • Technique + Strenght (Multiple Lines of Text)
  • Endurance/Long Actitivty (Multiple Lines of Text)
  • Sent Picture (Yes/No)
  • Number of Km (Number)
  • Number of Mins (Number)
  • Approved (Yes/No)
  • Submitted (Date and Time, Include Time: Yes)

After the list with all the columns was created, the next step was to create the Flow in Power Automate. You can create Flows directly from the SharePoint List, as shown below, and then use one of the provided templates:

Myself I started at https://flow.microsoft.com and selected to create a new Flow as Automated – from blank, and the selecting the trigger for “When a new response is submitted”:

After I give a name to the Flow and select which Form I want to trigger on responses from, I add an Action to get the response details, as shown below:

The next part is a little more complex. When a Form response is submitted, all response details will be provided as Strings. And if I try to update those values directly into the SharePoint list, it will fail because the Column require a number format. So I need to convert those string values to either Integer or Float respectively.

Power Automate have some Data Operation Actions I can use in Flows, and I have used the following three Compose actions, where I get the Self Assessment, Number of Km and Number of Min response details:

The Compose action will create objects I can refer back to later in the Flow. The next complex thing is that I need to check if there are any values for number of kilometers and minutes, and these Form fields are not required and can be empty. There could be several ways to do this, I did it this way:

As shown above, I first add an Action for Initialize Variable, and give it the name for AntallKm (Number of Km), and set the initial value to 0. I specify that the type is Float (as I want to have decimal values). Next I add a Condition action, where I check if the ComposeAntallKm is empty, meaning it is equal to false. I do this as an expression, where the expression is using the empty function and checking against the output from the ComposeAntallKM earlier: empty(outputs(‘ComposeAntallKm’))

If this condition evaluates to false, meaning that there has been provided a value in the Form, then the Flow will go to the If Yes action, and I convert the string to a Float value with the expression: float(outputs(‘ComposeAntallKm’)), I’ve tried to illustrate that with the green arrow below. This expression would have failed the Flow if I didn’t check if it was empty or not. If the value is empty, it would go to the If No action, and this is just empty because I then just let the value be the default 0 i provided when I initialized the variable (illustrated with the red arrow below).

Next in the Flow, I do the exact same logic for the number of minutes:

I’m now ready to update the SharePoint List with a new item. I select the SharePoint Online Create item action, and after specifying the Site and List name, I’ll choose most of the item values from the dynamic content picker. The Km and Minutes values are picked from the respective variable. And for Self Assessment, I do this with an expression consisting of the int function that converts the string to an integer from the Compose action further up.

For reference, my entire Flow at this point is shown below (collapsed without action details):

Take it to the Next Level – Teams Bot with Adaptive Card!

At this point I have a working solution where I get all new responses put in to the SharePoint List. I can now look through, edit and approve the registered trainings.

An even cooler solution would be that each registered training will be posted to the Team Channel as an adaptive card, where I can edit and approve and submit that back so that the list would be updated directly. That way I can follow up and check trainings in my Teams client on either my PC or Mobile, without needing to go to the SharePoint list.

So I added a couple of more steps to my Flow after the Create item action. First I added an action for posting an Adaptive Card to a Teams Channel and wait for a response, the next step was to Update the list item with the values.

I will go into more details later under the “How I did it” section, but the end result of this was that every time a player submits a new training activity, I will get this Adaptive Card in my Teams Channel, summarizing the details of the training and providing me with the ability to edit the player name in case they typed it wrong, update any values for km or minutes, and select yes or no for if the player has sent photos or the training is approved:

When I click the Update button, the values are submitted back to the Flow and the List item is updated.

How I did it:

Before I go into the Flow details on how to post adaptive card to Teams channel and process the response back, I want to show how I built the adaptive card.

Adaptive cards are posted as a JSON formatted message. You can read more about it here https://adaptivecards.io/, see samples and usage scenarios and more. There’s also a great resource called the Adaptive Cards Designer, https://adaptivecards.io/designer/, where you can build your own cards from blank or using one of the sample templates.

The designer lets you add controls and configure properties visually, while producing the JSON message you will need later. This is the design format of the card I ended up with, a little bit of Norwegian text here but you get the main idea, note that I have some placeholder values her, from where I will add data from my Flow later:

In the “Card Payload Editor” window you will see the JSON format you will need use in your Flow, and in the following snippet I’ll provide you with my JSON message for this example here for you to reuse or build on as you like:

{
    "type": "AdaptiveCard",
    "version": "1.0",
    "body": [
        {
            "type": "Image",
            "altText": "Borgen IL",
            "url": "https://borgensawebstorage.z6.web.core.windows.net/borgen_logo.png"
        },
        {
            "type": "TextBlock",
            "size": "Large",
            "weight": "Bolder",
            "id": "Title",
            "text": "Ny Egentrening Registrert!",
            "horizontalAlignment": "Left"
        },
        {
            "type": "TextBlock",
            "text": "Ny egentrening er registrert av <NAVN>.",
            "wrap": true
        },
        {
            "type": "FactSet",
            "facts": [
                {
                    "title": "Type Treningsøkt",
                    "value": ""
                },
                {
                    "title": "Dato",
                    "value": ""
                },
                {
                    "title": "Egenvurdering",
                    "value": ""
                },
                {
                    "title": "Økt beskrivelse",
                    "value": ""
                }
            ]
        },
        {
            "type": "TextBlock",
            "text": "Verifisering",
            "size": "Large",
            "weight": "Bolder",
            "color": "Attention"
        },
        {
            "type": "TextBlock",
            "text": "Verifiser treningsdata og eventuelt oppdater:",
            "size": "Small",
            "weight": "Bolder"
        },
        {
            "type": "TextBlock",
            "text": "Spillernavn",
            "size": "Medium",
            "weight": "Bolder",
            "color": "Attention"
        },
        {
            "type": "Input.Text",
            "value": "",
            "style": "text",
            "isMultiline": false,
            "maxLength": 50,
            "id": "Spillernavn_input"
        },
        {
            "type": "TextBlock",
            "text": "Antall Km",
            "size": "Medium",
            "weight": "Bolder",
            "color": "Attention"
        },
        {
            "type": "Input.Number",
            "value": "",
            "style": "text",
            "isMultiline": false,
            "maxLength": 20,
            "id": "AntallKm_input"
        },
        {
            "type": "TextBlock",
            "text": "Antall Minutter",
            "size": "Medium",
            "weight": "Bolder",
            "color": "Attention"
        },
        {
            "type": "Input.Number",
            "value": "",
            "style": "text",
            "isMultiline": false,
            "maxLength": 20,
            "id": "AntallMin_input"
        },
        {
            "type": "TextBlock",
            "size": "Large",
            "weight": "Bolder",
            "color": "Attention",
            "text": "Godkjenn",
            "horizontalAlignment": "Left"
        },
        {
            "type": "TextBlock",
            "size": "Small",
            "weight": "Bolder",
            "text": "Har spilleren sendt skjermbilde, bilde eller video?",
            "horizontalAlignment": "Left",
            "separator": true
        },
        {
            "type": "Input.ChoiceSet",
            "id": "input_media",
            "value": "1",
            "choices": [
                {
                    "title": "Nei",
                    "value": "false"
                },
                {
                    "title": "Ja",
                    "value": "true"
                }
            ],
            "style": "expanded"
        },
        {
            "type": "TextBlock",
            "size": "Small",
            "weight": "Bolder",
            "text": "Er egentreningen godkjent?",
            "horizontalAlignment": "Left",
            "separator": true
        },
        {
            "type": "Input.ChoiceSet",
            "id": "input_godkjent",
            "value": "1",
            "choices": [
                {
                    "title": "Nei",
                    "value": "false"
                },
                {
                    "title": "Ja",
                    "value": "true"
                }
            ],
            "style": "expanded"
        }
    ],
    "actions": [
        {
            "type": "Action.Submit",
            "title": "Oppdater"
        }
    ],
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}

With that JSON message ready, I can now add the “Post an Adaptive Card to a Teams Channel and wait for a response” action to my Flow. I select my Team, and the Channel I want to post the adaptive card to, and then paste in the JSON message from the Adaptive Card Designer:


As you can see from the images above, I’ve added dynamic data from the Flow where I had placeholders for values. Note also that I use the values “false” and “true” for the Input.ChoiceSet, this will make it correct when I set the values back to the updated List item.

PS! Another important thing to note is the “id” property of the inputs I want to be able to update back to the Flow. This “id” property needs to be specified later.

The Update message is what will be shown back in Teams after the Adaptive Card has been updated and submitted back with the Action.Submit button. It will look like this:

After the card is updated, a response will be sent back to the Flow. So my next step would be to add a Update item action for updating the selected values in the SharePoint List. From here I will select the SharePoint Site and List Name, and then getting the Id for the existing list item I want to update. This Id is from the earlier action in the Flow from where I created the list item:

The values I want to update back in the List from the Adaptive Card Response is shown above with the blue Teams icon. I will have to specify these by adding an expression that looks like the following, as there are currently no dynamic output from the previous action I can select:

outputs('Post_Adaptive_Card_to_Egentrening_Teams_Channel_Wait_Response')?['body/data/Spillernavn_input']

Note the following from above, refers back to the action name (since I had blank spaces in the step name, I will have to refer back to it with underscore), and from the response body and data section I will refer to the “id” property of the adaptive card input.

Create similar expressions for the other inputs, and that should be it! The complete Flow step is now like this:

Summary and Next Steps

In this quite lengthy blog post I have shown how I built myself a great follow up solution for my soccer team self practice trainings. The boys find it easy to use, and I can use the tools and solutions I know and love for following up. I have also learnt quite a bit of new tricks and tips 😉

I also start to have some great statistics, I can summarize and rank the players so that I can create a top 10 list for example. And these ranks can be published to the boys, they do love a competition and this can motivate them to do some extra work. I have created this HTML table, that I update semi-manually now. I’m already working in the next Flow that will publish updates to this table automatically. So there might be a follow up blog post on this.. 😉

I hope this has been helpful or/and inspiring, reach out to me if you have questions, and remember the Power of the Flow 🙂

Azure Advent Calendar 2019 – Azure Information Protection

Today is 18th December and my turn to being part of the awesome Azure Advent Calendar. Thanks to Gregor Suttie (@gregor_suttie) and Richard Hooper (@Pixel_Robots) for this great initiative.

In today’s video, linked below, I’m speaking about Azure Information Protection (https://docs.microsoft.com/en-us/azure/information-protection/). I tried to keep the video under 30 minutes, but there is so much to talk about that I ended up doing a full 45 minute session. If you are new to Azure Information Protection, this will hopefully be a good start. If you already know a thing or two about AIP and RMS, you might pick up some tips and pointers as well. I will also go into the new functionality of Unifying Labeling, and look at some of the client experiences.

So with that, here is my contribution for Azure Advent Calendar 2019:

How to Use Azure AD Privileged Identity Management PowerShell and Graph API

A while back I wrote a blog post on how you could download, install and use a separate Azure AD PIM PowerShell Module for managing Privileged Roles, https://gotoguy.blog/2018/05/22/getting-started-with-azure-ad-pim-powershell-module/. With the recent update of the AzureADPreview Module, the cmdlets for managing Privileged Roles are now included in the module, so there is no longer required to install a separate module for this!

In this blog post I will explain and show how these commands can be used. These PowerShell CmdLets are also at parity with the Graph API, so I will also show equivalent methods for this.

Install or Update AzureADPreview Module

First you need to either install or update the AzureADPreview Module, so that you are running on version 2.0.2.27 or newer.

Update April 14 2020: Latest version of AzureADPreview Module is 2.0.2.89.

The AzureADPreview Module can be installed from PowerShellGallery using Install-Module or Update-Module, and you can verify which version you have installed using Get-Module <modulename> –ListAvailable like this:

image

With that requirement out of the way we can proceed to look at the commands.

Privileged Role Management Commands

Currently in the AzureADPreview Module, there are 13 commands related to Privileged Roles:

Get-Command -Module AzureADPreview | Where-Object {$_.Name -like “*privileged*”}

image

The new cmdlets in AzureADPreview Module related to PIM are documented here, https://docs.microsoft.com/en-us/powershell/module/azuread/?view=azureadps-2.0-preview#privileged_role_management.

Note that some of the above commands are not in that documentation, all the new commands have a *MS* which means it is mapped to equivalent Microsoft Graph API’s.

In the interest of this blog post, here is a quick explanation of each of the available commands:

  • Add-AzureADMSPrivilegedResource. Use this API to add a new azure AD MS privileged resource.
  • Close-AzureADMSPrivilegedRoleAssignmentRequest. Cancel a AzureADMSPrivilegedRoleAssignmentRequest.
  • Get-AzureADMSPrivilegedResource. Get azure AD MS privileged resource.
  • Get-AzureADMSPrivilegedRoleAssignment. Get role assignments for a specific provider and resource.
  • Get-AzureADMSPrivilegedRoleAssignmentRequest. Get role assignment request for a specific resource.
  • Get-AzureADMSPrivilegedRoleDefinition. Get role definitions.
  • Get-AzureADMSPrivilegedRoleSetting. Get role settings.
  • Open-AzureADMSPrivilegedRoleAssignmentRequest. Create a role assignment request.
  • Set-AzureADMSPrivilegedRoleAssignmentRequest. Update a role assignment request.
  • Set-AzureADMSPrivilegedRoleSetting. Update role setting.

The other 3 Privileged Role commands that are still available to use, but currently are not documented on the above link to docs are:

  • Get-AzureADPrivilegedRole. List all Directory Roles available for Privileged Roles assignments.
  • Get-AzureADPrivilegedRoleAssignment. List active and eligible privileged role assignments.
  • New-AzureADPrivilegedRoleAssignment. Creates a new privileged role assignment for specified role and user.

(*) As soon as your tenant is upgraded to using the updated Privileged Identity Management experience for Azure AD roles (see below) these three commands will be outdated and should not be used anymore.

Further on in this blog post I will provide some more examples and usage scenarios for the new Azure AD Privileged Role Management commands, and their equivalent Microsoft Graph API methods, but first something on the difference between Azure Resources and Azure AD, changes in management experience and current limitations in the PowerShell commands.

Azure Resources vs. Azure AD

As you might know, Azure AD PIM can be used for managing privileged role assignments to both Azure AD roles and Azure Resources:

image

The new PowerShell commands that follows the syntax verb-AzureADMSPrivilegedRole…. all require a parameter called ProviderId, which as per today only support “AzureResources”. This means that currently you can only use the new Azure AD PowerShell commands for managing PIM for Azure resources, not for Azure AD roles yet! I had this confirmed with the Microsoft Program Manager for Azure AD PIM, as you can see from the conversation https://twitter.com/stevemsft/status/1143977432690466816?s=20 and shown in the below image:

image

This is related to the following notice from the Azure AD PIM Microsoft Graph documentation, stating that Azure AD roles will move to the Azure resource API in the coming months:

image

Update March 19 2020: Tenants are now starting to get migrated to the new Azure AD PIM provider similar to Azure Resources! If you log in to your tenant and see the following info in the Privileged Identity Management blade, then you can also use the new Azure AD provider, as I will show in the examples below:

As the new PowerShell commands are built on Microsoft Graph, this also means that they will work for Azure AD roles depending o the move to the Azure resources API.

Explore Privileged Resources

image

There are two commands for exploring and adding Privileged Azure Resources:

  • Get-AzureADMSPrivilegedResource –ProviderId azureResources
  • Add-AzureADMSPrivilegedResource –ProviderId azureResources

If you want to explore Privileged Azure AD roles you have to change the ProviderId like this:

  • Get-AzureADMSPrivilegedResource -ProviderId aadroles
  • Add-AzureADMSPrivilegedResource –ProviderId aadroles
g-raph

Their equivalent Microsoft Graph API methods for Azure Resources are (Beta endpoint only as per july 2019):

  • List: GET /privilegedAccess/azureResources/resources
  • Get: GET /privilegedAccess/azureResources/resources/{id}
  • Register: POST /privilegedAccess/azureResources/resources/register

Getting or listing Privileged Resources is based on that you have onboarded to Azure AD PIM for Azure Resources, as explained here: https://docs.microsoft.com/en-us/azure/active-directory/privileged-identity-management/pim-resource-roles-discover-resources. You can also add a privileged resource by ExternalId, which I will show an example of later.

The equivalent Graph API methods for Azure AD roles are (Beta endpoint only as per march 2020):

  • List: GET /privilegedAccess/aadRoles/resources
  • Get: GET /privilegedAccess/aadRoles/resources/{tenantid}
  • Register: POST /privilegedAccess/aadRoles/resources/register (Note per march 2020: There are currently no way to programmatically onboard tenants to PIM for Azure AD roles)

Getting or listing Privileged Azure AD roles requires that you have signed up for PIM for Azure AD roles: https://docs.microsoft.com/en-us/azure/active-directory/privileged-identity-management/pim-getting-started#sign-up-pim-for-azure-ad-roles

Examples of Exploring Privileged Azure Resources

To get a list over all privileged Azure resources, just run:

Get-AzureADMSPrivilegedResource –ProviderId AzureResources

This will return a list (capped at max 200 results), with the Id, ExternalId, Type, DisplayName and more for each resource that have been registered to Azure AD PIM:

image

As mentioned above the list is capped at max 200 results, which is a Microsoft Graph limitation for this privileged resource API. You can use the –Top parameter to specify a lower number of returned results, like –Top 50,  but it will just ignore and cap at 200 if you for example type –Top 300.

So to return fewer results we can use the –Filter parameter which support Odata query. I have tried some different combinations, and not all will work as expected. Some examples of working filters:

Get-AzureADMSPrivilegedResource -ProviderId AzureResources -Filter “Type eq ‘resourcegroup'”

Get-AzureADMSPrivilegedResource -ProviderId AzureResources -Filter “Type eq ‘subscription'”

Get-AzureADMSPrivilegedResource -ProviderId AzureResources -Filter “DisplayName eq ‘elvsabootdiag001”

Get-AzureADMSPrivilegedResource -ProviderId AzureResources -Filter “startswith(DisplayName,’rg-‘)”

image

What I found DON’T work is filters for specific resource types like:

Get-AzureADMSPrivilegedResource -ProviderId AzureResources -Filter “Type eq ‘Microsoft.Compute/virtualMachines'”

..or any other specific resource type like Microsoft.Network/loadBalancers, Microsoft.Network/networkSecurityGroups, etc., which I find is a bit strange as I would like to filter on those as well.

If you know the specific resource id you can also get that privileged resource object directly:

Get-AzureADMSPrivilegedResource -ProviderId AzureResources –Id <resource id>

Lets compare this to the Microsoft Graph API methods, using Graph Explorer. To list all managed Azure Resources I run the GET /privilegedAccess/azureResources/resources like this:

image

Which pretty much returns the same list of resources and attributes as I did when running the PowerShell command. There is one important change though, and I mentioned earlier that the PowerShell command would only return max 200 results. In the Graph response I will receive a skip token from where if I run that I will get the next set of potentially 200 more results, and so on.

Graph can also handle filters of course, so lets try that. Here are some variations you can try out:

GET /privilegedAccess/azureResources/resources?$filter=type eq ‘resourcegroup’&$top=5

GET /privilegedAccess/azureResources/resources?$filter=displayName eq ‘rg-auth-dc’

GET /privilegedAccess/azureResources/resources?$filter=startswith(displayName,’rg-‘)

And if you want to get a specific resource with Graph, just specify the id like this:

GET /privilegedAccess/azureResources/resources/ad7327ba-50f4-4f03-a4ee-029f310b6775

Which will return the specific resource in the response:

image

Last, to add a resource as a managed resource to Azure AD PIM, using PowerShell can be done like this:

Add-AzureADMSPrivilegedResource -ProviderId AzureResources -ExternalId “/subscriptions/<your-subscription-id>”

And via Graph:

POST /privilegedAccess/azureResources/resources/register

Request Body:

{
“externalId”: “/subscriptions/<your-subscription-id>”
}

These commands are just to get a list of and adding managed Azure resources for Azure AD PIM, i the next parts we will look into actually managing assignments and settings.

Examples of Exploring Privileged Azure AD role resource

To get a list over all tenants registered for privileged Azure AD roles, just run:

Get-AzureADMSPrivilegedResource –ProviderId aadRoles

This will return a list of directories that have been onboarded for PIM for Azure AD Roles, with the Id referring to tenant id, Type, DisplayName and more:

If you know the specific tenant id you can also get that privileged resource object directly:

Get-AzureADMSPrivilegedResource -ProviderId aadRoles –Id <tenantid>

Lets compare this to the Microsoft Graph API methods, using Graph Explorer. To list all managed Azure AD roles directories I run the GET /privilegedAccess/aadRoles/resources like this:

Note that you also can specify the resource for Azure AD roles directly, by specifying the tenant id like this:

GET /privilegedAccess/aadRoles/resources/{tenant-id}

These commands are just to get a list and settings for tenant onboarded to Azure AD roles management with Azure AD PIM, i the next parts we will look into actually managing assignments and settings.

Explore Role Assignments

image

The following command can be used for listing or getting specific role assignments for Azure resources:

  • Get-AzureADMSPrivilegedRoleAssignment –ProviderId AzureResources –ResourceId <resource id>

And for Azure AD roles, the command would be the following, where the resource id is the tenant/directory id:

  • Get-AzureADMSPrivilegedRoleAssignment –ProviderId aadRoles –ResourceId <tenant id>
g-raph

The equivalent Microsoft Graph API methods for Azure Resources:

  • List: GET /privilegedAccess/azureResources/resources/{resourceId}/roleAssignments
  • List: GET /privilegedAccess/azureResources/roleAssignments?$filter=resourceId+eq+'{resourceId}’
  • List (Mine): GET /privilegedAccess/azureResources/roleAssignments?$filter=subjectId+eq+'{myId}’
  • Get: GET /privilegedAccess/azureResources/resources/{resourceId}/roleAssignments/{id}
  • Get: GET /privilegedAccess/azureResources/roleAssignments/{id}?$filter=resourceId+eq+'{resourceId}’
  • Get (Mine): GET /privilegedAccess/azureResources/roleAssignments/{id}?$filter=subjectId+eq+'{myId}’

And these are the main Graph API methods for Azure AD roles:

  • List: GET /privilegedAccess/aadRoles/resources/{tenantId}/roleAssignments
  • List (Mine): GET /privilegedAccess/aadRoles/resources/{tenantId}/roleAssignments?$filter=subjectId+eq+'{myId}’
  • List (Role): GET privilegedAccess/aadRoles/resources/{tenantId}/roleAssignments?filter=roleDefinitionId eq ‘{myRoleDefinitionId}’

Now that seems a lot of different variations for Graph calls for the one PowerShell command, but as you will see later Graph can be a little more flexible in querying in different ways.

Examples of Exploring Role Assignments for Azure Resources

Lets see some samples for PowerShell first. Now we need to supply a resource id, that can be subscription object, a resource group object, any type of resource objects like virtual machines, virtual networks and so on, and even management group objects. So based on the commands previously shown in the blog post, we should be able to get out the resource id’s first, for example like this:

$myResource = Get-AzureADMSPrivilegedResource -ProviderId AzureResources -Filter "DisplayName eq 'NetworkWatcherRG'"

Get-AzureADMSPrivilegedRoleAssignment –ProviderId AzureResources –ResourceId $myResource.Id

So this returns a list of role assignments for the specified resource, each assignment has its own id, as well as the ResourceId, the RoleDefinitionId (which role that has been assigned, like reader, contributor, owner, etc), SubjectId (which user, service principal, group, etc has been assigned the role). In addition we can get info on any linked eligible assignements, start and end time for assignements, assignment state and if the assignment is active or not, or if the type is inherited or assigned directly to the resource.

image

The same results can be retrieved by first querying for the resource id:

And then querying for the role assignments belonging to this resource:

Basically the above command returns the same as this blade in the Azure Portal:

image

Examples of Exploring Role Assignments for Azure AD roles

If i want to look at Role Assignments for Azure AD roles via PowerShell you would specify ProviderId as aadRoles and the ResourceId to be the tenant id:

$myTenant = Get-TenantDetailsFromGraph
Get-AzureADMSPrivilegedRoleAssignment -ProviderId aadRoles -ResourceId $myTenant.TenantId

This will return all Azure AD role assignments in your tenant:

To return fewer results we can use the –Filter parameter which support Odata query. I have tried some different combinations, here are some examples of working filters:

Get-AzureADMSPrivilegedRoleAssignment -ProviderId aadRoles -ResourceId $myTenant.TenantId -Filter "SubjectId eq '<myId>'"

Get-AzureADMSPrivilegedRoleAssignment -ProviderId aadRoles -ResourceId $myTenant.TenantId -Filter "RoleDefinitionId eq '<myRoleDefinitionId>'"

Get-AzureADMSPrivilegedRoleAssignment -ProviderId aadRoles -ResourceId $myTenant.TenantId -Filter "AssignmentState eq 'Eligible'"

Get-AzureADMSPrivilegedRoleAssignment -ProviderId aadRoles -ResourceId $myTenant.TenantId -Filter "AssignmentState eq 'Active'"

Using Graph API I can explore and list the role assignments using the same filter examples like with PowerShell above:

Now that we have control over role assignments, we can look into the role definitions and settings in the next part.

Explore and Manage Role Definitions and Settings

image.png

The following commands can be used to get and manage role definitions and settings for Azure AD roles:

  • Get-AzureADMSPrivilegedRoleDefinition -ProviderId aadRoles -ResourceId <TenantId>
  • Get-AzureADMSPrivilegedRoleSetting -ProviderId aadRoles -Filter “ResourceId eq ‘<TenantId>'”
  • Set-AzureADMSPrivilegedRoleSetting -ProviderId ‘aadRoles’ -Id ‘<myRoleSettingsId>’ -ResourceId ‘<TenantId’ -RoleDefinitionId ‘<myRoleDefinitionId>’ -UserMemberSettings <Microsoft.Open.MSGraph.Model.AzureADMSPrivilegedRuleSetting>
g-raph

The equivalent Microsoft Graph API methods:

  • GET /privilegedAccess/azureResources/resources/{resourceId}/roleDefinitions
  • GET /privilegedAccess/azureResources/roleDefinitions?$filter=resourceId+eq+'{resourceId}’
  • GET /privilegedAccess/azureResources/resources/{resourceId}/roleDefinitions/{id}
  • GET /privilegedAccess/azureResources/roleDefinitions/{id}?$filter=resourceId+eq+'{resourceId}’
  • GET /privilegedAccess/azureResources/resources/<resourceId>/roleSettings
  • GET /privilegedAccess/azureResources/roleSettings?$filter=resourceId+eq+'<resourceId>’
  • GET /privilegedAccess/azureResources/roleSettings/{id}
  • PATCH /privilegedAccess/azureResources/roleSettings/{id}

Explore and Manage Role Assignment Requests

image.png

The following command can be used for exploring and managing role assignment requests:

  • Get-AzureADMSPrivilegedRoleAssignmentRequest. Get role assignment request for a specific resource.
  • Open-AzureADMSPrivilegedRoleAssignmentRequest. Create a role assignment
    request.
  • Set-AzureADMSPrivilegedRoleAssignmentRequest. Update a role assignment
    request.
  • Close-AzureADMSPrivilegedRoleAssignmentRequest. Cancel a
    AzureADMSPrivilegedRoleAssignmentRequest.
g-raph

The equivalent Microsoft Graph API methods:

  • GET /privilegedAccess/azureResources/resources/{resourceId}/roleAssignmentRequests
  • GET /privilegedAccess/azureResources/roleAssignmentRequests?$filter=resourceId+eq+'{resourceId}’
  • GET /privilegedAccess/azureResources/roleAssignmentRequests?$filter=subjectId+eq+'{myId}’
  • GET /privilegedAccess/azureResources/roleAssignmentRequests/{id}
  • POST /privilegedAccess/azureResources/roleAssignmentRequests
  • POST /privilegedAccess/azureResources/roleAssignmentRequests/{id}/updateRequest
  • POST /privilegedAccess/azureResources/roleAssignmentRequests/{id}/cancel

Speaking at Experts Live Europe 2019

I’m very happy to announce that I will back speaking at Experts Live Europe 2019! I got 2 sessions to present this year, and I really look forward to go to Prague in November and be a part of this great community conference for the 7th time in a row! Read on for some more details, history and perspectives on why you should attend this conference also.

Experts Live Europe, SCU Europe & Me

This years event is the third edition under the name “Experts Live Europe”. Before that it was known as System Center Universe Europe, or just SCU Europe. The first edition of SCU Europe was in Bern, Switzerland, 2013. I went there because I wanted to attend a European based conference with focus on Datacenter Management after it was announced that Microsoft Management Summit (MMS) would be no more. I was immediately blown away by the community experience, great speakers and content. I made some contacts and friends I’m still happy to see and meet again today.

Of course I had to make it to the 2nd and 3rd editions of SCU Europe that were held in Basel, Switzerland, in 2014 and 2015. Speakers, sessions and community was as good or better as the first conference in Bern, and content was starting to focus more on Microsoft Cloud. Myself I was intrigued and motivated to be a bigger part of this community, and started submitting sessions proposals to be a speaker myself.

In 2016, SCU Europe moved out of Switzerland and to Berlin, Germany. And this year I was one of the speakers! I was so proud when the opening keynote video introduced this speaker from Norway, you can by the way see all those opening videos at https://www.expertslive.eu/history. The conference being in Berlin meant a much easier travel from Norway, and this time around I was able to bring some colleagues and customers along. We had a great time:

At the end of the 2016 conference it was announced that the name SCU Europe will be no more, from 2017 the conference would be named Experts Live Europe, being part of the Experts Live network of global community conferences (www.expertslive.org).

In 2017 we were back in Berlin and I was accepted as a speaker again :). Once again we traveled as a group of colleagues and customers, and had a great time enjoying the excellent content and community.

In 2018, after all 5 previous conferences had been in a city that started with the letter “B”, it was announced that Experts Live Europe 2018 was to be held in the beautiful city of Prague, Czech Republic. For the third time in a row I would be one of the Experts again. Prague is also easy to travel to from Norway so we once again went as a group.

And that brings us to 2019, where we go back to the excellent Prague Congress Center, 20-22nd of November! I’m once again very proud to be speaking at this great community conference, and my record of 100% attendance for every SCU/ExpertsLive Europe record is intact, 7th time in a row now :).

My Sessions

As previously mentioned, I will be delivering two sessions at this years Experts Live Europe. And for the first time I will hold a session together with the one and only Samuel Erskine. This session should be both informative and entertaining!

My session with Sam is titled “Same old System Center.. but how can we hook up the Cloud and make it hot again!”. As System Center 2019 was released earlier this year, and with updates from Ignite, we will look into how System Center 2019 can go from “same old, same old..” to integrating with the Azure Cloud Platform and make System Center hot again!

My second session is named “Manage Identity Lifecycle and Access Control with Azure AD Identity Governance”, in which we will focus on how to manage identity lifecycle and make sure users have the right access at every time using Identity Governance solutions in Azure AD.

You can see the complete agenda for all the sessions here: https://www.expertslive.eu/agenda.

Community Rocks!

The thing I love most with ExpertsLive Europe is the really strong community of experts, sponsors and attendees. Beside the sessions and pre-conference itself, a big part of the value of attending is getting to know new people, connect again with people you have met before, learn, discuss and ask questions, get answers and be followed up with the best of the MVP’s and Community leaders out there. I started out standing in the corner and slowly started interacting with the community members, and now feel I have made many friends and being part of this great community myself. I really want to say hi to you in Prague, either if you are attending one of my sessions or you see me in the expo area, at the VIP party or attendee party.

Hope to see you there, and if you’re still not signed up, you can still register at http://www.expertslive.eu!

Speaking at Evolve Conference!

I’m delighted to announce that I will be speaking at Evolve Conference in Birmingham, 21st of October! Evolve Conference is a free Microsoft 365 and Azure Conference, and if you live near or are able to travel to Birmingham on monday there is still time to register: https://www.evolveconf.co.uk/.

Evolve, previously known as UC Day UK, is a one day conference covering a range of interesting topics on Microsoft 365 and Azure, delivered by top notch speakers from UK and abroad.

I have been lucky enough to present at this event on 2 occasions before, and I have been blown away at both the amount of sponsors and attendants making this a great community event.

I will be presenting a session on “Mastering Azure AD B2B Guests”, which will provide you with all the info you need for taking control of B2B collaboration in your organization.

Hope to see you there, and please come and say hi if you do!

Explore Microsoft Graph as a B2B Guest Account

The Microsoft Graph Explorer (https://aka.ms/ge) is always a great learning source and useful tool for querying the Microsoft Graph, especially as you can use your own Azure AD work account or Microsoft account to query for real data. Another advantage with the Graph Explorer is that you can use it without requiring an App Registration in your tenant, something most users are not able to do themselves as they don’t have the administrator rights for registering apps.

However, sometimes working with Microsoft Graph, I find myself in a scenario where I want to use my own work account, but where the Microsoft Graph resources I want to query is in another Organization’s tenant. These kind of scenarios is usually where my account is invited as a B2B guest user to the resource tenant, and currently there are no way to use the Graph Explorer tool to do that.

So I need another tool, and as an IT pro I could easily write myself some PowerShell code, or as a developer I could create a Web app or Console app querying Graph, and run the queries against the other tenant from there.

On the other hand, I want to do this more inline with the Graph Explorer experience, so the most logical choice for me is to use a tool where I can just run the REST API queries I like. And the most popular tool, both by me and many others are the “Postman” client. You can get it yourself for free at https://www.getpostman.com, both Windows, Linux and Mac downloads are supported!

So in this blog post I will show how I use Postman to query Microsoft Graph in a B2B Guest User scenario.

Requirements & Preparation

So, first you will need to get invited to another tenant where the resources you wan’t to query is. You might already have this Azure AD B2B invitation accepted previously. If you aren’t an administrator in that tenant, you will have to ask someone that has the rights to invite to do that for you.

Next, you will need to get assigned permissions to the resources you want to query in the resource tenant. This is all dependent on what kind of queries you want to run, whether it is for reading, writing or deleting resources for example.

Then, you will need an Global Administrator in that resource tenant to create an App Registration. The following instructions and screenshots can be used as a guide:

First, under the Azure AD experience in the Azure Portal, go to App Registrations and create a new Registration, type a name and a redirect URI like shown below:

image

You can type any name you like, for example in this scenario I want to use it for querying identities. I choose to support accounts in this organizational directory only, as this app registration is for members or guests from this tenant. And since I will be using the Postman i specify the redirect URI of “https://www.getpostman.com/oauth2/callback”. This is important as when I authenticate from Postman later the response will be returned to the Postman client.

When accessing Microsoft Graph you have to authenticate using one of the Oauth2 flows, and the most common is using authorization code flow, (https://developer.microsoft.com/en-us/graph/blogs/30daysmsgraph-day-12-authentication-and-authorization-scenarios/), which is exactly this scenario is all about as I will authenticate on behalf of my guest user account in the resource tenant. That means that I will have to add some delegated Graph permissions to the app registration, and in this example I add User.ReadBasic.All, this will make me able to query users via Graph:

image

After granting Admin consent for the permissions, I can verify that the permissions are added correctly:

image

Next I will need to create a client secret to be used in the request to get an access token, go to certificates & secrets and add a client secret of chosen time expiry:

image

Copy and make sure you save the client secret for later next:

image

Then copy the application id and tenant id and save for later:

image

Click on endpoints (see arrow above), and note the Oauth2 authorization and token endpoints (v2), this endpoints contains the tenantid:

image

With these steps the requirements are complete and we can move on to the postman client.

Authenticating and Querying Graph with Postman

There are a lot of features in the Postman client that you should look into when working with REST API’s. You can organize your queries in Collections, save variables in Environments, synchronize your requests across devices and so on. But for now we will start simple and easy.

In the main canvas at your workspace, create a new query, and for example start with https://graph.microsoft.com/v1.0/me:

image

Don’t push Send just yet, we need to authenticate first. Go to the Authorization section under the request, and select Oauth2, then click Get New Access Token:

image

In the following dialog fill in your details as shown below, where:

  • Token Name: You can type what you want here, this is named reference to the Access Token you will aquire.
  • Grant Type: As earlier mentioned, running graph queries as the logged in user (delegated) use the Authorization Code Oauth2 flow.
  • Callback URL: This is the URL that you specified earlier for Redirect URI under the Azure AD App Registration.
  • Auth URL and Access Token URL: These are the URL’s you saw earlier from the Endpoints setting for the Azure AD App Registration (contains the Tenant ID).
  • Client ID: This is the Application ID for the App Registration.
  • Client Secret: This is the secret key you generated under the App Registration.
  • Scope: Provide a default scope, use the default.
  • State: Scope is used for creating application logic that prevents cross use of Access Tokens. You can type anything you want here.
  • Client Authentication: Select to Send client credentials in body when working with Graph requests.
image

When you click request token, you will be taken to the resource tenant for authenticating with your delegated user. Type in your username and click next. This username can either be a normal user that belongs to this resource tenant, or in this case you can log in with your B2B Guest Account:

image

Now dependent on any Conditional Access policies and settings you might be required to approve sign in:

image

After successfully authenticating I should receive a valid Access Token:

image

If you get an error here, please verify your App Registration settings and that the account you logged in as is correct.

Scroll down and click Use Token:

image

You will see that the Access Token is now filled in the Access Token textbox. Next click Preview Request down to the left, this will add the Access Token to the request as a authorization header:

image

If you click on Headers, you can see the Authorization header has been added with the access token as a Bearer Token value:

image

This Access Token is now valid for 1 hour, and you can run as many requests you like as long as you are inside the delegated permissions (for the App Registration) and the logged in users actual permissions. After 1 hour you can request a new Access Token.

PS! Postman will save the authenticated user session in cookies, if you want to log in as a different user clear those cookies, se “cookies” link right below the Send button.

So, now lets run this query, click Send at verify the response, you should get details for your guest user. From the screenshot below you can clearly see that this user is a Guest account looking at the userPrincipalName attribute:

image

Lets try another query, in this query I list all users that has userPrincipalName that starts with “Jan”, and showing only the displayName and userPrincipalName attributes:

image

As you can see from the result above, I have several guest accounts in this tenant (Microsoft Account, Google, Azure AD) as well as a normal user account. You can also see that the Postman client is helpful in specifying my parameters.

Inspecting a B2B Guest Access Token

If you copy the Access Token you got earlier, and paste it into a site like jwt.ms or jwt.io, we can take a look at the access token contents and claims:

image

If I scroll down a little I see the displayname of the App Registration, but the most important info is the mail claim, which for Guest users will be the external e-mail address. Idp is the source authority for the Guest account, in this case another Azure AD tenant with the Tenant Id as shown below:

image

Working with Environments

Chances are that you might work with several environments in Postman, and that where it’s useful to create environment variables. For example create an environment like shown below:

image

That way you can select which environment you want to work with when running queries, and when referencing variables use “{{ .. }}”. For example under Get New Access Token, change to this:

image

Now, lets finalize this blog post by logging in with another guest account, I will choose my Gmail account, I’ve already set up Google Federation and invited this user to my tenant.

First I need to clear the cookies:

image

Next I will click to Get a New Access Token again, and then authenticate as my Google account, which directs me to the Google login page:

image

After successfully authenticating, and using the new Access Token in the Authorization Header, I can run the basic /me query again, this time showing me that I’m now authenticated to Graph with my Gmail user:

image

And looking inside the Access Token again, I can see that the e-mail address is my gmail and the idp is now google.com:

image

If I had logged on with a Microsoft Account the idp value would have been “live.com”.

Next steps

So, now you know how to authenticate to and query Microsoft Graph with an Azure AD B2B Guest User. I really hope this functionality will come to Graph Explorer eventually, but for now Postman is already an awesome free tool for organizing and running your Microsoft Graph queries that I use a lot myself.

The Microsoft Graph Team has also published this source for a lot of useful collections of Graph queries:

https://github.com/microsoftgraph/microsoftgraph-postman-collections

Get and Set Automatic Replies like OOF with Microsoft Graph

Hi, a short blog post this time as Summer Vacation 2019 is here shortly! And on that note, the topic of the post is to show how you can get and set automatic replies with Microsoft Graph. Automatic replies, previously known as Out of Office (OOF) messages is a mailbox setting for each Exchange Online enabled user.

The Microsoft Graph API documentation for mailbox settings is located here: https://docs.microsoft.com/en-us/graph/api/user-get-mailboxsettings?view=graph-rest-1.0&tabs=http, and besides automatic replies you can also get and set locale (language and country/region), time zone, and working hours.

But for now we will only focus on automatic replies, using the automaticRepliesSetting resource type: https://docs.microsoft.com/en-us/graph/api/resources/automaticrepliessetting?view=graph-rest-1.0.

This resource type has the following settings:

{
   "externalAudience": "String",
   "externalReplyMessage": "string",
   "internalReplyMessage": "string",
   "scheduledEndDateTime": {"@odata.type": "microsoft.graph.dateTimeTimeZone"},
   "scheduledStartDateTime": {"@odata.type": "microsoft.graph.dateTimeTimeZone"},
   "status": "String"
 }

Let’s look at some different samples, and I will use Graph Explorer (https://aka.ms/ge). Please note that every end user already have user permission to get or set their own mailbox settings, but you need an Exchange Admin role to get or set the settings for other users in your organization. In addition, if you create your own app registration for Microsoft Graph, you need to make sure the app has either MailboxSettings.Read or MailboxSettings.ReadWrite permission.

In Graph Explorer, after you sign in with your work account, you can modify these permissions if needed:

image

After signing out and in again you will be prompted to consent, if you havent already:

image

Get Current Mailbox Settings

To get your own current settings you can run the following:

GET /me/mailboxSettings

In Graph Explorer this would look like this, and you might have some previous values set here. In my example automatic replies have a status of disabled:

image

To get another users mailbox settings you can run the following (but then you must be an Exchange Admin):

GET /users/{id|userPrincipalName}/mailboxSettings

Simple Update of Status

Lets see how Microsoft Graph can be used to change the status value, there are 3 different settings:

  • disabled. No automatic replies are sent.
  • alwaysEnabled. Automatic replies are sent as specified.
  • scheduled. Automatic replies are sent if between a specific time period.

First, change the method i Graph Explorer to PATCH:

image

Then you need to supply a request body. This sample is just for enabling automatic replies:

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Me/mailboxSettings",
    "automaticRepliesSetting": {
        "status": "alwaysEnabled",
    }
}

So paste that body to Graph Explorer and then Run Query:

image

You should then get a successful response. Likewise you can set the status to “disabled” again, or to “scheduled”. But using scheduled means that you must set some datetime values as well.

Set Scheduled Automatic Replies

To set scheduled automatic replies, in your request body include the resource types scheduledStartDateTime and scheduledEndDateTime. You can read more about that resource type here, including available time zones: https://docs.microsoft.com/en-us/graph/api/resources/datetimetimezone?view=graph-rest-1.0. This is a sample specifying a scheduled automatic replies:

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Me/mailboxSettings",
    "automaticRepliesSetting": {
        "status": "scheduled",
        "scheduledStartDateTime": {
            "dateTime": "2019-07-15T08:00:00.0000000",
            "timeZone": "Europe/Berlin"
        },
        "scheduledEndDateTime": {
            "dateTime": "2019-08-09T16:00:00.0000000",
            "timeZone": "Europe/Berlin"
        }
    }
}

Customize internal and external reply messages

The last part is where we put it all together, specifying the following values:

  • internalReplyMessage: Plain text or HTML formatted message sent to all internal users in your organization as the automatic reply.
  • externalReplyMessage: Plain text or HTML formatted message sent to all external users as the automatic reply, but depending on this value:
  • externalAudience: If “none”, no external users will get automatic replies, if “contactsOnly” replies will only be sent to users in your contacts, and if “all” every external user will get a reply.

So this is a working sample of a complete request body:

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Me/mailboxSettings",
"automaticRepliesSetting": {
        "status": "scheduled",
        "externalAudience": "contactsOnly",
        "internalReplyMessage": "<html>\n<body>\n<div></div>\n<div>Hi, I'm enjoying summer vacation 2019. I'm back at work August 12th!</div>\n<div><br>\n</div>\n<div>Kindly Regards</div>\n<div>Jan Vidar Elven</div>\n<div></div>\n</body>\n</html>\n",
        "externalReplyMessage": "<html>\n<body>\n<div></div>\n<div>Hi, I'm enjoying summer vacation 2019. I'm back at work August 12th!</div>\n<div><br>\n</div>\n<div>I'll only read e-mails intermittently, and rarely respond before I'm back. Please contact management if anything urgent business needs follow up. Contact info on our website.</div>\n<br>\n</div>\n<div>Kindly Regards</div>\n<div>Jan Vidar Elven</div>\n</div>\n<div></div>\n</body>\n</html>\n",
        "scheduledStartDateTime": {
            "dateTime": "2019-07-15T08:00:00.0000000",
            "timeZone": "Europe/Berlin"
        },
        "scheduledEndDateTime": {
            "dateTime": "2019-08-09T16:00:00.0000000",
            "timeZone": "Europe/Berlin"
        }
    }
}

In Graph Explorer:

image

And I can verify in my Outlook settings:

image

Summary and Usage Scenarios

Beside that it is always fun to learn something new about the Microsoft Graph, and automation, the reality is that for many users they will just click to enable or disable automatic replies directly in their Outlook client, both Office Outlook, Outlook Mobile and Outlook on the Web supoorts this. Finding out how to do it with Graph took me just under 2 hours, including writing this blog post. But then again, I learned something new! And I picked up a couple of more tips and tricks on different JSON Request Body constructs 😉

Anyway, in a bigger picture, Graph API is great for customizing, integrating, reporting and automating, so if your organization maybe have create a vacation calendar, you could use the Graph API to automatically enable or disable out of office replies, this is just one example, many more will exist. Please share with me in the comments if you have done or plan to do something with this or similar.

Smile

But first: Summer Vacation 2019! And I’m all set with automatic replies !