Overview
This post provides details for creating an Azure Function (C# Script; .csx) that connects to Dynamics 365 Online (Customer Engagement). This approach uses a combination of an Azure AD application and Application User in Dynamics 365 for server-to-server authentication.
There are several benefits to using Azure Functions with Dynamics 365. Some of these benefits include:
- Perform D365 CRUD operations, run custom actions, etc. using the IOrganizationService interface
- Run the function on a schedule (e.g., run each day at 1 AM)
- Trigger the function via HTTP, new file in Azure storage, message queue, etc.
- "Serverless": No Windows machines to maintain
- Use functions from Azure Logic Apps, which has 100+ connectors to other services
Prerequisites
- Rights to create resources in an Azure subscription
- Admin account for a Dynamics 365 instance (v8.2 or newer)
- Azure and Dynamics 365 are in the same tenant
Create an Azure AD App Registration
In the D365 connectivity approach described in this article, the Azure Function(s) you create will obtain an authentication access token from Azure AD. An access token is used to allow your Function to access Dynamics 365 without the need to provide credentials. This section, then, provides the steps for creating an Azure AD app registration for the purpose of obtaining an access token.
You can utilize the same Azure AD app registration for multiple Azure Functions. Thus, when providing the name for the Azure AD application registration, you may want to provide a generic name such as "AzureFunctionsD365ClientApp".
Follow these steps to create an Azure Active Directory app registration and collect the information needed by the Azure Function.
- Open a new notepad (or other) file to paste values that you'll collect below.
- Sign in to the Azure Portal
- Click Azure Active Directory in the far-left navigation menu
- Click "App registrations"
- Click Endpoints (at the top)
- Copy the Federation Metadata Document URL to a text editor file. In particular, you will need the GUID that follows the ".com" in the URL. This is the Azure AD Tenant ID (Also known as Directory ID).
- Close the Endpoint blade and click "New application registration"
- For the app registration name (e.g., "AzureFunctionsD365ClientApp")
- Select "Web app / API" as the application type.
- Enter https://localhost or any valid URL in this field. (Note: Since this article describes how to connect to Dynamics 365 from an Azure Function in the same tenant, the URL in this field will not be used.)
- Click the Create button
- Open the Application Registration that you just created. Copy the Application ID and paste to the text file.
- Click Settings, then click Keys. In the "Passwords" section, create a new key by entering "NeverExpires" in the description field and selection "Never expires" for the duration. Click Save and then copy the key value to the text editor
- You may now close the Azure AD screens (blades)
Next, you will create an Application User record in Dynamics 365 and link that record to the Azure AD registration created above.
Create a Dynamics 365 Application User
This section provides the steps for creating an Application User account in Dynamics 365. This account will be connected to the Azure AD app registration to provide server-to-server connectivity from Azure to Dynamics 365.
- Sign in (as an administrator) to the Dynamics 365 instance that your Azure Functions will connect to.
- Navigate to Settings >> Security >> Users
- Change the view to Application Users
- Click the New button
- Create the new Application User
- Make sure the form name is "User: Application User"
- User Name: Provide a user name that describes the main purpose for the Application User. For example, if you intend to use the Application User for general purpose use in Dynamics 365 (e.g., from Azure Functions or other Azure applications), then enter a name such as "AzureDynamics@yourdomain.com".
- Application ID = Provide the Application ID (GUID) that you copied to a text file when creating the Azure AD registration
- Note: Dynamics 365 allows only one Application User per Application ID
- First Name = Enter "Azure Dynamics" or other name to identify the user record
- Last Name = Enter “Application” (recommended) or any other value
- Primary Email = You can enter the same value as entered for the User Name, or enter any other valid email address.
- Click Save
- Assign a custom security role for the Application User.
Create an Azure Function
This section provides the steps and source code for an Azure Function that connects to Dynamics 365 Online (Customer Engagement).
- Sign in to the Azure Portal
- (Optional) Create a Resource Group for this and future Azure Function apps
- Create a Function App following the steps in the linked article
- Note: A function app is the container that hosts individual functions. Thus, when providing a name for the function app, enter a name that describes the purpose for the individual functions. For example, if the functions in the function app will send email messages on a schedule from data in Dynamics 365, then enter a name such as "D365ScheduledEmailsFunctionApp".
- In the Function App, create a function using the HTTP trigger template (C#).
- Function name example: D365HappyBirthdayEmail
- Within the function, click the "View files" tab. Then click the Add button and enter "project.json" as the file name. Copy and paste the project.json content provided below and then click Save. (Use Notepad++ to remove blank lines if they appear when you paste the text.)
- Click run.csx to open the Azure function C# code in the edit window. Copy and paste the code provided in the run.csx section below. Click Save to save the function.
- Provide actual values from Dynamics 365 and Azure in the constants set up toward the beginning of the function.
- Save and test the function. The expected result is a number that represents the number of accounts created in the Dynamics 365 instance in the past two years.
project.json
{"frameworks": {
"net46":{
"dependencies": {
"Microsoft.CrmSdk.CoreAssemblies": "8.2.0.2",
"Microsoft.IdentityModel.Clients.ActiveDirectory": "2.22.302111727"
}
}
}
}
run.csx
using Microsoft.IdentityModel.Clients.ActiveDirectory;using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk.WebServiceClient;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net;
using System.Text;
private const string DYNAMICS_365_URL = "https://contoso.crm.dynamics.com";
private const string AZURE_TENANT_ID = "YOUR TENANT'S ID (GUID) GOES HERE";
private const string AZURE_AD_APPLICATION_ID = "YOUR AZURE AD APP ID (GUID) GOES HERE";
private const string AZURE_AD_APPLICATION_KEY = "YOUR AZURE AD APP KEY GOES HERE";
private const string AZURE_AD_AUTHORITY_URL = "https://login.microsoftonline.com/";
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
var requestedToken = GetD365AccessToken();
string value = string.Empty;
try
{
using (var crmService = new OrganizationWebProxyClient(GetServiceUrl(), false))
{
crmService.HeaderToken = requestedToken;
// Example: Retrieve an aggregate value from a FetchXML query.
string fetchXml = GetFetchXmlQuery();
EntityCollection queryResults = crmService.RetrieveMultiple(new FetchExpression(fetchXml));
if (queryResults != null)
{
Entity record = queryResults.Entities.FirstOrDefault();
value = ((AliasedValue)record["RecordCount"]).Value.ToString();
log.Info("value = " + value);
}
}
}
catch (Exception ex)
{
log.Error(ex.Message);
var result = new HttpResponseMessage(HttpStatusCode.InternalServerError);
result.Content = new StringContent(ex.Message);
return result;
}
return req.CreateResponse(HttpStatusCode.OK, value);
}
// Get an authorization token from Azure AD
private static string GetD365AccessToken()
{
var clientCredential = new ClientCredential(AZURE_AD_APPLICATION_ID, AZURE_AD_APPLICATION_KEY);
var authenticationContext = new AuthenticationContext(AZURE_AD_AUTHORITY_URL + AZURE_TENANT_ID);
var authenticationResult = authenticationContext.AcquireTokenAsync(DYNAMICS_365_URL, clientCredential);
var requestedToken = authenticationResult.Result.AccessToken;
return requestedToken;
}
private static Uri GetServiceUrl()
{
return new Uri(DYNAMICS_365_URL + "/xrmservices/2011/organization.svc/web?SdkClientVersion=8.2");
}
private static string GetFetchXmlQuery()
{
return @"<fetch aggregate='true' >
<entity name='account' >
<attribute name='name' alias='RecordCount' aggregate='count' />
<filter type='and' >
<condition attribute='createdon' operator='last-x-years' value='2' />
</filter>
</entity>
</fetch>";
}
Related Articles
Accessing Azure App Services using Azure AD Bearer tokenBuild web applications using Server-to-Server (S2S) authentication
No comments:
Post a Comment