Tag Archives: Power Platform

Connect Power Platform to Azure AD Protected APIs using built-in HTTP connectors

There are several ways you can access the Azure AD Protected APIs in Power Platform Flows and Apps. Without creating Custom Connectors, which basically can connect to any REST based API that is available, it is useful to know what built-in HTTP connectors are available and can be used for delegated authentication to Azure AD Protected APIs like Microsoft Graph or other APIs.

This and more will be answered and demoed in this blog post.

What is an Azure AD Protected API?

First, lets do a quick explanation of what an Azure AD Protected API is.

The most known Microsoft API is the Microsoft Graph API. Another well known API is the Azure REST API. These APIs are protected by and accessed by identities and applications from Azure Active Directory organizations.

Other than that, any API has the possibility to be protected by Azure AD and by using industry standard authentication and authorization protocols like OpenID Connect (OIDC) and OAuth2.0. This includes APIs you might build yourself or APIs from third party services.

Delegated vs. Application Access

When you want to access Azure AD Protected APIs from Power Platform you also need to know the access scenarios you will need. There are 2 main access scenarios: Delegated access and Application-only access, and as shown in the picture below, there is a distinct difference in that the delegated access scenario always includes the user.

This means that if you are running a Power Automate flow, or a PowerApp, you will usually run that interactively as your user account, and any connections you use will be running via the app under your user context and permissions to access the data resources the app might be using. The data resource in this example can be represented by an Azure AD Protected API.

If you share the PowerApp or Power Automate flow with other users in your organization, then those users will use the connections to the API resource under their own user context.

If you, on the other hand, wants to run a Power Automate flow on a schedule or in any other way without user interaction or without a user context, then you typically will use the app-only access scenario.

In this blog post I will focus on delegated access scenarios, and where you can use built-in HTTP connectors that will run in connection based on the running user.

Built-in HTTP Connectors in Power Platform

Let’s start with an overview over what built-in HTTP connectors and actions for Azure AD Protected APIs we have available in Power Platform. These are HTTP connector actions you can use in Power Automate, PowerApps and in most cases also Logic Apps, and the following list is current as of December 2022:

  • “Send an HTTP request (preview)”, Office 365 Outlook connector.
  • “Send an HTTP request (preview)”, Office 365 Users connector.
  • “Send an HTTP request”, Office 365 Groups connector.
  • “Send an HTTP request V2 (preview)”, Office 365 Groups connector.
  • “Send an HTTP request (preview)”, Office 365 Groups Mail connector.
  • “Send an HTTP request to SharePoint”, SharePoint connector.
  • “Send an HTTP request (preview)”, LMS365 connector.

These are all Standard connectors that you can freely use if you are using the Power Apps/Automate plan for Microsoft 365.

There is also an interesting HTTP action with Azure AD that is Premium and has the following action:

  • “Invoke an HTTP request”, HTTP with Azure AD connector.

Premium means you need to acquire a standalone Power Apps and Power Automate licensing plan.

There is also a “Send an HTTP request to Azure DevOps” in the Azure DevOps connector that is also Premium.

While these connectors are for querying resources that are under the scope of the data that are accessible via the connector, the most important part is that these are all dependent on the user connection and is using the delegated access scenario.

This makes them especially useful for calling Microsoft Graph API in the context of your own user.

If you want to send HTTP requests in an app-only access scenario, then you can use the built in HTTP connector, which is Premium. But that is not the scope of this blog post, so let’s continue looking into some scenarios and examples for the above delegated access connectors.

Send HTTP Request via Office 365 connectors

Lets do a couple of scenarios of the Office 365 Outlook/Users/Groups connectors from above, and their HTTP request actions. I have created a instant cloud flow with a manual trigger for now.

First I will initialize a variable for the base Url, this will be the Microsoft Graph API:

Then I add each of the HTTP actions from the Office 365 connectors, using the baseUrl and the “me” resource to start with. In the below image I’ve renamed the HTTP actions so that you can see from which connector they are from, and I’m running a simple GET method:

I’ve also configured the run after setting for each of the actions, so that I can verify the results of the others if anyone fails:

When I save and test the flow I can verify that I’ve signed in to the connectors as my own user, and any permissions that these have:

When I try to run this Flow, it will fail on several of the actions, this is expected:

The reason it fails is because there are restrictions on the resources and objects the actions are allowed to query, for example for the first one we get the info on that this connector can use either “me” resource or “users” resource, but also only for the listed objects like messages, calendar etc:

One of the actions is successful however, and that is from the Office 365 Groups connector and the first version of the Send an HTTP request, which is allowed to get the /me resource:

Lets make some adjustments to the queries for the different actions:

In the above image I’ve added some supported objects for the different actions, and all these should return a valid Graph response:

So this means as long as you either:

  • use the /me/{object} or
  • /users/{userid-or-userprincipalname}/{object} or
  • /groups or /groups/{groupid}/{object}

From the supported list of objects (messages, events, calendar, etc) then you can run any Microsoft Graph API queries including GET, POST, PUT, PATCH and DELETE.

From earlier we saw that one of the connector actions had more broad support than the others, and that was the Office 365 Groups connector and the original version of the “Send an HTTP request”. There has since been released a V2 version of the same action, and that limits only queries against /groups resource.

Lets do a quick test on if the first action supports getting the user’s registered authentication methods. If we run that same query using Graph Explorer, we will get a list of your users methods for authentication, including Authenticator, Phone, Windows Hello etc. In the action I will type /me/authentication/methods, but when I run it will fail with request authorization failed:

This means that even though I have permission as my self to query my authentication methods (see Graph Explorer for example), the connector does not have the correct delegated permissions to act on my behalf. From Graph Explorer I can verify that I need to consent to permissions for authentication methods:

So to summarize the Office 365 connectors and HTTP actions, they can be valuable for many Graph API requests for your users, but only for permitted objects and permissions.

This is where the “Invoke an HTTP request”, HTTP with Azure AD connector, can be useful, and we will look into that next.

Send Request via HTTP with Azure AD Connector

First of all, this is a Premium connector, so you must make sure you are licensed with a separate Power Apps / Power Automate plan for this, but a trial should also be available to for exploring the connector.

According to the documentation, the HTTP with Azure AD connector can be used to fetch resources from various web services that are authenticated by Azure AD. It can also be used to query from an on-premise web service.

Note that there are known issues and limitations, such as if you get “Forbidden” or “Authorization Request Denied“, or “Insufficient privileges to complete the operation.” then it could be because this connector has a limited set of scopes.

The big advantage of this connector is that you can use it for several of Microsoft APIs, not just the Graph API.

Lets try it out, I will create another instant cloud flow with manual trigger, and then add the HTTP with Azure AD connector and Invoke an HTTP request action:

If this is the first time you have added that connector action and you don’t have any existing connections, then you need to configure that and sign in first. If we want to use the Microsoft Graph API, then you need to fill in this and then sign in:

Then, we can start with a simple Graph request for getting my profile info:

When we run that, it should successfully return the user profile:

Ok, let’s try another request again, and see if this connector lets us query for authentication methods:

So here we can see that the limitations in scope also applies to this connector, as we get an access denied and request authorization failed.

But there are more scenarios that this connector action support than the Office 365 connectors I described in the previous section. For example I can get my groups memberships, note the use of $count parameter and that it requires the consistencyLevel=eventual in the Request Header:

There aren’t really any good documentation on the HTTP for Azure AD connector to say which resources and objects you can send requests to, but you can assume that you can do a lot of the normal member of a directory can do, but not necessarily those that require Graph permission consent outside read user information.

Note that you can use the HTTP for Azure AD connector to other APIs than Microsoft Graph, and this is just a list of examples:

  • management.azure.com
  • vault.azure.net
  • <tenant>.sharepoint.com
  • api.loganalytics.io

But I thought I should wrap up the blog post by looking at how the HTTP for Azure AD connector can be used for invoking requests for APIs that you have built yourself, like for Azure Functions or Logic Apps.

Invoke Requests for your Serverless APIs with HTTP for Azure AD

Protected Logic Apps

I have previously written a blog post series on how you can protect Logic Apps with Azure AD: https://gotoguy.blog/2020/12/31/protect-logic-apps-with-azure-ad-oauth-part-1-management-access/

Lets use that knowledge to see if the HTTP for Azure AD connector can invoke requests to a Logic App that is protected with an Azure AD Authorization Policy.

I have created a new simple Logic App with a HTTP request trigger:

I’ve added a response action, and made sure that the response is returning JSON content like this:

Then, I have added an Azure AD Authorization Policy like the following, requiring that the issuer claim in the authorization header is from my tenant:

I can now go to my Power Automate flow, and add an HTTP for Azure AD connector action, but I need to set up a new connection like the following. Note that the base resource URL will be the Logic App instance, and the Azure AD Resouce URI will be one of the well known Azure APIs like below. (I haven’t been able to use graph.microsoft.com here, because I suspect the fact that Graph API tokens cannot be validated outside Microsoft Graph itself).

When I have signed in at set that connection active I can configure the Invoke an HTTP request action like the following, note that I have left out the parameters that belong to the SAS (shared access scheme) like sig, sv, sp, as you only can use on of SAS or OAuth2:

When I run this I get a successful response:

This means that the HTTP for Azure AD connector was now able to invoke a request for a protected API that I have built myself. Of course this was a simple request, but if you look at the aforementioned blog post series of protecting Logic Apps, you can see how you can include the Authorization Header in the Logic App trigger, and then get the claims and do authorization logic inside the Logic App.

In this example I didn’t include my own API definition, so lets now get even more advanced and look at an Azure Functions API example!

Protected Azure Functions with Custom API

Last year for Festive Tech Calendar in 2021, I created this solution for my contribution: https://gotoguy.blog/2021/12/22/creating-an-azure-ad-protected-api-in-azure-in-a-school-hour/

I will now use this as an example on how I can invoke requests for this API using Power Automate and the HTTP for Azure AD connector!

In another instant cloud flow, I’ve added the Invoke request with HTTP for Azure AD, and I need to create another connection, which will be configured as follow:

The base URL is the Function App URL, and for Resource URI I’ve used the Application ID URI I created when I defined the custom API (see the blog post for details).

However, when I sign in I will first get an error like the following:

Note the client app id I’ve marked with a yellow shape above, this is a well known global confidential application ID for Power Platform, so this will be the same ID for you as it is for me. This is also described in this article: https://learn.microsoft.com/en-us/power-query/connectorauthentication.

This means that I need to go to my custom API app registration, and add this ID as an authorized client application and which scopes it is authorized to use:

After adding that I can now sign in to create the connection, and next configure the invoke HTTP request as follows, using one of the API methods I’ve defined (again see my blog post from last year for details :):

When I run the Flow I see that the request is successful, and I indeed get a response from the Protected Functions API:

Summary

In this blog post I have shown how you can use and connect Power Platform to Azure AD Protected APIs using built-in HTTP connectors that use the delegated access scenario.

While the Office 365 connectors are great for limited scenarios around Microsoft Graph, we could verify that the HTTP for Azure AD connector (Premium) had more usage scenarios, including the ability to invoke requests from both Microsoft APIs, as well as your own APIs.

Hope this has been useful, thanks for reading!

Speaking at Global Automation Bootcamp 2021

I’m happy to announce that I’m part of the amazing global initiative of automation bootcamps in starting from February 5th to 20th 2021!

Update: The Azure Automation track has now been pushed back one week from February 20th to February 27th.

I will speak about how Azure Serverless Automation solutions like Azure Functions, Logic Apps and more can be protected by Azure AD and how Power Platform can securely send requests to trigger your automation scenarios. Session details:

You can register for FREE here at this link: Global Automation Bootcamp 2021 – Power Community

The agenda is very exciting with top speakers, and sessions will be delivered according to the following tracks and days:

  • Automation Summit Day 1, Fri 5th February
  • Power Automate Saturday Bootcamp, Sat 6th February
  • Power Automate Bootcamp Day 2, Sun 7th February
  • RPA & UI Test Automation Bootcamp, Sat 13th February
  • Azure Automation Bootcamp, Sat 27th February
  • Powershell Saturday Bootcamp, Sat 20th February

You can sign up anytime, hope to see you at my session and catch any of the other great sessions 🙂

Protect Logic Apps with Azure AD OAuth – Part 3 Connect to API from Power Platform

In this article I’m going to build on my previous blog posts in this series where I have written about how to add Azure AD OAuth authentication and authorization to your Logic Apps and expose them as an API. For reference the links to these blog post articles are here:

If you want to connect to API’s using Power Platform (Power Automate Flows, PowerApps etc.), you can do this in two different ways:

  • Using HTTP action and send requests that use Azure AD OAuth authentication. This will use the “Client Credentials” OAuth flow, and is suitable for calling the API using application permissions and roles.
  • Setting up a Custom Connector for the API, and using the HTTP logic app trigger as operation. This will use the “Authorization Code” OAuth flow, and is suitable for using delegated permissions and scopes for the logged on user via connections.

So it depends on how you want your Power Platform users to be able to send requests your Logic App API. Should they do this as themselves with their logged on user, or should they use an application identity? There are use cases for both, so I will show both in this article.

Connect to Logic App API using Custom Connector

Using Custom Connectors is a great way to use your own identity for sending requests to an API. This way you can also securely share Custom Connectors, and Flows/PowerApps, using them in your organization, without needing to share sensitive credentials like client id and client secrets.

If you want to create a Custom Connector in Power Platform that triggers an HTTP request to a Logic App, you can currently do this in one of the following ways:

  1. Creating Custom Connector using Azure services and Logic App.
  2. Exporting the Logic App to a Power Platform environment.
  3. Creating the Custom Connector using an OpenAPI swagger definition file/url.
  4. Creating the Custom Connector from blank.

Lets take a quick look at each of these, but first we need to take care of some permissions in Azure for creating the Custom Connector automatically.

Azure Permissions for Logic Apps and listing swagger

There are some minimum permissions your user needs to be able to create a Custom Connector automatically by browsing the Azure Service.

A good starting point is using one of the built in Azure roles for Logic Apps:

But even these do not have the permissions necessary, if you try you will get an error similar to the following:

So we need to att this ../listSwagger/action for the scope, and you could give the user full Contributor access, but that seems rather excessive. Lets create a custom role instead. I will do this using Azure PowerShell, for reference please see the docs: Create or update Azure custom roles using Azure PowerShell – Azure RBAC | Microsoft Docs.

After connecting to Azure using an Azure account that can create custom roles for the scope (Owner or User Access Administrator), start by exporting an existing role as a starting point:

# 1. Export a JSON template as a reference based on an exisiting role
Get-AzRoleDefinition -Name "Logic App Operator" | ConvertTo-Json | Out-File .\LogicAppAPIOperator.json

Then edit this JSON file, by removing the Id parameter, defining a Name, setting the IsCustom to true, and Description to something descriptive like below. I have also set my Azure subscription id under assignable scopes, and added the required ../listSwagger/action:

{
  "Name": "Logic App API Operator",
  "IsCustom": true,
  "Description": "Lets you read, enable and disable logic app, and list swagger actions for API.",
  "Actions": [
    "Microsoft.Authorization/*/read",
    "Microsoft.Insights/alertRules/*/read",
    "Microsoft.Insights/metricAlerts/*/read",
    "Microsoft.Insights/diagnosticSettings/*/read",
    "Microsoft.Insights/metricDefinitions/*/read",
    "Microsoft.Logic/*/read",
    "Microsoft.Logic/workflows/disable/action",
    "Microsoft.Logic/workflows/enable/action",
    "Microsoft.Logic/workflows/validate/action",
    "Microsoft.Resources/deployments/operations/read",
    "Microsoft.Resources/subscriptions/operationresults/read",
    "Microsoft.Resources/subscriptions/resourceGroups/read",
    "Microsoft.Support/*",
    "Microsoft.Web/connectionGateways/*/read",
    "Microsoft.Web/connections/*/read",
    "Microsoft.Web/customApis/*/read",
    "Microsoft.Web/serverFarms/read",
    "Microsoft.Logic/workflows/listSwagger/action"
  ],
  "NotActions": [],
  "DataActions": [],
  "NotDataActions": [],
  "AssignableScopes": [
    "/subscriptions/<my azure sub id>"
  ]
}

After that you can create the custom role:

# 3. Create the new custom role:
New-AzRoleDefinition -InputFile .\LogicAppAPIOperator.json

This role can now be assigned to the Power Platform user(s) that need it, using the scope of your Logic Apps, for example the Resource Group. You can either add the role assignment to the user directly, or preferrably using Azure PIM:

With the correct permissions now in place, you can proceed to the next step.

Creating Custom Connector using Azure services and Logic App

Log in to Power Apps or Power Automate using your Power Platform user. Under Data and Custom Connectors, select to create a New custom connector. From there select “Create from Azure Service” (Preview as per now):

Next, type name for your Custom Connector, select which Azure subscription, and from which Azure service which in this case is Logic Apps. Note that you can create from Azure Functions and Azure API Management as well. Then select the Logic App name from the list:

If you don’t see or get any errors here, verify your permissions.

Click Continue, and you will see something like the following, where the Host and Base URL has automatically been set correctly for your Logic App HTTP trigger. If you want you can upload an icon, background color and desctription as well:

Click Next to go to Security. Here we need to change the Authentication to OAuth 2.0, as this is what we have implemented for authorizing requests to the Logic App. To authenticate and get the correct Access Token, we will reuse the LogicApp Client app registration that we created in the previous blog post. Copy the Application Client ID and Tenant ID:

And then create and copy a new secret for using in the Power Platform Custom Connector:

Fill in the rest of the OAuth 2.0 details for your environment like below:

Note from above that we need to specify the correct resource (the backend API) and scope. This is all very vell described in the previous blog post.

Click Create Connector to save the Connector details. Make sure that you copy the Redirect URL:

And add that to the App Registration redirect URLs:

Next, under the Custom Connector proceed to step 3. Definition. This is where the POST request trigger will be, and it should already be populated with an action:

We need to specify a value for Summary, in my case I will type “Get Managed Devices”:

Next, under Request Query, remove the “sp”, “sv” and “sig” parameters, as these will not be needed as long as we are using OAuth2 authorization scheme:

The Body parameter should be correctly specified, expecting an operatingSystem, osVersion and userUpn request body parameters:

Last, lets check the Responses from the Logic App. They have been successfully configured with status 200 (OK) and 403 (Not Authorized), as these two responses were defined in the Logic App.

If the Response body is empty like below, we would need to import from sample output from the Logic App (response body should have automatically been configured if the response action in Logic App had a response schema defined):

For response status 200, the sample import is:

[
  {
    "deviceName": "",
    "manufacturerer": "",
    "model": "",
    "operatingSystem": "",
    "osVersion": "",
    "userDisplayName": "",
    "userPrincipalName": ""
  }
]

Giving this response definition:

The sample response from the 403 not authorized should be:

{
  "Message": "",
  "Roles Required": "",
  "Roles in Token": "",
  "Scopes Required": "",
  "Scopes in Token": ""
}

Giving this response definition:

NB! It’s important to have correct responses defined like above, it will make it easier to consume those responses later in Power Automate Flows and Power Apps.

Click on Update Connector, and then go to next section 4. Test.

We can now test the Logic App trigger via the Custom Connector, first we need to create a new connection:

After logging in, and if needed consenting to the permissions scopes (se previous blog post for details), we should have a connection. We can now test the trigger by supplying the api-version (2016-10-01) and specify the operatingSystem, osVersion and userUpn parameters:

Click Test operation and verify a successful response like below:

Let’s try another test, this time leaving the userUpn blank (from the previous blog post this means that the Logic App tries to return all managed devices, if the user has the correct scope and/or roles). This time I get a 403 not authorized, which is expected as I don’t have the correct scope/role:

Checking the Logic App run history I can see that my Power Platform user triggered the Logic App and I can see the expected scp and roles claim:

Perfect so far! In the end of this blog post article I will show how we can get this response data to a Power App via a Flow and the Custom Connector, but first lets look into the other ways of creating a Custom Connector.

Exporting the Logic App to a Power Platform environment

In the previous example, I created the Custom Connector from my Power Platform environment, in this example I will do an Export from the Logic App. The user I will do this with needs to be a Power Platform licensed user and have access to the environments, or else I will get this:

To Export, click this button:

Then fill inn the name of the Custom Connector to create, I will call this ..v2, and select environment:

You might get another permission error:

If so, we need to update the Custom Role created earlier with this permission. Do the following:

# 3b. Update the custom role
$roleLogicAppAPIOperator = Get-AzRoleDefinition -Name "Logic App API Operator"
$roleLogicAppAPIOperator.Actions.Add("Microsoft.Logic/workflows/triggers/listCallbackUrl/action")
Set-AzRoleDefinition -Role $roleLogicAppAPIOperator

The role and the assignment should now be updated, so we can try again. You might need to refresh or log out and in again for the permission to be updated. After this the Export should be successful:

We can now find the Custom Connector right below the first we created:

We still need to edit the Custom Connector with the authentication details, adding the app/client id, secret etc. The export has also left out all the query parameters (sv, sp, sig) but also the required api-version. This must be fixed, the easiest way is to switch to Swagger Editor, and add the line 15 and 16 as shown below:

After this you should be able to Update the Connector, and the Test, Create a Connection and verify successful results.

Creating the Custom Connector using an OpenAPI swagger definition file/url

Both examples above, either importing a Custom Connector from Azure Service, or exporting the Logic App to a Custom Connector in a Power Platform environment, require that the user doing this both has:

  • Azure RBAC role assignment and permissions as detailed above.
  • Access to Power Platform environment and licensed for using Power Platform.

What if you as Azure administrator don’t want your Power Platform users to have access to Azure, but you still want to help them with creating Custom Connectors that send requests to selected Logic App workflow APIs?

Then you can provide them with an OpenAPI swagger definition file or url. You can get the swagger OpenAPI definition by running this Azure REST request: Workflows – List Swagger (Azure Logic Apps) | Microsoft Docs.

To get your swagger you can look at the first blog post in this series, where I showed how you could use Az PowerShell to get management access tokens using Get-AzAccessToken and running Invoke-RestMethod. In this example I’m just going to use the Try it button from the Docs link above, and then authenticate to my Azure subscription, and fill inn the required parameters:

Running this request should produce your requested swagger OpenAPI definition. You can now copy this to a file:

Before you share this OpenAPI file with your Power Platform developers, you should edit and remove the following request query parameters, as these are not needed when running the OAuth2 authorization scheme:

After this you can create a new Custom Connector, by specifying an OpenAPI file / url, depending on where you made the file available to your Power Platform Developers.

Browse to the filename and type a Connector name:

After this you will have the basis of the Connector defined, where you can customize general settings etc:

You will now need to add the authentication for Azure AD OAuth2 with client id, secret etc under Security, as well as creating the connector, and under test create a connection and test the operations. This is the same as I showed earlier, so I don’t need to show tthe details here.

Creating the Custom Connector from blank

You can of course create Power Platform Custom Connectors from blank as well, this should be easy enough based on the details I have provided above, but basically you will need to make sure to set the correct Host and Base URL path for your Logic App here:

After adding the authentication details for Azure AD OAuth2 (same as before), you will need to manually add actions, and providing a request from sample, as well as defining the default responses for 200 and 403 status, as shown in the earlier steps.

With the Custom Connector now in place for sending Requests to the Logic App using delegated authentication, we can now start using this Connector in our Flows/Power Apps.

Lets build a quick sample of that.

Creating a Power Automate Flow that will trigger Logic App API

I’ll just assume readers of this blog post knows a thing or two about Power Automate and Cloud Flows, so I’ll try to keep this high level.

I’ve created a new instant Cloud Flow, using PowerApps as a trigger. Then I add three initialize variables actions, giving the variables and actions name like below, before I set “Ask in PowerApps” for values:

Next, add a Custom Connector action, selecting the Custom Connector we created earlier (I have 3 versions here, as I shown above with the alternative ways to set up a Custom Connector from Logic Apps:

Next, select the action from the chosen Custom Connector, and fill in the parameters like below:

Next, we need to check the response status code we get back from the Custom Connector. Add a Switch control action, where we will check against the outputs from the Get Managed Devices and statusCode:

Make sure that the Switch action should run either if the Get Managed Devices is successful or failed:

For each case of statusCode we will check against, we need a Response action to return data back to the PowerApp. For status code 200 OK, I’ll return the Status Code as shown below, adding Headers to be Content-Type application/json, and using the Body output from the Get Managed Devices custom connector action. The Response Body JSON schema is based on the sample output from the Logic App API.

PS! To get the Body output, you can use the following custom expression: outputs('Get_Managed_Devices')?['body']:

For status code 403 I have added the following Case and Response action, using the Body output again, but this time the schema is based on the 403 response from the Logic App API:

Last, as every case should have Response, I’ll add the following Default Case:

The whole Flow visualized:

PS! Instead of using the Response action, I could also have used the “Respond to PowerApps” action. However this action only let me return text strings, numbers, boolean etc, and I wanted to return native JSON response.

Make sure that you test and verify the Flow before you proceed.

Creating the Power App to connect to the Flow

With the Flow ready, lets quickly build a PowerApp. My PowerApp is a Canvas App, and I have been using the Phone layout.

You can build this any way you want, but I used a dark theme, added an Icon at the top screen, and the 3 labels and text inputs for the parameters needed for the Flow. I then added a button for triggering the Flow, and under the button I have added a (hidden now) text label, for showing any error messages from the Flow. And I have added a Gallery control under there again for showing the resulting devices:

For the Button, select it and click on the Action menu and the Power Automate to connect your Flow. Next, change the “OnSelect” event for the Button to the following command:

Set(wait,true);
Clear(MyManagedDevices);Set(MyErrorMessage,Blank());
Set(MyDeviceResponse,GetManagedDevices.Run(textOperatingSystem.Text,textOSVersion.Text,textUserUpn.Text));
If(IsBlank(MyDeviceResponse),Set(MyErrorMessage,"Authorization Error: Check Flow for details on missing scope or roles claims for querying organization devices."),ClearCollect(MyManagedDevices,MyDeviceResponse));
Set(wait,!true)

A quick explanation of the commands above:

  • Set(wait,true) and Set(wait,!true) is to make the PowerApp “busy” when clicking.
  • I then Clear my Collection and Variable used.
  • I then use Set to get a “MyDeviceResponse”, this will return a collection of items (devices) returned via a JSON array from the Flow, or if I’m not authorized, it will return a failed response (based on the 403) and a blank MyDeviceResponse.
  • Next I do a If test, if the MyDeviceResponse i Blank, I’ll set the MyErrorMessage variable, if it’s not blank I will run a ClearCollect and fill the Collection with returned devices.

I fully appreciate that there might be other ways to do this fail checking and error handling, please let me know in the comments if you have other suggestions 🙂

For the Gallery I set the Data source to MyManagedDevices collection, and I have selected to use the layout of “Title, subtitle, and body”. You can change the device data that get filled in for these items in the Galleri, for example Manufacturer, Version, Name etc.

And last I set the Text property of my error message label to the MyErrorMessage variable:

Let’s Save, Publish and test this PowerApp!

First, I’ll try to add parameters for getting all Windows 10 devices, leaving user principal name blank. This will via the Custom Connector send a request to the Logic App API to return all devices, and in this case I’m not authorized to do so, so I’ll get an authorization error:

This is something I can verify in the Flow run history also:

Next, I’ll try to return my test users devices only, and this is successful and will fill the gallery:

We now have a working Flow and PowerApp connected to the Logic App API using the signed in users delegated permissions. If I want I can now share the PowerApp and Flow including the Custom Connector with other users in my organization, and they can use their own user identity for connections to the Logic App API.

In the next part of this blog post I will show how you can access the Logic App API using HTTP action and application permissions.

Connect to Logic App API using HTTP action

Sometimes you will have scenarios where you want to use an application identity to call an API like the Logic App I have used in this blog post article series. This is especially useful if you want to run a Power Automate Flow without a logged in user’s permissions.

I the previous blog post part 2 for exposing Logic App as an API, I created this App Registration to represent the Application Client scenarios and Application permissions:

In that App Registration, create a new Client Secret for using in Power Automate, and copy this to your clipboard:

Make sure to copy the Application (Client) ID and Tenant ID also:

Now let’s create a new Power Automate Flow to test this scenario. This type of Flow could use a range of different triggers based on your needs, but I’ll just use a Instant Cloud Flow as trigger where I have configured the following inputs:

Note that I have configured userUpn as an optional input.

Next add a “Compose” action for the Client Secret, give the action a name and paste in the Client Secret you created earlier. Note the Lock symbol:

Click on settings and select to Secure Inputs:

Next add a HTTP action, specifying the Method to POST and the URI to be the LogicApp API url, remember to not include the sv, sp and sig query parameters. Set Headers Content-Type to application/json, and under queries add the api-version. For body build the JSON request body using the inputs. We need to build a dynamic expression for userUpn, as this can be optional. I have used the following expression:

if(not(empty(triggerBody()?['text_2'])),triggerBody()?['text_2'],'')

Click to show advanced settings, and choose Authentication to use Azure AD OAuth. Add the authority, tenant id and set audience to the custom Logic App API URI. Then paste in the Application (Client) Id, and use the Outputs from the Compose Client Secret action:

This authentication above will use the Client Credentials Flow to get an access token that will be accepted by the Logic App API.

The remaining parts of this Flow can be exactly the same as the previous Flow we built, a Switch control that continiues on success/failure of the HTTP action:

And then returning Response objects from the HTTP action body for each case:

When testing the Flow now, I can see that the client secret is hidden from all relevant actions:

Summary and Next Steps

We are at the end of another extensive blog post. The focus for this article has been to show how you can use Power Automate to connect to your custom API, that we built in the previous blog post for exposing the Logic App as an API.

The community are increasingly creating Power Platform custom connectors and http actions that sends requests to API’s to Microsoft Graph directly, and that is great but it might result in too extensive permissions given to users and application clients. My focus has been to show how you can control authentication and authorization using on-behalf-of flows hidden behind a Logic App API where users and clients are allowed to send requests based on allowed permission scopes and/or roles, using the powers of Azure Active Directory and OAuth2.

There will be a later blog post in this series also, where I look into how Azure API Management can be used in these scenarios as well.

In the meantime, thanks for reading, hope it has been helpful!

How to Send Requests to GitHub API from Power Platform using Custom Connector

Recently I came across a personal scenario where I use Hugo and GitHub Pages as a team site for a Soccer team I’m coaching and wanted to automate some updates to the web site. I’ve written a blog post previously on how I organized trainings at home using Power Platform: How I as a Soccer Coach…. | GoToGuy Blog, and I am now using Github Pages and Hugo for publishing some statistics and more for that scenario.

In this blog post I will show how I:

  1. Created an OAuth Application for Github API.
  2. Created a Custom Connector in Power Platform for connections to that OAuth Application.
  3. Created Operations for getting content, updating content and triggering workflows for Github Actions.
  4. Connected to Github API using my Azure AD account and user impersonation.
  5. Created a Power Automate Cloud Flow for using the Custom Connector and the defined operations.

Lets get started!

Create OAuth Application for Github API

Start by logging in to your GitHub account and go to Settings. Under Settings you will find Developer Settings where you can access OAuth Apps. You can also go directly to the following URL https://github.com/settings/developers.

Click to Register a new application, and fill in something like the following:

As the above image shows, give the application a descriptive name for your scenario, you can type any homepage URL, this is not important in this scenario. The authorization callback URL is important though, as this will the callback to the Custom Connector we will create later. We can verify the URL later, but use https://global.consent.azure-apim.net/redirect.

Register the application. Next you can change the settings for the registered app. You will have to copy the Client ID, we will need that later. You also need to create Client Secret, make sure to copy that as well, you will only be able to see this once. You can also change some settings like name, logo and branding if you like. This is how my Github App registration looks like now:

We can now proceed to Power Platform to create the Custom Connector.

Create Custom Connector to Github API in Power Platform

Log in to your Power Platform environment, and go to Custom Connectors under Data. Click to create a New custom connector. You can select to create from blank if you want to follow along the steps in my blog post here, or you can select to import an OpenAPI for URL, as I will provide the swagger file at the end of this blog post.

Give the connector a name of your choice and continue:

Next you need to specify “api.github.com” as host. You can also optionally upload a connector icon, as I have done here:

(You can grab the mark logo used above from here, GitHub Logos and Usage, note the usage requirements).

Next, go to Security. Select OAuth 2.0 as authentication type, and then selec GitHub as Identity Provider.

(PS! You can select Generic OAuth 2 also, but it will fall back to GitHub as Identity Provider eventually after all).

Add your Client ID and Secret from the Github OAuth application registration:

It is important to configure the correct scope (or scopes) as this will authorize the client for accessing the API. If you leave the scope blank, you will only get public read only access. You can read more on available scopes here: Scopes for OAuth Apps – GitHub Docs

In my case I want to have full read and write access to public repositories, as well as read write to user profile, so I set the scope to “public_repo user” (use space delimiter for multiple scopes):

I can now click “Create connector”. After creating the security details are now hidden/disabled, and I can verify the Redirect URL to be the same as the Callback URL from the GitHub OAuth app registration:

We can now start defining the operations for the actions I want to do against the GitHub API.

Create Operations for sending requests to GitHub API

When querying and sending request to the GitHub API you need to know the API details and required parameters for what you want. The following link is for the official GitHub Rest API reference: Reference – GitHub Docs.

In my example I want to define the following 3 operations in my Custom Connector:

Under 3. Definition, select to create a New action, and call it something like “Get Repository Content” with the Operation ID set to “GetRepositoryContent”:

Then, under Request, click Import from sample. Select the Verb GET, and under URL type https://api.github.com. The rest of the query we will get from the GitHub API docs. Copy the following fra the REST API reference docs:

So that your sample request now looks like this, remember to add the recommended Accept header:

Click Import. The request will now ask for owner, repo and path as parameters:

Next, click the default response. Here you can copy the sample response from the REST API docs, I’ve copied the sample response for getting file contents:

After that click “Update connector” and we have the first action operation defined.

Click New action again, this time for updating file contents:

For the sample request the Verb is PUT, the URL is the same as when getting file content, but now we need to specify a request body as well:

I’ve created the sample request body based on the docs reference, with just empty placeholder values for the parameters needed. Some of these can be omitted, but message, contents, sha and branch is required for updating an existing file:

{
 "message": "",
 "content": "",
 "sha": "",
 "branch": "",
 "committer": {
  "name": "",
  "email": "",
  "author": {
   "name": "",
   "email": ""
  }
 }
}

After importing the sample request, you can click into the body parameter and change to required for the body itself, as well as the payload parameters that you always want to include from below:

Add a sample default response as well, I’ve copied the example response for updating a file from the docs.

Click “Update connector” again and we are ready to add the third action:

This will be a POST request, with the following URL and request body:

Note from above that “ref” needs to be referencing a branch or tag name as is a required parameter. “Inputs” is an object, depending on your GitHub Actions workflow if incoming parameters is defned, so in many cases this can be empty.

You can leave the default response as it is, as API will return 204 No Content if request is successful.

Click on “Update connector” again, and you should now have 3 actions successfully configured.

We can now proceed to create a connection and authenticate to GitHub API using this custom connector.

Connect to Github API using my Azure AD account and user impersonation

Go to “4. Test”, and click to create a “New connection”. This will create a new authentication popup, and if you’re not already logged in to GitHub you must log in first. Note the correct reference and branding to the “Elven Power Platform OAuth App”:

After logging in I’m prompted to authorize the OAuth app to access data in my account. Note that the scopes “public_repo” and “user” is shown in the authorization request:

If you own other organizations you can grant access to that as well. Click Authorize “OwnerName”: as shown below:

After authorizing you will be redirected back to the Connections, and you should be able to successfully get a new connection object.

Let’s take a look at GitHub settings again, under https://github.com/settings/applications. You should see the OAuth App and the correct permissions configured if you click into details. You can also revoke the access if you need to remove it or reconfigure the scopes for example:

Let’s do a test from the Custom Connector and see what we get. Click on the GetRepositoryContent, and provide the paramaters for “owner” (your GitHub account name), “repo” (any repository, I’m using my GitHub Pages repo here), and a “path” to an existing file in that repo (I’m just testing against my README.md at root, but this can be any subfolder\file also). Click Test operation and see:

This should be successful, note that the response contains a couple of important values for later, the “sha” for the existing file, and the “content” which is a base64 representation of the current contents of the README.md file.

Click on the Request tab, and you will see a preview of how the request was constructed. You will also see the Authorization Header with the Bearer Token:

A couple of important things to note:

  • The request uses an API gateway in Azure APIM, not GitHub directly.
  • The Bearer Token in the Authorization Header is for the Azure API GW audience, so it cannot be used directly against GitHub API.

Copy the entire token value, from after “Bearer <token……>”, and paste it into a JWT debugger like jwt.io. From there we can look at the decoded payload:

From that payload it’s clear that the Token has been issued by my Azure AD tenant and for my logged on user in Power Platform. The scope is user_impersonation, so this will be used in a on-behalf-of flow scenario via the audience defined as apihub.azure.com, which in turn will request from GitHub API resources on my behalf via the APIM gateway used by Power Platform.

You can also lookup the appid from the Token in the Azure AD tenant, and you will find the following Enterprise Application, from where you can enable or disable it on an organization level, or you can examine the sign in logs:

We can test the other operations as well, but let’s create a Flow for that scenario.

Create a Power Automate Cloud Flow for using the Custom Connector to Get and Update File Content

Create a new Cloud Flow, using an instant trigger for manually triggering a flow. Add some inputs like shown below:

Next, add a new action and from under Custom find the GitHub Custom Connector:

Add the “Get Repository Content” action and then fill in the inputs like below:

Next, add a Compose action, with the following dynamic expression:

base64ToString(outputs('Get_Repository_Content')?['body/content'])

This is just for checking what the existing file contents is:

We can do a quick Save and then Test Flow so far, from the Run history I should get the correct inputs, and when finding the existing file the outputs will include the sha value of the existing file, as well as the base64 encoded value of the content:

And when looking at the decoding of the content I can see that the readme.md file content is shown correctly:


Go into Flow edit mode again, and add another Compose action, this time we need to base64 encode the new content I want to update the file with:

Note that the base64 function uses for parameter the input trigger of base64(triggerBody()?['text']), as this is the first text parameter of the trigger.

Add a new action, this time for the Custom Connector again, and the Update File Contents. Specify the owner, repo and path as previously input values, type a custom message for the message, and select the outputs from the “Base64 Updated Content” action, and use the sha value from the “Get Repository Content”. The rest of the values (committer, author objects) are optional:

Save and then do another test, for example like the following to update the README.md file:

And the test should be successful:

I can also verify this at my repository and check the file has been updated. Note also the commit message:

Triggering a GitHub Actions Workflow

The last thing I wanted to go through in this blog post is using the Power Platform Custom Connector to trigger a GitHub Actions workflow. My use case for this is to start a Hugo build when I have dynamically updated files for my static website, but for now I will keep it simple.

I have via a basic template created a simple workflow like this:

This workflow can also be triggered manually using workflow_dispatch, so let’s use that to verify that I can call it from Power Platform.

Add a new action at the end of the Flow, adding the Custom Connector action for Dispatching Workflow event:

Specify Owner and Repo from inputs, and for workflow id either specify ID or the name of the workflow file, in this case blank.yml. The ref parameter is either a branch or tag name, so in my case I use main branch. I leave the other parameters blank as I don’t have any inputs to supply, and use the default Accept header.

Save and Test the Flow again, supplying an updated file content, owner, repo and path similar to what we did previously. When the Flow runs it should complete successfully:

If I go to my GitHub repository, and under Actions, I can see that this workflow has been triggered:

Actually it has been triggered twice, as the first trigger is automatic for the push commit on the file update, and the other (named “CI” in results) is the actual workflow dispatch from the Flow.

Basically this means that I can select some different logic to when my workflows will trigger, either as a push or pull trigger, or as a trigger event based on my Flows. But of course I won’t normally run both triggers 😉

I now have what I need for working further with my personal Hugo and GitHub Pages project, my plan is to update data and assets files from my Power Platform environment, and then trigger a Hugo build for my website. I might blog more on that process later.

Summary and some last thoughts

In this blog post I wanted to show how you can work with the GitHub REST API via a Power Platform Custom Connector. This way you can basically achieve anything that the GitHub API has available, provided the correct scope/scopes has been authorized.

I do want to mention however that there is a GitHub Connector you can use directly in Power Automate, Logic Apps, or Power Apps also: GitHub – Connectors | Microsoft Docs, where you can create a direct connection to your GitHub account. You should take a look at that if that can server your needs.

In my case I needed the API to get or update file contents directly, as well as when using impersonation people in my organization can use their own Azure AD accounts if I share the Custom Connector with them, they don’t need their own GitHub accounts as long as the OAuth App has been authorized on my behalf.

If you want a quickstart on creating the Custom Connector your self, below is the Swagger definition. Thanks for reading, hope it has been useful!

swagger: '2.0'
info: {title: JanVidarElven Github Connector, description: GitHub API Connector for
JanVidarElven, version: '1.0'}
host: api.github.com
basePath: /
schemes: [https]
consumes: []
produces: []
paths:
/repos/{owner}/{repo}/contents/{path}:
get:
responses:
default:
description: default
schema:
type: object
properties:
type: {type: string, description: type}
encoding: {type: string, description: encoding}
size: {type: integer, format: int32, description: size}
name: {type: string, description: name}
path: {type: string, description: path}
content: {type: string, description: content}
sha: {type: string, description: sha}
url: {type: string, description: url}
git_url: {type: string, description: git_url}
html_url: {type: string, description: html_url}
download_url: {type: string, description: download_url}
_links:
type: object
properties:
git: {type: string, description: git}
self: {type: string, description: self}
html: {type: string, description: html}
description: _links
summary: Get Repository Content
operationId: GetRepositoryContent
description: Get File or Folder Content from Repository
parameters:
– {name: owner, in: path, required: true, type: string}
– {name: repo, in: path, required: true, type: string}
– {name: path, in: path, required: true, type: string}
– {name: Accept, in: header, required: false, type: string}
put:
responses:
default:
description: default
schema:
type: object
properties:
content:
type: object
properties:
name: {type: string, description: name}
path: {type: string, description: path}
sha: {type: string, description: sha}
size: {type: integer, format: int32, description: size}
url: {type: string, description: url}
html_url: {type: string, description: html_url}
git_url: {type: string, description: git_url}
download_url: {type: string, description: download_url}
type: {type: string, description: type}
_links:
type: object
properties:
self: {type: string, description: self}
git: {type: string, description: git}
html: {type: string, description: html}
description: _links
description: content
commit:
type: object
properties:
sha: {type: string, description: sha}
node_id: {type: string, description: node_id}
url: {type: string, description: url}
html_url: {type: string, description: html_url}
author:
type: object
properties:
date: {type: string, description: date}
name: {type: string, description: name}
email: {type: string, description: email}
description: author
committer:
type: object
properties:
date: {type: string, description: date}
name: {type: string, description: name}
email: {type: string, description: email}
description: committer
message: {type: string, description: message}
tree:
type: object
properties:
url: {type: string, description: url}
sha: {type: string, description: sha}
description: tree
parents:
type: array
items:
type: object
properties:
url: {type: string, description: url}
html_url: {type: string, description: html_url}
sha: {type: string, description: sha}
description: parents
verification:
type: object
properties:
verified: {type: boolean, description: verified}
reason: {type: string, description: reason}
signature: {type: string, description: signature}
payload: {type: string, description: payload}
description: verification
description: commit
summary: Update File Contents
description: Update existing file in repository
operationId: UpdateFileContents
parameters:
– {name: owner, in: path, required: true, type: string}
– {name: repo, in: path, required: true, type: string}
– {name: path, in: path, required: true, type: string}
– {name: Accept, in: header, required: false, type: string}
– name: body
in: body
required: true
schema:
type: object
properties:
message: {type: string, description: message, title: ''}
content: {type: string, description: content, title: ''}
sha: {type: string, description: sha, title: ''}
branch: {type: string, description: branch, title: ''}
committer:
type: object
properties:
name: {type: string, description: name}
email: {type: string, description: email}
author:
type: object
properties:
name: {type: string, description: name}
email: {type: string, description: email}
description: author
description: committer
required: [branch, content, message, sha]
/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches:
post:
responses:
default:
description: default
schema: {}
summary: Dispatch Workflow Event
operationId: DispatchWorkflowEvent
description: Trigger a GitHub Actions Workflow by ID
parameters:
– {name: owner, in: path, required: true, type: string}
– {name: repo, in: path, required: true, type: string}
– {name: workflow_id, in: path, required: true, type: string}
– {name: Accept, in: header, required: false, type: string}
– name: body
in: body
required: true
schema:
type: object
properties:
ref: {type: string, description: ref, title: ''}
inputs:
type: object
properties: {}
description: inputs
required: [ref]
definitions: {}
parameters: {}
responses: {}
securityDefinitions:
oauth2_auth:
type: oauth2
flow: accessCode
authorizationUrl: https://github.com/login/oauth/authorize
tokenUrl: https://login.windows.net/common/oauth2/authorize
scopes: {public_repo user: public_repo user}
security:
– oauth2_auth: [public_repo user]
tags: []

Blog Series – Power’ing up your Home Office Lights: Part 1 – Get to know your Hue Remote API and prepare for building your solution

This blog post is part of the Blog Series: Power’ing up your Home Office Lights with Power Platform. See introduction post for links to the other articles in the series:
https://gotoguy.blog/2020/12/02/blog-series—powering-up-your-home-office-lights-using-power-platform—introduction/

In this first part I wanted to introduce the requirements and preparations for automating with your Home Lights API. In my case I’m using Philips Hue, as Hue has a well developed API that also can be accessed from remote. And since I’m on the topic of automating with Power Platform, I need to be able to access the API from remote.

In principle, you could use this guide against any light system that has an API, but of course I will show all the examples and config based on Philips Hue in this blog series.

In this blog post I will show you how to set up and be ready for the next parts of this blog series. If you want to dive deeper into understanding the API and testing from remote, I would recommend you read this blog post I published earlier this year about authentication, exploring and controlling using Postman:

Remote Authentication and Controlling Philips Hue API using Postman | GoToGuy Blog

Here’s a quick video introduction to this articles topic, and below we will cover the necessary overview of how you should prepare for the next parts of the blog series:

Create a New Remote Hue API app

The first thing you need to do after you have created 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. It is here where we will specify the HTTP request URL for the Logic App we will create in Part 3 of this blog series. For now, you can just enter some dummy URL like http://localhost/mylogicapp.
  • 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:

As you can see from above image, I’ve already configured the correct URL for my Logic App, meaning the Callback URL from above will trigger the Logic App below.

But as previously mentioned, you can now just specify something like http://localhost/mylogicapp.

Test if we can successfully get Authorization Code

As explained at Remote Authentication – Philips Hue Developer Program (meethue.com), the initial step in the authorization flow is granting permissions for the login user to the resources. This will be done using the following sample request:

GET https://api.meethue.com/oauth2/auth?clientid=<clientid>&appid=<appid>&deviceid=<deviceid>&devicename=<devicename>&state=<state>&response_type=code

Using a Demo App Registration,

ClientId: J9NckRHRPGAoYppWGtjnNJtriTOo5R4Q

AppId: elven_power_platform_demo_app

lets construct that URL, I’ve highlighted the parts you need to replace for your environment:

https://api.meethue.com/oauth2/auth?clientid=J9NckRHRPGAoYppWGtjnNJtriTOo5R4Q&appid=elven_power_platform_demo_app&deviceid=elvendemo&devicename=ElvenDemoLocal&state=anydemostring&response_type=code

Now, copy that URL, and paste it into your Browser, and hit Enter.

If you aren’t logged in with your Hue Developers account already, you must do so, and after that you will need to accept the following permission grant:

Now, if you are using localhost as the callback URL, the following response is perfectly normal:

Note the above authorization code, which is returned to the application together with the state string I supplied for verification. This Code, will together with the ClientId and Secret be used for accessing the Token endpoint and getting an Access Token. But that will come later in this series.

Summary and next steps

We have now prepared the necessary App Registration at Hue Developers portal, and laid the necessary foundations for the next steps in building the logic behind remote authentication.

If you want to explore more about authentication and access tokens, you can do that with the link in the beginning of the blog post using Postman.

Thanks for reading, hope to see you in the next part.

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 🙂