Create a Custom Connector for Azure AD protected Azure Functions

A key principle with Power Apps connectors that use Azure Active Directory (AAD) for authentication is that they don't provide users with access to any data that the user doesn't already have access to. This is because the API call to the AAD protected service executes under the user identity used to log in to the connector. Therefore, the target service maintains responsibility for enforcing what is permitted for the authenticated user.

This tutorial shows you how to use Azure Functions to build a REST API, enable AAD authentication, and then make it available to Power Apps as a Custom Connector.

Create Azure Functions using Visual Studio Code

With Azure Functions, there are many options to consider including hosting options, language choice, and authoring options such as using the Azure Portal, Visual Studio Code, Visual Studio, and so on. For this tutorial, we'll use C# and Visual Studio code. To complete this tutorial, you must first complete the Quickstart: Create an Azure Functions project using Visual Studio Code tutorial. Make note of the name you provided for your Function App in the Publish the project to Azure step.

Important

Do not complete the Clean up resources section. You'll need to keep all your resources in place to build on what you've already done.

Protect calls to Azure Functions using AAD

  1. Find your Function App in the Azure Portal. Select the name of your Function App in the list.

    List of Function Apps in the Azure Portal

  2. In the top section, switch to the Platform features tab,

    The 'Platform features' tab highlighted on the Function App blade in the Azure portal

  3. Next, from the Networking group, select the Authentication / Authorization link.

    The 'Authentication / Authorization' link highlighted on the Function App blade in the Azure portal

  4. On the Authentication / Authorization blade, enable App Service Authentication by switching the App Service Authentication toggle button to On.

    The 'On' option for the 'App Service Authentication' toggle button highlighted in the Function App authentication settings in the Azure portal

  5. In the Action to take when request not authenticated drop down, change the value to Log in with Azure Active Directory. This setting ensures that anonymous requests to the API aren't allowed.

    The 'Log in with Azure Active Directory' option highlighted in the 'Action to take when request not authenticated' drop down on the Function App authentication settings blade

  6. Next, in the list of authentication providers, select Azure Active Directory.

    'Azure Active Directory' highlighted in the list of authentication providers for a Function App

  7. In the Azure Active Directory Settings blade, set the Management mode option to Express. Set the second Management mode option to Create New AD App.

    Azure Active Directory Settings blade opened for a Function app in the Azure portal

    Important

    Before you continue, note the value in the Create App field and copy/paste it somewhere for later. This value represents the name of the AAD application that you'll use to secure the API. You'll use this value later when you configure your Custom Connector.

  8. Select the OK button to confirm your selection.

  9. Back in the Authentication / Authorization blade, select Save to update the Function App's authentication and authorization settings.

    The 'Save' button highlighted on the 'Authentication / Authorization' blade for a Function App in the Azure portal

  10. After saving, select Azure Active Directory in the Authentication Providers section.

    Azure Active Directory Provider

  11. Select the Azure AD App, then copy the Client Id value and paste it somewhere for use later.

    Client Id page

  12. Confirm that the API is correctly secured by opening a new browser window in private mode and navigating to the API. The URL for your Function App can be found in the Overview section of the Function App blade. If the authentication settings have been applied correctly, you should be redirected to the Azure AD login page.

    Azure AD login page

Create a Custom Connector for your Azure Function

To create a Custom Connector that uses AAD authentication, you must create an AAD app registration to secure the Custom Connector and acquire delegated access to the Azure Functions protected by the AAD app registration created in the Protect calls to Azure Functions using AAD section.

Create an app registration for your Custom Connector in AAD

First, create an AAD application for your Custom Connector. This is required to grant the Custom Connector permission to call your Azure Functions.

  1. Navigate to the App registrations page in the Azure Portal.

    Azure AD registrations page in the Azure Portal

  2. In the list of registered applications, select New registration.

    Add button

  3. Enter a name for your application, select the options shown in the screenshot below, and then select the Register button.

    Register an application form

    Note

    To learn more about app registration options, see Quickstart: Register an application with the Microsoft identity platform.

  4. After selecting the Register button, you will be shown API permissions for your App registration.

    API permissions screen

  5. Select the Add a permission button.

    Add a permission button

  6. Select the APIs my organization uses tab, then search for the app name from step 7 in the Protect calls to Azure Functions using AAD section. When you find it, click the name of the app.

    Important

    Your app name will be different than what's in the screenshot.

    Azure Function app name

  7. Select the user_impersonation checkbox and click the Add permissions button.

    Add user_impersonation permission

    Note

    To learn more about permissions, see Permissions and consent in the Microsoft identity platform endpoint.

  8. Select Certificates & secrets, then select the New client secret button.

    Certificates & secrets

  9. Enter a description for your secret, select an expiration period, and then select Add.

    Add a client secret

  10. Your new secret is displayed. Copy and paste the value somewhere. You'll need it later.

    Create a key

  11. Select Overview, then copy and paste the value of the Application (client) ID somewhere. You'll need it later.

    Create a key

Keep this page open so you can come back to it. There is one more step in the Azure portal, but first you create a custom connector.

Create a custom connector

Now that the AAD application is configured, you create the Custom Connector. To create a Custom Connector, you must describe the API you want to connect to so that the connector understands the API's operations and data structures. When building Azure Functions for Custom Connectors, you will often iteratively build your connector by repeating the following steps:

  1. Create, test, and debug the Azure Function locally with Visual Studio Code.
  2. Deploy your Function App to Azure.
  3. Update the Custom Connector configuration to reflect changes in your Function App.

Postman is a popular tool to test web APIs. It's often used to test Azure Functions. In this topic, you'll create a custom connector from a Postman collection. The Postman collection will be provided for you.

Note

See the Create a Postman collection for a custom connector topic to learn how to create your own. Using Postman to create a Custom Connector is just one option. See the Describe the API and define the custom connector topic to learn about others.

  1. In a new browser tab, sign in to Power Apps or Power Automate.

  2. Copy the code below. Using your favorite text editor, create a new file, and paste the code as the contents of the file. Save the file. You can name it whatever you prefer, but remember the name (ex: tutorial-custom-connector.json). You'll need to use this file in the next steps.

    {
        "id": "c4b5deba-f97b-47d0-82a5-a2b32561fb01",
        "name": "Custom Connector",
        "description": null,
        "auth": null,
        "events": null,
        "variables": [],
        "order": [
            "374365a1-ede5-4ead-8068-d878085dad26"
        ],
        "folders_order": [],
        "protocolProfileBehavior": {},
        "folders": [],
        "requests": [
            {
                "id": "374365a1-ede5-4ead-8068-d878085dad26",
                "name": "Hello",
                "url": "http://localhost:7071/api/Hello",
                "description": "",
                "data": [],
                "dataOptions": {
                    "raw": {
                        "language": "json"
                    }
                },
                "dataMode": "raw",
                "headerData": [
                    {
                        "key": "Content-Type",
                        "name": "Content-Type",
                        "value": "application/json",
                        "description": "",
                        "type": "text"
                    }
                ],
                "method": "POST",
                "pathVariableData": [],
                "queryParams": [],
                "auth": null,
                "events": null,
                "folder": null,
                "responses": [
                    {
                        "id": "46baba58-7b85-4a2e-8c7d-303080e08ba9",
                        "name": "Hello",
                        "status": null,
                        "mime": null,
                        "language": "plain",
                        "text": "Hello, Marc. This HTTP triggered function executed successfully.",
                        "responseCode": {
                            "code": 200,
                            "name": "OK"
                        },
                        "requestObject": {
                            "data": [],
                            "dataMode": "raw",
                            "dataOptions": {
                                "raw": {
                                    "language": "json"
                                }
                            },
                            "headerData": [
                                {
                                    "key": "Content-Type",
                                    "name": "Content-Type",
                                    "value": "application/json",
                                    "description": "",
                                    "type": "text"
                                }
                            ],
                            "method": "POST",
                            "pathVariableData": [],
                            "queryParams": [],
                            "url": "http://localhost:7071/api/Hello",
                            "rawModeData": "{\n\t\"name\": \"Marc\"\n}"
                        },
                        "headers": [
                            {
                                "key": "Date",
                                "value": "Wed, 04 Mar 2020 22:32:06 GMT"
                            },
                            {
                                "key": "Content-Type",
                                "value": "text/plain; charset=utf-8"
                            },
                            {
                                "key": "Server",
                                "value": "Kestrel"
                            },
                            {
                                "key": "Transfer-Encoding",
                                "value": "chunked"
                            }
                        ],
                        "cookies": null,
                        "request": "374365a1-ede5-4ead-8068-d878085dad26",
                        "collection": "c4b5deba-f97b-47d0-82a5-a2b32561fb01"
                    }
                ],
                "rawModeData": "{\n\t\"name\": \"Marc\"\n}",
                "headers": "Content-Type: application/json\n",
                "pathVariables": {}
            }
        ]
    }
    
  3. In the navigation pane, select Data to expand it, then select Custom Connectors.

    Custom connectors in navigation pane

  4. In the upper right corner, select New custom connector, then Import a Postman collection.

    Import a Postman collection

  5. Enter a name for the connector, select the Import button, browse to the file you created in step 2, then select Continue.

    Name and file location

  6. The General page opens. Change the Scheme to HTTPS, replace the Host value with the domain of your Function App, and then advance the wizard to the Security page.

    General information settings

  7. On the Security page, provide AAD information for the application.

    OAuth settings

    After entering security information, select the Update connector button to create the Custom Connector.

  8. On the Security page, the Redirect URL field is now populated. Copy this URL so you can use it in the next section of this tutorial.

    Note

    You may need to scroll down to see the Redirect URL.

    Resource URL

  9. In another browser tab, return to the the app registration you created in the Create an app registration for your Custom Connector in AAD section. Make sure you're in Overview section, then select Add a Redirect URI.

    Resource URL

  10. Select the Add a platform button.

    Add a platform button

  11. In the Configure platforms pane, select Web.

    Configure platforms

  12. In the Configure Web pane, paste the Redirect URL value from step 7, then select the Configure button.

    Configure platforms

  13. Return to the browser tab containing the Custom Connector configuration. You should still be on the Security page in the wizard.

  14. Advance to the Definition page in the wizard by selecting the word Definition and review.

    Configure platforms

    Note

    The Definition comes from the Postman collection you imported. If you changed any of the code in the Quickstart: Create an Azure Functions project using Visual Studio Code tutorial, the import file might not match the shape of your API. You'll either need to create a new Postman collection or manually adjust the action in the Custom Connector. To create a new Postman collection, use the links shared earlier in this tutorial here. To manually adjust the action, you can review the Create the connector definition section of the Create a custom connector from scratch tutorial.

Test the connector

Now that you've created the connector, test it to make sure it's working properly. Testing is currently available only in Power Automate and Power Apps.

  1. Advance to the Test page in the wizard by selecting the word Test.

    Test page

  2. On the Test page, select New connection.

    New connection

  3. Select the Create button, then log in with your AAD user.

    New connection

  4. Return to the Test page.

    • In Power Automate, you're taken back to the Test page. Choose the refresh icon to make sure the connection information is updated.

      Refresh connection

    • In Power Apps, you're taken to the list of connections available in the current environment. In the navigation pane, select Custom Connectors. Find your Custom Connector an select the edit icon.

      Custom connectors in navigation pane

  5. Navigate back to the Test page, enter a value for the name field, then select Test operation.

    Test operation

  6. Review the Request and Response.

    Connector request

    Connector request

Next steps

Now that you've created a custom connector and defined its behaviors, you can use the connector.