Dynamics 365 CRM and Azure Application Insights integration

Hello everybody,

Today, we will discuss about the integration of the Dynamics 365 and Azure application insights.

But what is Azure Application Insights ?

Azure application insights is a very powerful tool (free !) you can access from your azure subscription in order to track what's happening on your application.
This allow you to do analysis over time by checking the different logs, events or even exceptions which occurred on your apps.

I kinda love this tool because it provides you a central place for all your tracking, allow you to create dashboards to have an overview of what's going on. In a word, it's a must have today !

Introduction of what we will describe here :

Since version 8.2, Dynamics 365 is more and more integrated with Azure tools.
With this version, you can in several clicks, send your CRM data into Azure Service Bus part 1 and part 2.

But here we will focus on the integration with Application Insights.
There are 2 different integrations, the client side integration and server side one.

What we will see in this article :

  1. Client side integration.
  2. Server side integration for out of the box CRM actions.
  3. Server side integration for Plugins exception handling.

Pre-Requisites :

Client side integration can be done for any CRM version either with Online and OnPremise.
Server side integration needs a Dynamics 365 online instance running with the version 9.X for our use case.

Assumptions : You have an azure functions and an application insights created, in order to perform the following steps.

1. Client side integration

When you configure an application insights in Azure, a javascript code will be provided to allow you to track everything which is happening on your CRM Forms (javascript events, exceptions, errors while loading a page, etc).
You can find the how to on the docs.microsoft.com with a nice walkthrough or also here by Dilip Kumar.

This is a good opportunity to improve your code in order to speed up the page loading or code logic for example.
Drawback here, you must manually add the Application Insights JavaScript code on each form you want to track, and can't add it to list view pages or any other pages where you can't add custom JavaScript code.

2. Server side integration for out of the box CRM actions

Here is a schema of what we will do with "almost" no code :

Why using an Azure Function here ?

Using ILMerge to integrate the Application Insights assembly directly inside your Plugin assembly is unfortunately not supported by Microsoft.
"8/31/15 : This post has been edited to reflect that Dynamics CRM does not support ILMerge. It isn't blocked, but it isn't supported, as an option for referencing custom assemblies."

So let's move on, and focus on the process described above.

First step : configuring the CRM Side

As mentioned in the prerequisites, with the version 9.x, there is a new option with the plugin registration tool : Register New Web Hook
This will be the clue for the first step of our process.
Once you created your azure functions in the Azure portal, you will get a link like : https://YourAzureFunctionsNamespace.azurewebsites.net/api/FunctionName?code=somecharactershere, this link contains all necessary pieces of information to create our webhook from the Plugin Registration Tool.

Example :

After the creation of the webhook, you have the possibility to create steps (like for a regular plugin step) in order to send data to your azure functions while will process this data.
/!\ The step should be running asynchronously to avoid freezing your CRM (we never know !)

Second step : configuring the Azure Functions side

By default, when you create an azure functions with the httpTrigger, you will have something like :

using System.Net;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)  
{
    log.Info("C# HTTP trigger function processed a request.");

    // parse query parameter
    string name = req.GetQueryNameValuePairs()
        .FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
        .Value;

    if (name == null)
    {
        // Get request body
        dynamic data = await req.Content.ReadAsAsync<object>();
        name = data?.name;
    }

    return name == null
        ? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name on the query string or in the request body")
        : req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
}

We will perform few changes here in order to handle our CRM data properly.

1- Azure functions configuration

Making sure we integrate the Application Insights assembly. In order to do that, we will create a file called "project.json" in the functions :

  • Open your Azure functions
  • Click on the right pane to "View files"
  • Click the Add button
  • Call the file : "project.json"
  • Copy the following :
{
  "frameworks": {
    "net46":{
      "dependencies": {
        "Microsoft.ApplicationInsights": "2.4.0"
      }
    }
   }
}

This will allow you to have access to the Microsoft.ApplicationInsights assembly within the Functions.

2- Azure Functions code modifications

Then we will modify the code inside the functions, in order to process the CRM data and basically send it to the Application Insights like that.

Note that we defined an extra properties called "DataCRM" here which will contain the data coming from the CRM and passed as a parameter to the TrackEvent function of the Telemetry object.

Warning: Your Azure Functions must return a HttpStatusCode otherwise, your plugin will fail.

The result will looks like this from your Azure functions :
The result will looks like this from your Application Insights :

You can see on the screenshot above that we have the entire data stack coming from the PluginExecutionContext with the modified/created data on the CRM side.

3- Do what you want with this useful data !

On course you can process this data to extract specific pieces of information and process it on Insights side in order to perform reports or other things.

3. Server side integration for Plugins exception handling.

Here is what we want to achieve this time :

Let's say that this is the interesting part (from my point of view :)).
You will tell me that you don't care because there is an entity dedicated to the plugins actions inside the CRM to have an overview of what's going on in your instance.
That's true but Application Insights is way easier to use to group/filter/analyze those exceptions. You can really customize the rendering based on your needs, choose what will be sent to Application Insights to improve a proactive way the functionalities you are providing with your custom code.

For this part, we will use the same system as the point above with the OOTB functionality and will perform slight changes to handle the exceptions "only".

Scenario here : We want to catch all Plugin exceptions and send the following data to Application Insights :

  • EntityName
  • Plugin action
  • Exception Message
  • Exception StackTrace

(of course, you can add whatever you want more !)

First step : Plugin Assembly side

We will override the Plugin class which implement the IPlugin interface.
Inside the Execute function, we will add a catch which is specific to the Plugin errors between the try and catch (FaultException<OrganizationServiceFault> e)

catch (InvalidPluginExecutionException ex)  
{
    try
    {
        IServiceEndpointNotificationService cloudService = (IServiceEndpointNotificationService)serviceProvider.GetService(typeof(IServiceEndpointNotificationService));
        if (cloudService == null)
            throw new InvalidPluginExecutionException("Failed to retrieve the service bus service.");

        localcontext.PluginExecutionContext.SharedVariables.Add("EntityName", localcontext.PluginExecutionContext.PrimaryEntityName);
        localcontext.PluginExecutionContext.SharedVariables.Add("PluginAction", localcontext.PluginExecutionContext.MessageName);
        localcontext.PluginExecutionContext.SharedVariables.Add("ExceptionMessage", ex.Message);
        localcontext.PluginExecutionContext.SharedVariables.Add("ExceptionStackTrace", ex.StackTrace);

        string response = cloudService.Execute(new EntityReference("serviceendpoint", new Guid("ServiceEndPointGUID")), localcontext.PluginExecutionContext);

    }
    catch  {}

    throw;
}

A bit of explanation here for the code above :
We are using the Azure-aware plugin to send the PluginContext to Azure (in our case the Azure Functions), the docs is here (thanks Radu for the hint here)

To retrieve your ServiceEndpointGuid created from the PluginRegistrationTool earlier, you can go to your WebApi : https://tenant.crmX.dynamics.com/api/data/v9.0/serviceendpoints?$select=serviceendpointid,name and find the right ServiceEndPoint based on the name you provided and keep the serviceendpointid

An other interesting point is, if you give a closer look on the code above, i'm using the SharedVariables property to send my "custom" data because from the standard perspective, we don't have the exception pieces of information in the PluginExecutionContext which we are sending.

Second step : Azure Functions code

We have to customize a bit more the code to handle the new stuff here.
Regarding the configuration, we will just add the package "Newtonsoft.Json": "11.0.2" in the dependencies of the project.json in your Azure Functions.

Now, we will dive into the code :

You can see that we have a similar process here if we compare to the OOTB version. We simply need extra lines of code in order to retrieve the specific values from the ShareVariables node in the JSON received and use them.

The result in Application Insights will look like :

Once this is done, you can gather the data for your plugin exceptions, filter them by the custom properties we are sending from the Plugin Class.
This will give you the possibility to spot the most common errors and fix them.

Next possible steps

We've seen how to improve your proactive CRM trackings using Application Insights either for the client side but also for the Service side.
What is described here is a sample which can be improved / completed based on your needs of course.
Create notifications / reports based on the data received.
Use your mind to go further! (i'm open to any suggestions by the way)

Conclusion

Azure Application Insights is so powerful you can't have missed it !
Having a central place to track either client and server side to improve your CRM instance is really an important point to provide a nice tool to your end users.

I hope this will help you,
Happy CRM'in

Clément

PS : full code for the plugin class here, custom code starting line 197
PS2 : full code for the Azure Functions sending the exceptions here

Clement

Dynamics 365 CRM & Power Platform addict.