How to use Azure Functions to extend Power Platform - Part 1

Geschrieben von Alan Rachid am 12.11.2024

As I'm preparing for the Azure Developer Certificate (AZ-204) I thought it would be a good idea to write blog posts about some topics which could extend the functionalities of Microsoft Power Platform or Dynamics 365 CE with the power of Azure. Therefore, there will be a series of blog posts that showcase how Azure components are set up to be used. In this article I'm writing about Azure Functions and will answer the questions about what they are and how to connect them to Dataverse environments. In upcoming articles, I will dive deeper into use cases where Azure Functions may be a good choice for extending Dataverse. But this article will only focus on connecting to Dataverse from an external service and not on how to call an Azure Function from within Dataverse (Hint: Webhook).

What are Azure Functions?

Azure Functions is a Function as Service (FaaS) offering from Microsoft which allows developers to focus on writing code and not dealing with the underlying infrastructure like server-setups or Internet Information Services (IIS). On a higher level you could argue that Azure Functions is a serverless cloud service because the whole infrastructure is abstracted away, and you only pay for the execution of functions. Azure Functions are an event driven services where an Input-Trigger causes the function to execute. A triggered Function can receive input-data (Input-Bindings). Those are data sources that pass information into the function when triggered. After the function was executed successfully it can output data (Output-Bindings) into one or more data sinks. There is a wide variety of available Input- and Output-Bindings which can trigger a function or be used inside of a function (for example: Azure Service Bus, Timer, Cosmos DB). For more information see the official documentation. The container for one Azure Function is the so-called Azure Function App, which defines underlying settings for Hosting, Runtime and other global settings. So, a Function App can contain several Functions which thereby share some global settings.

structure of azure functions

Prerequisites to get started

Before we get started to trying out how to integrate Dataverse with Azure Functions there are some steps which you need to process before:

  • Create appregistration in Microsoft Entra Id: First it is necessary to create an app registration inside Micrsoft Entra Id. Therefore, open Microsoft Entra Id in the Azure Portal (portal.azure.com), click on the Add-Button and choose app registration. On the next page choose a suitable name for your app registration, keep the default values, and click create. Finally, you need to grant api permissions to your app registrations. There in the left navigation of Microsoft Entra Id expand the Manage-Area and click on App registrations. In the list choose the newly created once and again navigate to the Manage-Area and choose API permissions. Click on Add a permission and choose Dynamics CRM. The last step is to add a secret to your app registration. Click on Certificates & Secrets in the manage area of your left-hand navigation. Add a new client secret and store the secret value in your password manager. Your app registration is now ready to be used.
  • Add Application User to your Dataverse Environment: As a second step you should register your newly created app registration as an application user in your Dataverse environment. Therefore open the power platform admin center (admin.powerplatform.microsoft.com), click on Environments on the left-side navigation and in the list of environments choose the one you want to work with. In the Access-Area, which is located on the right side of the window, click on 'Show All Users' and then (!IMPORTANT) switch to the list of application users. Now click on Add new app user. Add your app registration, which was created in the previous step, choose the relevant business unit, and assign a security role to your app. As a reminder: The least needed security rights should be chosen and not the system administrator! 😉

After succesfully completing the two steps you are up and running to connect Azure Functions to Dataverse.

Connecting to Dataverse with ServiceClient and Appregistration

After you have created your Azure Functions App project in Visual Studio or Visual Studio Code create a new Http-triggered Function in your App-Project. In my case I'm working with Visual Studio Code and the official Azure Functions Extension. Furthermore, I have the Azure Function Core Tools installed which allow you to create Functions in your command line editor and push them to Azure. Following commands are good to know:
Azure Functions Core Tools
After you have setup your function app with dotnet as runtime you need to add the nuget package “Microsoft.PowerPlatform.Dataverse.Client” to your project. This allows you to use the ServiceClient-class to connect to your Dataverse-Instance.

Side node: The code snippets below uses early bound entities. I was curious, whether it is possible to use them inside of azure functions. So far, it looks like its working. The classes were generated with the Early Bound Generator, written by Daryl LaBar. I don't know if it's a good idea to use them in azure functions, but I don't see any reason not to. I highly appreciate any feedback if you have concerns regarding the usage.
import nuget package
The easiest way to connect to Dataverse is to use the ServiceClient directly in your function and store the secret and all necessary information as plain string in code. Therefore, you only need the secret and clientid of you appregistration and instantiante the ServiceClient class as in the following code snippet.
Dataverse Service Client with inline credentials
This solution is not recommended but it highlights the usage of the ServiceClient. The reason you should not use it is the mentioning of high security information like appsecrets in plaintext. It is a potential security risk which you can easily avoid.

Connect to Dataverse using ServiceClient and appsettings

To avoid any potential security risk, you can stop storing secrets and passwords in clear text by using the appsettings of the function app. To do this, add the following entries to your local appsettings file (local.settings.json):
appSettings.json
I added three new entries in the values section of the local appsettings. The entry "CrmConnection:Url" stores the URL to the target Dataverse environment. The entry "CrmConnection:ClientId" stores the appid/clientid of you appregistration and the entry "CrmConnection:ClientSecret" the corresponding secret of the appregistration. The code to use the appregistration new appregistration entries looks as followed:
Dataverse Service Client with credentials from appSettings.json
To read the appsettings you need to pass a new parameter of type IConfiguration into the constructor and store it as a field in your class. Getting the value of an entry inside of the appsettings is done like this:
Read appSettings configuration
After uploading your functions into your Azure Function App in the cloud you need to add the new appsettings entries into the environment variables. The environment variables can be found in the settings area.
By extracting your confidential information like secrets and environment urls from the code into your appsettings you improve security by avoiding hard-coded secrets. Furthermore the written code is following the Separation of Concerns Pattern by extracting secrets. You are separating configuration data from the application logic. Furthermore it enables us to use CI/CD Practices because inside of your Deployment Pipelines you can now dynamically set the correct values for different environments.

Connect to Dataverse using ServiceClient, appsettings and Azure Key Vault

To completely prevent storing secrets in code or even app configuration, you could move the secrets into Azure Key Vault. Azure Key Vault provides a centralized and secure place to store secrets and manage sensitive data. This reduces the risk of secrets being leaked. In addition by integration Microsoft Entra Id, you can specify which identities are allowed to access specific secrets.

First step to integrate Azure Key Vault is to create the Azure resource (Key Vault) and add your secure information (app secret, CRM Url) to it. For using the Key Vault secret locally in your Function App Project add a new entry to the local appsettings.
Azure Function configuration reading from azure key vault
I have added a new entry in the appsettings the the name "KeyVault:CrmSecret". The corresponding value it the secret identifier of your secret in Key Vault. To access the identifier you need to open the Key Vault, navigate to the secrets area, click on the secret, click on the current version and than copy the value of the secret identifier.
In the code of the function replace the secret value with:
Reading key vault secret from configuration
As a last step add a new environment variable in your Function App Project in Azure with the same name as the entry in your local appsettings and paste the secret identifier into the value field. If you need guidance setting up Key Vault and the secret, check out the following Blog.

Recap and Outlook

This article showcased how to connect to a dataverse instance by using Azure Functions and the ServiceClient class from the nuget package "Microsoft.PowerPlatform.Dataverse.Client". We have learned three different ways to store relevant connection information:

  • Store the connection information hard coded.
  • Store the connection information in appsettings.
  • Store the connection information in Key Vault and access them via appsettings

The most secure approach to store this information is by using Azure Key Vault. We should by all means avoid storing connecting information hard coded. This is not only a security risk but also avoids using CI/CD-practices.

In my next article of this series I will introduce managed identities and showcase how to use them to connect to dataverse from within an Azure Function. Since I am far from being the best developer, the code written in this article could be improved if necessary. Therefore, I am always grateful for feedback.

If you have any questions about our best practices or need support with implementing Microsoft Power Platform or Dynamics 365, please feel free to reach out to us. We look forward to connecting with you.

Nachricht senden