Integrating Dynamics 365 with Azure Function using Managed Identity

I love Azure Functions. These days, I have to restrain the tendency to solve every problem with this Swiss army knife. 

When designing Azure Function integration with Dynamics 365, one of the immediate questions raised is where to store connection strings, credentials and other sensitive details. 
The immediate answer is ‘Not in your code’. So where?

One option is to store sensitive details in the Application Settings store which is ‘encrypted at rest and transmitted over an encrypted channel.’
While this option is  quite easy to use, it isn’t considered most secured.

One option is to store sensitive details in the Application Settings store

Another option is using Managed Identity with Azure Key Vault service.
With this option, Azure Function App is assigned an AAD identity, which is then authorized to access specific Key Vault secrets.
This option is considered more secured as the Function App is specifically granted access to  specific sensitive data, while Application Settings stored data is generally exposed.

In this post, I’ll walkthrough the process of setting and using Managed Identity to Integrate Azure Function with Dynamics 365. In this case, Dynamics 365 access details will be stored in Azure Key Vault.

For the walkthrough, I’ll use the Lead Landing Page scenario, replacing Flow component with Azure Function. Although a bit verbose, the process is quite simple.

Implementation Notes

  • Like most Azure services, Azure Key Vault usage costs money. With Azure Key Vault, any retrieval of secret is paid for. In order to reduce costs, some caching mechanism (which will not be discussed in this post) is in order.

Prerequisites

  1. Have accessible Microsoft Dynamics 365 instance
  2. Have access to Azure environment with sufficient privileges to create Azure Function and Key Vault store.

Walkthrough

  1. Register App with AAD

    Register a new Web App/API in Azure AD with a Client Secret and copy Application Id key and secret to Notepad.

  2. Add an App User to Dynamics 365

    Follow this article: Add a new Application User in Dynamics 365

  3. Create a Function App

    Create a Function App

    Set Function App details and click ‘Create’

    Set Function App details and click ‘Create’

    Add a new Function

    Add a new Function

    Select the HTTP trigger option

    Select the HTTP trigger option

    Click ‘Create’ to complete Function creation

    Click ‘Create’ to complete Function creation

    Leave the function code as is for now, we will alter it later on.

    Leave the function code as is for now, we will alter it later on.

  4. Assign Function App with a Managed Identity

    Go to the Function App Platform Features tab and click ‘Identity’

    Go to the Function App Platform Features tab and click ‘Identity’

    Click the ‘On’ button to assign a System identity and click ‘Save’

    Click the ‘On’ button to assign a System identity and click ‘Save’

    Click ‘Yes’ to enable system assigned managed indentity

    Click ‘Yes’ to enable system assigned managed indentity

    You can see the newly assigned identity object ID

    You can see the newly assigned identity object ID

  5. Setup Azure Key Vault store

    Create a new Azure Key Vault store

    Create a new Azure Key Vault store

    Click ‘Create’ at the bottom of the screen

    Click ‘Create’ at the bottom of the screen

    Click ‘Add new’ under the Access policies blade.
    In the Add access policy, check Get and List in the Key Management Operations group.
    Under the Principal blade, find and select your Function App. Click ‘Select’.
    This will grant our Function identity access to the Key Vault secrets.

    Click ‘Add new’ under the Access policies blade. In the Add access policy, check Get and List in the Key Management Operations group.

    Next, select a Resource Group for the Key Vault store. Click ‘Create’ to complete Azure Key Vault store creation

    Next, select a Resource Group for the Key Vault store

  6. Store secrets in Azure Key Vault

    Find the newly created Azure Key Vault store or access it from the dashboard if possible.

    Find the newly created Azure Key Vault store or access it from the dashboard if possible.

    Access the Secrets area

    Access the Secrets area

    Click Secrets and ‘Generate/import’ to generate a new secret

    Click ‘Generate/import’ to generate a new secret

    Set secret Name (select a self explanatory name, since once created, you won’t be able to see the actual secret value in the area).
    Set the secret string in the Value field. Click ‘Create’.
    In this case, the secret I defined is Dynamics Web API URL, similar to https://<ORGNAME>.api.crm<DATACENTERCODE>.dynamics.com/api/data/v9.1/

    Set secret Name (select a meaningful name, as this will be used in our code). Set the secret string in the Value field

    In the same manner, add additional secrets to hold the applicationId and secret keys you copied after registering an app in AAD (step 1 in this walkthrough).

    In the same manner, add additional secrets to hold the applicationId and secret keys you copied after registering an app in AAD

    Click each of the newly created secrets and copy the Secret Identifier, which will be used in the code to access the secret value

    Click each of the newly created secrets and copy the Secret Identifier, which will be used in the code to access the secret value

  7. Update Azure Function Code 

    Go back to the Function created on step 3 above.
    Click View Files, add a new project.json file and paste in the following definition. Click ‘Save’.

    {
        “frameworks”: {
            “net46”: {
                “dependencies”: {
                    “Microsoft.Azure.KeyVault”: “2.4.0-preview”,
                    “Microsoft.Azure.Services.AppAuthentication”: “1.1.0-preview”                
                }
            }
        }
    }

    image

    Go back to the function code and replace the existing code with the code found here (placed in Github for convenience).

    This code, triggered by an HTTP request from the Lead landing page, performs the following tasks:
    – Receives and parse Lead data
    – Extract Dynamics access details from Azure Key Vault
    – Use access details to generate an access token
    – Create a new Dynamics Lead record using Web API
    – Returns operation result to the caller

    In the GetDyn365AccessDetails method, replace the URLs for the three keys

    dyn365WebAPIURL, dyn365AppIdKVURL, dyn365secretKVURL with the URLs copied on step 6.
    Click ‘Save’.

    Click ‘Get Function URL’ and paste somewhere, as it will be used next

    Click ‘Get Function URL’ and paste somewhere, as it will be used next

  8. Hookup Lead Landing Page to Azure Function

    Last, create a new HTML page, and copy the HTML found here (placed in Github for convenience).
    Find the AzureFunctionRequestURL variable and replace its value with the Azure Function URL copied in the previous step. Save.

    Find the AzureFunctionRequestURL variable and replace its value with the Azure Function URL copied in the previous step

  9. Test

    To test the solution, run the Lead Landing HTML page. Submitting Lead data should results with a new Lead record in Dynamics 365.

    To test the whole flow, run the Lead Landing HTML page

    Submitting Lead data should results with a new Lead record in Dynamics 365.

    If the flow fails, add the address from which the HTML page is executed to the Azure Function CORS collection to authorize it.

    If the flow fails, add the address from which the HTML page is executed to the Azure Function CORS collection to authorize it.

    If the flow fails, add the address from which the HTML page is executed to the Azure Function CORS collection to authorize it.

Advertisements

Implementing Lead Landing Page with Flow (in 10 min. or less)

Microsoft Flow, along with Logic Apps, Power Apps and CDS has revolutionized integration with Microsoft Dynamics 365.
I have been working with Dynamics products since 2005 and when comparing the resources required back then to hook up a landing page to Dynamics, I estimate that modern solutions require less than 5%.
In addition, you don’t have to be an expert developer to implement simple integration scenarios, as declarative mechanisms like Flow and Logic Apps can handle the heavy lifting.    

In this post I’ll walkthrough the process of Implementing a Lead Landing Page with Flow while writing the minimum amount of required code.

The ingredients:

  1. Microsoft Flow Handle Landing Page Lead triggered by HTTP request, creating a Lead record in Microsoft Dynamics 365 instance using Flow built in Dynamics Create Record Action.
  2. HTML page implementing landing page UI along with JavaScript code sending Lead data to the Handle Landing Page Lead Flow.

Prerequisites:

  1. Have access to Microsoft Dynamics 365 online instance and Flow environment residing in the same tenant

Walkthrough:

  1. Create a ‘Handle Landing Page Lead’ Flow 

    Start a Flow from scratch and create a Request trigger. Once saved,  the URL value will be filled automatically.

    Start a Flow from scratch and create a Request trigger. Once saved,  the URL value will be filled automatically.

    Next, add a Parse JSON action and paste in the following schema

    {
        “type”: “object”,
        “properties”: {
             “topic”: {
                “type”: “string”
              },
              “firstName”: {
                 “type”: “string”
              },
              “lastName”: {
                 “type”: “string”
              },
              “email”: {
                 “type”: “string”
              },
              “mobilePhone”: {
                 “type”: “string”
              }
         }
    }

    Next, add a Parse JSON action and paste in the following schema

    Next, add Dynamics Create Record action. Select your target Dynamics 365 instance and map to the Lead entity.
    Map the previous Parse JSON action output values as Lead attributes.

    Next, add Dynamics Create Record action

    Finally, add a parallel branch element. Add two HTTP Response actions, one handling Lead creation success and the other for failure.
    Both returning code 200, each returning the respective message in the response body.
    Click each response ellipsis and set the ‘Configure run after’ option to handle the
    previous step success and failure respectively. 
    Finally, save the Flow.

    Finally, add a parallel branch element

    Click each response ellipsis and set the ‘Configure run after’ option to handle the

  2. Create HTML Landing page

    Create the following HTML page. Replace the flowRequestURL variable value with Flow Request Action URL generated in the first step.

    <html>
    <head>
        <meta charset="utf-8" />
        <title>Implementing Lead Landing Page with Flow</title>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    </head>
    <body style="font-family:'Segoe UI';background-color:lightskyblue">
            <b>Contact us</b>
            <table>
                <tr>
                    <td><label>Topic</label></td>
                    <td><input type="text" id="topic" name="topic" placeholder="insert topic"></td>
                </tr>
                <tr>
                    <td><label>First Name</label></td>
                    <td><input type="text" id="firstName" name="firstName" placeholder="insert first name"></td>
                </tr>
                <tr>
                    <td><label>Last Name</label></td>
                    <td><input type="text" id="lastName" name="lastName" placeholder="insert last name"></td>
                </tr>
                <tr>
                    <td><label>Email Address</label></td>
                    <td><input type="text" id="email" name="email" placeholder="insert email address"></td>
                </tr>
                <tr>
                    <td><label>Mobile Phone</label></td>
                    <td><input type="text" id="mobilePhone" name="mobilePhone" placeholder="insert mobile phone number"></td>
                </tr>
                <tr>
                    <td colspan="2">
                        <input value="Send" type="button" onclick="sendRequest()">
                    </td>
                </tr>
            </table>    
        <br />
        <div id="messageArea"></div>
    
        <script type="text/javascript">
    
            // Send new Lead request to Microsoft Flow
            sendRequest = function () {
    
                //set Flow request URL - available once Flow Request step is saved 
                var flowRequestURL = "https://FLOWREQUESTURL";
    
                //set Lead object
                var leadData = {
                    topic: $("#topic").val(),
                    firstName: $("#firstName").val(),
                    lastName: $("#lastName").val(),
                    email: $("#email").val(),
                    mobilePhone: $("#mobilePhone").val()                
                };
    
                //send request
                var req = new XMLHttpRequest();
                req.open("POST", flowRequestURL, true);
                req.setRequestHeader("Accept", "application/json");
                req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
                req.onreadystatechange = function () {
                    if (this.readyState == 4) {
                        req.onreadystatechange = null;
                        //handle success
                        if (this.status == 200) {
                            $("#messageArea").text(this.response);
                        }
                        //handle failure
                        else {
                            $("#messageArea").text(this.response);
                        }
                    }
                }
                //send request
                req.send(window.JSON.stringify(leadData));
            }
        </script>
    </body>
    </html>
    

Test your landing page by submitting a new Lead. If all works as expected, it will be instantly created in your Dynamics 365 instance.

Test your landing page by submitting a new Lead

If all works as expected, it will be instantly created in your Dynamics 365 instance.

Referencing Dynamics Assemblies with Azure Function Apps v.2

Just stumbled upon a new  Azure environment, where Azure Function Apps have been upgraded to version 2.
Right away, noticed that my Azure Function code referencing Dynamics assemblies does not compile, complaining about

The type or namespace name ‘Xrm’ does not exist in the namespace ‘Microsoft’ (are you missing an assembly reference?)

After digging around, I found out that the project.json is not longer valid with v.2.
Instead, the function.proj file must be created and reference Dynamics assemblies in the following manner:

<Project Sdk=”Microsoft.NET.Sdk”>

  <PropertyGroup>

    <TargetFramework>461</TargetFramework>

  </PropertyGroup>

  <ItemGroup>

    <PackageReference Include=”Microsoft.CrmSdk.CoreAssemblies” Version=”9.0.0.7″/>

    <PackageReference Include=”Microsoft.CrmSdk.XrmTooling.CoreAssembly” Version=”9.0.0.7″/>

  </ItemGroup>

</Project>

Instead, the function.proj file must be created and reference Dynamics assemblies in the following manner

Microsoft Graph API – Assign DYN365 License to AAD User

The automated process of user provisioning becomes common in many projects. Often, the process includes components outside of Dynamics 365 such as  creating a user in Azure Active Directory, assigning plans and licenses and adding user to AAD groups. All of these can be automated using the Microsoft Graph API.

In this post, I’ll demonstrate assigning Microsoft Dynamics 365 license to an existing user with Postman. You can later convert Postman requests to JS or C# code or use in Flow and Logic Apps.

Prerequisites

  1. Have Postman application installed
  2. Have access to Office 365 and Azure environments

Walkthrough

  1. Register a new Web App/API in Azure AD with a Client Secret and copy Application Id key, callback URL and secret to Notepad.
    Make sure your app is granted the ‘Read directory data’ privilege

    Make sure your app has at least the Read directory data privilege

  2. Set a request to retrieve available SKUs

    In Postman, create a new GET request to the following address, after replacing <YOUR_DOMAIN_NAME> with your actual domain name (e.g. bikinibottom.onmicrosoft.com)

    https://graph.microsoft.com/v1.0/<YOUR_DOMAIN_NAME>.onmicrosoft.com/subscribedSkus

    In Postman, create a new GET request

  3. Get an Authorization Token

    Before actually accessing the Graph API, you’ll need an access token to authenticate your requests.
    Click the Authorization tab, and set type to OAuth2.0.
    Click the Get New Access Token and select the Authorization Code option for the Grant type attribute In the dialog opened.
    Fill in the Client ID, Client Secret and Callback URL details copied previously to Notepad.

    Fill in the Client ID, Client Secret and Callback URL details

    Click the ‘Request Token’ button to receive a dialog containing an Access Token. Scroll down and clock ‘Use Token’ button

    Click the ‘Request Token’ button to receive a dialog containing an Access Token

  4. Retrieve Subscriptions

    Click ‘Send’ to execute the request which will retrieve a list of commercial subscriptions that your organization has acquired.

    execute the request which will retrieve a list of commercial subscriptions that your organization has acquired

    If you have Dynamics 365 licenses, the following node will be included in the response.
    Copy the skuId value which will be used in the next step.

    If you have Dynamics 365 licenses, the following node is included in the response

  5. Assign License to User

    Create an additional POST request to assign license to user with the following URL. Replace the target user principal name 
    (e.g.
    sandycheeks@bikinibottom.onmicrosoft.com)

    https://graph.microsoft.com/v1.0/users/<USER_PRINCIPAL_NAME>/assignLicense

    In the Authorization tab, set the same settings as the first request. As you already have a token, no need to get a new one.
    Add a header of  Content-Type = application/json

    Add a header of  Content-Type = application/json

    Set the request body with the previously copied skuId

    Set the request body with the previously copied skuId

    Click ‘Send’ to send the request and assign Dynamics 365 license to the target user.
    A correct response looks like this

    A correct response looks like this

 

 

 

Auto Backup Dynamics 365 Solution using Flow & Dropbox

Harnessing the scaffold suggested in my last post, I would like to suggest a simple way to automatically backup Dynamics 365 solution with Flow/Logic Apps.

The following Flow will allow you to copy Dynamics 365 solution file into Dropbox account on schedule for backup or any other purpose.

You can download the scaffold Flow solution here and import into your Flow environment. Then, update the necessary details according to the following walkthrough. The last Dropbox related Action is not included, you’ll add it yourself.


Prerequisites

  1. Access to Microsoft Dynamics 365 online instance and Flow environment
  2. Register Microsoft Dynamics 365 online instance in Azure AD and have the Application Id key ready.Make sure you set the oauth2AllowImplicitFlow as described here.
  3. Have an accessible Dropbox account.

Walkthrough

Here is the full Flow collapsed Flow:

full Flow collapsed Flow

  1. Download the scaffold Flow solution here and import into your Flow environment
  2. Edit the imported Flow and set the following variables with values to match your environment:
    1. Client id & Dynamics 365 instance URL

      Client id & Dynamics 365 instance URL

    2. Set Dynamics 365 instance user name and password

      Set Dynamics 365 instance user name and password

    3. Set the target Dynamics 365 solution unique name and state the Dropbox target folder name (where the solution file will be created).
      Set false is the target solution is unmanaged, true otherwise

      Set the target Dynamics 365 solution unique name and Dropbox target folder where the solution file will be created.

    4. Add the Dropbox ‘Create File’ Action after the Parse JSON Action:
      Authenticate to your Dropbox account to allow creating the solution File in the target folder

      Authenticate to your Dropbox account to allow creating the solution File in the target folder

Once the Flow is activated, the target solution file will be created on schedule in the target Dropbox folder

Once the Flow is activated, the target solution file will be created on schedule in the target folder

Executing Web API Calls from Flow/Logic Apps

While writing my previous post regarding Flow as a scheduling mechanism,  I stumbled across this post demonstrating how to execute Web API calls from Flow.

Why would you want to execute Web API calls from Flow/Logic Apps?
While Microsoft Flow/Logic Apps support basic Dynamics 365 operations, there are many missing functions that can simplify common integration scenarios between these platforms: executing Custom Actions, triggering Processes, handling metadata and issuing complex queries.
Some of these problems can be worked around using the Command Pattern, but using the Web API from within Flow/Logic Apps provides a simple and powerful integration mechanism.  

In this post, I’ll demonstrate how to authenticate and execute Web API functions and actions from Flow (a similar process can be used with Logic Apps):

  • Execute a FetchXML query
  • Execute a Custom Action

The requirement used to demonstrate is again the automated weekly evaluation of Leads: once a week, any Lead which is older than 5 days and not rated hot is disqualified.
With this implementation approach, Flow is used for scheduling, query for target business records and applying a Dynamics 365 Custom Action to each business record. 

Prerequisite

  1. Have access to Microsoft Dynamics 365 online instance and Flow environment
  2. Register Microsoft Dynamics 365 online instance in Azure AD and have the Application Id key ready.
    Make sure you set the oauth2AllowImplicitFlow as described here.

Walkthrough

  1. Download and import Solutions

    Download and import this Flow solution into your Flow workspace.

    image

    image

    image

    Download, import and publish this unmanaged solution into your Dynamics 365 instance. It contains one Custom Action that will be executed from Flow using Web API. 

  2. Set Flow Settings

    Edit the newly imported Flow

    image

    Set the following keys with values to match your environment

    image

    image

    image

  3. Note the following settings

    This variable holds FetchXML query to retrieve the target Lead records.

    This variable holds FetchXML query to retrieve the target Lead records.

    This variable holds the target entity name as it is used with Web API

    This variable holds the target entity name as it is used with Web API

    This variable holds the target Custom Action to handle each Lead.
    Note that for unbound Custom Action, the Microsoft.Dynamics.CRM should be removed.

    This variable holds the target Custom Action to handle each Lead

    This action requests a token used to authenticate to Web API

    This action requests a token used to authenticate to Web API

    This action executes the FetchXML query

    This action executes the FetchXML query
    This action parses the query resulting records.
    If you change the FetchXML query to support a different entity or attributes, make sure you also change the JSON schema to support it

    This action parses the query result

    This action executes the target Custom Action for each Lead record returned by the query

    This action executes the Custom Action for each Lead record returned by the query

  4. Test your Flow

    After a successful run, Flow will display the number of affected records.
    In Dynamics 365, these records should appear as disabled now.

    image

    If your Flow fails on the RequestOAuth2Token action, try this solution

Implementation Notes 

  • I prefer authenticating using an Application Id and Secret via an App user rather than plain user credentials. 

Execute a Recurring Job in Microsoft Dynamics 365 with Flow

I have written about executing recurring jobs in Dynamics 365 few times in the past. Over time, I suggested different scheduling mechanisms such as Microsoft Dynamics Workflow Timeout step or Azure Scheduler, as the pattern I suggested allows changing the scheduling mechanism without impacting other solution parts.

Asynchronous Batch Process Pattern

Flow can be also used as a scheduling mechanism, one that does not require coding like Azure Function,  as it has a built in integration with Microsoft Dynamics 365 Online.
Once invoked on schedule, the executing component query Dynamics 365 for target business records and apply some business logic (Process) to each business record.

In this post I would like to demonstrate a solution for executing a recurring job in Microsoft Dynamics 365 using Flow as the scheduling component.

Sample business requirement

Weekly Leads Evaluation: once a week, disqualify all Leads that are more than 5 days old and are not rated ‘Hot’.
Add a note to each Lead record to indicate that it was disqualified by an automated process.

Walkthrough

  1. Download, Import and publish the ABP unmanaged solution.
  2. Create the Action component: Disqualify Lead Workflow

    This Workflow Rule will be applied to each valid target business record. It simply attaches a Note and changes the Lead record state to Disqualified. It can be a/synchronous, but you may want to start with a asynchronous execution to monitor the process execution.
    Make sure you define the process as an on-demand process with no automated triggers.

    Create the Action component: Disqualify Lead Workflow

  3. Define Target Business Records Query

    Using the Advanced Find, define a new query to retrieve all Lead records where Created On date is older than 5 days and are not rated ‘Hot’.
    Click to Edit Columns button and remove all possible columns to maximize the query efficiency.
    Click the Download Fetch XML button and open the resulting file with some text editor. Copy the FetchXML query text to the clipboard

    Define Target Business Records Query

  4. Create a Batch Process Record

    This record is used to orchestrate by holding a FetchXML query to define the target business records and also the Action process which will be applied on the the target business records.
    If you imported an unmanaged solution, find the Batch Process entity and set it to appear in the Settings area.
    Create a Batch Process record and paste the FetchXML query text into the Target Records textbox.
    Name the record ‘Weekly Leads Evaluation Process’.
    Select the Disqualify Lead Workflow (created in step 2) in the Process Lookup field and save the record.
    Copy the Reference Token value for the next step.

    Create a Batch Process Record

  5. Define Flow Rule

    In your tenant, navigate to the Flow area and create an empty Flow named Weekly Leads Evaluation.
    Define a Recurrence Trigger to trigger the batch process

    Define a Recurrence Trigger to trigger the batch processDefine a Recurrence Trigger to trigger the batch process

    Next, define a List records action.
    In the Filter Query text box, paste the Batch Process Reference Token value and precede it with dyn_referencetoken eq (e.g. dyn_referencetoken eq ‘LOGY8Y1W6’).

    Next, define a List records action

    Define Update record action which will automatically wrap it with Apply to each wrapper. Add a Condition before the Update Action to verify that the Flow operates only if the Batch Process record status is Scheduled. 

    Define Update record action which will automatically wrap it with Apply to each wrapper

  6. Activate

    Change the Batch Process record status to Scheduled and save.
    Activate the Flow Rule.
    You can monitor the Flow activation by navigating to the Run History area.

    You can monitor the Flow activation by navigating to the Run History area

    For each run, you can see the completion status (success/failure) and failure reason

    For each run, you can see completion status (success/failure) and failure reason

Similarly, you can now add additional scheduled batch processes. 

Implementation Details