Azure · Azure Function · Data Migration And Integration

Integrate Dynamics CRM to Service Bus queue using Azure function

It has been a while since I have written any blog. In today’s blog I will walkthrough the process to integrate Service Bus Queue to Dynamics CRM using Azure function.

Requirement :-

The requirement is whenever any message is added to Service Bus Queue, we want to create IOT Alert records in CRM. For this post I have considered Message format in Json format.

Approach :-

Last blog I have talked about new CDS SDK which is targeted for .net core platform. To implement this requirement we will make use of Azure function V3 Service Bus Queue trigger and CDS SDK to create records in CRM Dynamics.

Walkthrough :-

Step -1 Create new Service Bus Namespace and Queue in Azure portal. Copy Service Bus Namespace connectionstring which we will use in Azure function.

Navigate to Service Bus Namespace->Shared Access Policies->RootmanagerSharedAccessKey->Copy Primary Connection String

Blog11

Step -2 Next step is to create Azure function from Visual Studio/Visual Studio Code. Create new project->Select Azure function->Select Service bus queue trigger->Provide Queue Name and ConnectionString App settings Name give any name

Step -3 Now Add below nuget packages to your newly created project

Microsoft.PowerPlatform.Cds.Client
Newtonsoft.Json
Step -4 Copy and paste below code in your Azure function. Replace CRM URL, Client ID and Client Secret. Make sure you register new app in Azure AD and create application user in CRM. Check my previous blog to register app and create application User.
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
using Microsoft.Azure.ServiceBus;
using Microsoft.PowerPlatform.Cds.Client;
using Microsoft.Xrm.Sdk;
using Microsoft.Azure.ServiceBus.InteropExtensions;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Collections.Generic;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;
using System.Linq;
using Microsoft.Xrm.Sdk.Query;
using System.Text;

namespace IconicDynamicsConnector
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static void Run([ServiceBusTrigger("cfsdynamics365sbqueue", Connection = "SBConnectionString")] Message myQueueItem, ILogger log)
        {
             log.LogInformation($"C# ServiceBus queue trigger function processed message: {myQueueItem.Body}");
            //string messageBody = myQueueItem.GetBody();
            var bytesAsString = Encoding.UTF8.GetString(myQueueItem.Body);
            JObject messageObject = JsonConvert.DeserializeObject(bytesAsString);
            System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
            var cdsClient = new CdsServiceClient("AuthType = ClientSecret; ClientId=Replace with your client ID; Url = https://CRMORG.crm.dynamics.com; ClientSecret=Replace;");
            if (cdsClient.IsReady)
            {
                Console.WriteLine("Hello, connected to CDS using .NET Core");
                //cdsClient.CallerId = Guid.Parse("bc1809ae-552f-48d0-8f2d-75bf4f6f927b");
                Entity newIOTAlert = new Entity("msdyn_iotalert");
                newIOTAlert["msdyn_description"] = messageObject["Description"].ToString();
                string alertType = messageObject["AlertType"].ToString();
                if (!string.IsNullOrEmpty(alertType))
                {
                    Dictionary<string, string> dcAlertType = Function1.FetchOptionSetList(cdsClient, "msdyn_iotalert", "msdyn_alerttype");
                    KeyValuePair<string, string> alertTypeVal = dcAlertType.Where(x => x.Key.ToUpper() == alertType.ToString().ToUpper()).FirstOrDefault();
                    if (alertTypeVal.Value != null)
                    {
                        newIOTAlert["msdyn_alerttype"] = new OptionSetValue(Convert.ToInt32(alertTypeVal.Value));
                    }
                }
                if (!string.IsNullOrEmpty(messageObject["AlertTime"].ToString()))
                {
                    DateTime dt;
                    if (DateTime.TryParse(messageObject["AlertTime"].ToString(), out dt))
                    {
                        newIOTAlert["msdyn_alerttime"] = dt;//DateTime.Parse(messageObject["AlertTime"].ToString());
                    }
                }
                string device = messageObject["Device"].ToString();
                if (!string.IsNullOrEmpty(device))
                {
                    Guid customerDeviceId = Function1.GetGuidByNameAttrib(cdsClient, "msdyn_customerasset", "msdyn_name", device, null, "msdyn_customerassetid", true);
                    if (customerDeviceId != Guid.Empty)
                    {
                        newIOTAlert["msdyn_CustomerAsset"] = new EntityReference("msdyn_customerasset", customerDeviceId);
                    }
                }
                Guid alertId = cdsClient.Create(newIOTAlert);

                /*
                 {
                    "Description":"Check Azure Function SB Trigger",
                    "AlertType":"Anomaly",
                    "AlertTime":"07/28/2020 09:00 PM",
                    "Device":"My Customer Asset"
                }
                 */
            }
        }

        public static Dictionary<String, String> FetchOptionSetList(IOrganizationService service, String entityName, String fieldName)
        {
            Dictionary<String, String> dcOptionDic = new Dictionary<String, String>();

            try
            {
                if (String.Equals(entityName, "GlobalOptionSet", StringComparison.OrdinalIgnoreCase))
                {
                    #region "--- Global OptionSet ---"
                    RetrieveOptionSetRequest retrieveOptionSetRequest = new RetrieveOptionSetRequest
                    {
                        Name = fieldName
                    };

                    // Execute the request.
                    RetrieveOptionSetResponse retrieveOptionSetResponse = (RetrieveOptionSetResponse)service.Execute(retrieveOptionSetRequest);

                    // Access the retrieved OptionSetMetadata.
                    OptionSetMetadata retrievedOptionSetMetadata = (OptionSetMetadata)retrieveOptionSetResponse.OptionSetMetadata;

                    // Get the current options list for the retrieved attribute.
                    OptionMetadata[] optionList = retrievedOptionSetMetadata.Options.ToArray();

                    for (int optionCount = 0; optionCount < optionList.Length; optionCount++)
                    {
                        dcOptionDic.Add(optionList[optionCount].Label.UserLocalizedLabel.Label, optionList[optionCount].Value.ToString());
                    }
                    return dcOptionDic;
                    #endregion
                }
                else
                {
                    #region "--- Entity OptionSet ---"
                    RetrieveAttributeRequest attributeRequest = new RetrieveAttributeRequest
                    {
                        EntityLogicalName = entityName,
                        LogicalName = fieldName,
                        RetrieveAsIfPublished = true
                    };
                    // Execute the request
                    RetrieveAttributeResponse attributeResponse = (RetrieveAttributeResponse)service.Execute(attributeRequest);
                    OptionMetadata[] optionList = (((Microsoft.Xrm.Sdk.Metadata.EnumAttributeMetadata)(attributeResponse.AttributeMetadata)).OptionSet.Options).ToArray();
                    for (int optionCount = 0; optionCount < optionList.Length; optionCount++)                     {                         dcOptionDic.Add(optionList[optionCount].Label.UserLocalizedLabel.Label, optionList[optionCount].Value.ToString());                     }                     return dcOptionDic;                     #endregion                 }             }             catch (Exception ex)             {                 throw ex;             }         }         public static Guid GetGuidByNameAttrib(IOrganizationService service, String entityName, String srcAttrib, String srcValue, String srcValType, String targetAttrib, bool checkForActive)         {             try             {                 if (service != null)                 {                     QueryExpression query = new QueryExpression(entityName);                     query.ColumnSet = new ColumnSet(targetAttrib);                     query.NoLock = true;                     if (srcValType == null || srcValType == String.Empty)                     {                         query.Criteria.AddCondition(srcAttrib, ConditionOperator.Equal, srcValue);                     }                     else if (srcValType == "Guid")                     {                         query.Criteria.AddCondition(srcAttrib, ConditionOperator.Equal, Guid.Parse(srcValue));                     }                     if (checkForActive)                     {                         query.Criteria.AddCondition("statecode", ConditionOperator.Equal, 0);                     }                     EntityCollection entityColl = null;                     entityColl = service.RetrieveMultiple(query);                     if (entityColl != null)                     {                         // Execute the query and return the result                          if (entityColl.Entities.Count > 0)
                        {
                            if (entityColl.Entities[0].Contains(targetAttrib))
                            {
                                return (Guid)entityColl.Entities[0].Attributes[targetAttrib];
                            }
                        }

                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return Guid.Empty;
        }
    }
}

First I am parsing the queue message and storing it in Jobject variable then I used CDSClient to connect to CRM Dynamics. Next I am checking if connection is ready then Creating new entity object of Iot Alert entity and mapping the fields.

We will pass below JSON data to Service Bus Queue, you can use Service bus explorer or you can directly sent message from azure portal. Make sure you selecte content type as Json format.

{
                    “Description”:”Check Azure Function SB Trigger”,
                    “AlertType”:”Anomaly”,
                    “AlertTime”:”07/28/2020 09:00 PM”,
                    “Device”:”My Customer Asset”
}
AlertType is optionset field so to pass this to CRM we need to get Integer value based on Text for which I used first generic method FetchOptionSetList.
Device is lookup field so we need Guid to pass to CRM for which I used second method

GetGuidByNameAttrib
Setp -5 Next step is to add Service Bus connection String in local.settings.json file
“SBConnectionString”: “Endpoint=sb://cfsdynamics365sb.servicebus.window****”
Build your Azure function project, debug your code to check if everything working and then deploy your Azure function to Azure portal
Step -6 Once deployed and published, use service bus explorer tool to send json data to queue which will trigger your Azure function. Verify if Azure function triggered and succeeded. Check if Iot Alert record created in CRM.
Key Points :-
  • Azure Function V3 only supports .net core framework so legacy CRM SDK will not work
  • CDS Client SDK is in still preview and not ready for production use
  • You can always modify this base code to create any type of entity record in CRM Dynamics
  • It’s not  a good practice to store sensitive information in App settings so make sure you store this in Azure key value and use that in your Azure function code
  • Make sure you have implemented error logging and retry policy
  • Lookup field would required Schema Name to be passed instead of Name same as web api

Hope this helps!

One thought on “Integrate Dynamics CRM to Service Bus queue using Azure function

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s