CRM Plugin to Service Bus

Recently I got a chance to work with Dynamics CRM 2016 on-premise and Azure Service bus.

Requirement is simple. When a record gets updated in CRM, a JSON message to be dropped in Azure Service bus.

I know there is an option to register Azure service bus queue as a serviceendpoint in Dynamics CRM 2016. And from version 9 onwards, you can register an Azure function as  web-hook and interact with Azure Service bus Queue.

Available methods will post the context to Azure Service bus. However, in my scenario I was trying to drop a custom message instead. I need to push data from multiple entities.

First, created a POC using console application which uses Azure service bus nuget package for CRM version 8.0.0.0. Used same logic to implement a plugin. It didn’t work. As it was an on-premise environment, I thought I will have more control over it. I copied all required assemblies to respective folders and registered my plug-in. I was expecting it to work. NO LUCK.

Later I used Nuget ILMerge task for Visual studio to merge all my dependent assemblies into my plug-in assembly and registered my plug-in. Assuming it might be due to missing dependent assemblies. NOLUCK. No error message as well.

Azure Service bus assembly comes up with IQueueClient interface which exposes a method called SendAsync. In my case, Plug-in executes all statements but message doesn’t appear in the Queue. Whereas my POC was working perfectly from server and client machines. This forced me to use more tools to understand the inner exception. However due to time constraints, I started to explore alternate ways.

After researching, found out that Azure Queue actually exposes a rest endpoint which we can be used to drop message. Here is the Microsoft link on the specification of the rest end point. Below is the code on how to use Azure rest API.

 internal void SendMessageViaRest(Dictionary<string, string&gt; ConfigKeys, string message)
        {
            //string asbUri = "https://<ServiceBus Name&gt;.servicebus.windows.net/<Queuename&gt;/messages";
            string asbUri = ConfigKeys[ConfigKeyConstants.VOCRestEndpoint];
            TimeSpan ts = new TimeSpan(0, 0, 90);
            //string sasToken = GetSASToken("sb://<servicebusname&gt;.servicebus.windows.net", "RootManageSharedAccessKey", "uTxRhAp9jw0iCBMpH0qRqOQWzpJHqfuUUfR27Vwd2T4=", ts);
            string sasToken = GetSASToken(ConfigKeys[ConfigKeyConstants.VOCAzureNameSpace], ConfigKeys[ConfigKeyConstants.VOCKeyName], ConfigKeys[ConfigKeyConstants.VOCKeyValue], ts);




            HttpClient client = new HttpClient();
            client.DefaultRequestHeaders.Add("Authorization", sasToken);
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, asbUri)
            {
                Content = new StringContent(message, Encoding.UTF8, "application/json")
            };
            HttpResponseMessage response = client.SendAsync(request).Result;

            //throw new InvalidPluginExecutionException(response.StatusCode.ToString());
        }

In the above step, we will get SAS Token using queue service bus namespace, Service bus Shared Access Key (Keyname and Primary key value) Below are the values used to get SaSToken. Once we have SAS token, we will then use endpoint url to post our message to Queue.

“sb://<servicebusname>.servicebus.windows.net”, <ShareaccesspolicyName>, “PrimaryKey”, ts);

internal string GetExpiry(TimeSpan ttl)
        {
            TimeSpan expirySinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1) + ttl;
            return Convert.ToString((int)expirySinceEpoch.TotalSeconds);
        }
internal string GetSASToken(string resourceUri, string keyName, string key, TimeSpan ttl)
        {

            var expiry = GetExpiry(ttl);
            
            string stringToSign = Uri.EscapeDataString(resourceUri).ToLowerInvariant() + "\n" + expiry;

            HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));

            var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));

            var sasToken = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}",

            Uri.EscapeDataString(resourceUri).ToLowerInvariant(), Uri.EscapeDataString(signature), expiry, keyName);

            return sasToken;
        }
private void DropMessageToQueue(ParticipationDTO Participant, Dictionary<string, string&gt; ConfigKeys, ITracingService traceServcie)
        {



            string json = JsonConvert.SerializeObject(Participant);

            //throw new InvalidPluginExecutionException(json);

            string messageBody = json;
            traceServcie.Trace(json);

            new
            QueueHelper().PushMessageToQueue(ConfigKeys, json);
        }

Hope this helps. I struggled to find a sample on this so thought of presenting it here.