Azure · Azure Function

{Azure Function} Integrate External System with Dynamics CRM using Webhook

What is a WebHook?

The concept of a WebHook is simple. A WebHook is an HTTP callback: an HTTP POST that occurs when something happens; a simple event-notification via HTTP POST.

A web application implementing WebHooks will POST a message to a URL when certain things happen. When a web application enables users to register their own URLs, the users can then extend, customize, and integrate that application with their own custom extensions or even with other applications around the web.

For the user, WebHooks are a way to receive valuable information when it happens, rather than continually polling for that data and receiving nothing valuable most of the time. WebHooks have enormous potential and are limited only by your imagination! (No, it can’t wash the dishes. Yet.)

Azure function is serverless computing service provided by microsoft.

Business Use Case –

We often come across the requirement where we need to integrate CRM with external system. For example you want to integrate Real Estate website with CRM to get the Leads into your system.

In today’s blog we are going to implement webhook using Azure function which will be used as posting URL to receive Leads from external party such as Real Estate vendors.

Whenever somebody visit your page on Real Estate website and if they are interested, they submit their details on website and same details will be send to our webhook and then Lead will be created in our CRM.

I will walk you through steps to achieve this business use case.

Step 1:

Open Visual Studio-> Click on Create Project-> Search for Azure function in Template-> Create new project

AF01

Step 2:

This is one of the important step where you will have to Azure function version. As of now CRM does not have any assembly which support Dot net core which is V3. We will select V1 .net framework and HTTP trigger. And click on Create.

AF02

Step 3:

We are going to use Odata web api to interact with CRM. Go to nuget manager  in visual studio and install ADAL library.

Copy and Paste below code. We need to register application in Azure AD. Copy Application ID, Tenant ID and client secret from registered application.

I have created placeholder in my sample code. you will have to replace with your details.

//FOLLOW THE STEPS HERE TO REGISTER AN APPLICATION IN AZURE AD AND CREATE AN APPLICATION USER IN CRM
https://msdn.microsoft.com/en-us/library/mt790171.aspx


using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace ExternalPartyIntegrationFA
{
    public static class HttpClientExtensions
    {
        public static Task SendAsJsonAsync(this HttpClient client, HttpMethod method, string requestUri, T value)
        {
            var content = value.GetType().Name.Equals("JObject") ?
                value.ToString() :
                JsonConvert.SerializeObject(value, new JsonSerializerSettings() { DefaultValueHandling = DefaultValueHandling.Ignore });

            HttpRequestMessage request = new HttpRequestMessage(method, requestUri) { Content = new StringContent(content) };
            request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");

            return client.SendAsync(request);
        }
    }
    public static class ExternalPartyIntegrationD365FA
    {
        //FOLLOW THE STEPS HERE TO REGISTER AN APPLICATION IN AZURE AD
        //AND CREATE AN APPLICATION USER IN CRM
        //https://msdn.microsoft.com/en-us/library/mt790171.aspx

        //This was registered in Azure AD as a WEB APPLICATION AND/OR WEB API

        //Azure Application / Client ID
        private const string ClientId = "00000000-0000-0000-0000-000000000000";
        //Azure Application Client Key / Secret
        private const string ClientSecret = "SECRET VALUE FROM AZURE";

        //Resource / CRM Url
        private const string Resource = "https://test.crm.dynamics.com";

        //Guid is your Azure Active Directory Tenant Id
        private const string Authority = "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000003";

        private static AuthenticationResult _authResult;

        [FunctionName("ExternalPartyIntegrationD365FA")]
        public static async Task Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]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;
            JObject requestBodyObject = null;
            string requestBodyJson = string.Empty;
            try
            {
                log.Info("Before Request Body Parse");
                requestBodyObject = JsonConvert.DeserializeObject(await req.Content.ReadAsStringAsync());
                log.Info("Request Body:" + requestBodyObject);
                //Convert Request body to JSON string 
                if (requestBodyObject != null)
                {
                    requestBodyJson = JsonConvert.SerializeObject(requestBodyObject, Formatting.Indented);
                    log.Info("Request Body in JSON:" + requestBodyJson);

                }

                string accessToken = GetAccessToken();
                if (!string.IsNullOrEmpty(accessToken))
                {
                    string leadId = CreateLead(accessToken, requestBodyObject);
                    return req.CreateResponse(HttpStatusCode.OK, "Lead Submitted : LEAD ID - " + leadId);
                }
                else
                {
                    return req.CreateResponse(HttpStatusCode.BadRequest, "Something went wrong");
                }
            }
            catch (Exception ex)
            {
                return req.CreateResponse(HttpStatusCode.BadRequest, "Error: " + ex.Message);
            }
        }

        public static string GetAccessToken()
        {
            string accessToken = string.Empty;

            AuthenticationContext authContext =
                new AuthenticationContext(Authority);

            //No prompt for credentials
            ClientCredential credentials = new ClientCredential(ClientId, ClientSecret);
            _authResult = authContext.AcquireToken(Resource, credentials);

            return _authResult.AccessToken;
        }

        public static string CreateLead(string accesToken, JObject requestBody)
        {
            string leadId = string.Empty;
            using (HttpClient httpClient = new HttpClient())
            {
                httpClient.BaseAddress = new Uri(Resource);
                httpClient.Timeout = new TimeSpan(0, 2, 0);
                httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
                httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");
                httpClient.DefaultRequestHeaders.Accept.Add(
                    new MediaTypeWithQualityHeaderValue("application/json"));
                httpClient.DefaultRequestHeaders.Authorization =
                    new AuthenticationHeaderValue("Bearer", accesToken);

                //Create
                JObject newLead = new JObject();
                newLead.Add("firstname", JToken.FromObject(requestBody["FirstName"].ToString()));
                newLead.Add("emailaddress1", JToken.FromObject(requestBody["Email"].ToString()));
                newLead.Add("lastname", JToken.FromObject(requestBody["LastName"].ToString()));
                newLead.Add("mobilephone", JToken.FromObject(requestBody["Phone"].ToString()));

                HttpResponseMessage createResponse =
                    httpClient.SendAsJsonAsync(HttpMethod.Post, "api/data/v8.2/leads", newLead).Result;

                Guid leadGuid = new Guid();
                if (createResponse.IsSuccessStatusCode)
                {
                    string leadUri = createResponse.Headers.GetValues("OData-EntityId").FirstOrDefault();
                    if (leadUri != null)
                    {
                        leadGuid = Guid.Parse(leadUri.Split('(', ')')[1]);
                        leadId = leadGuid.ToString();
                    }

                }
                else
                    throw new Exception("Lead Create Failed");
            }

            return leadId;
        }
    }
}

GetAccessToken method is used to get the access token from Azure AD which will be later used to invoke ODATA web api as bearer token. To get access token we need clientid, client secret, authority and resource.

CreateLead method is used to create Lead in CRM. Currently Lead data will come in request body as JSON format. We can also change out code to accespt the data from query string as well.

Below Code is used to get the content from request body.

requestBodyObject = JsonConvert.DeserializeObject(await req.Content.ReadAsStringAsync());

Step 4:

Once build is completed. Keep debugger at starting point and hit F5. We will get local Azure function URL generated which can be used from Postman to trigger azure function locally.

AF03

Step 5:

Copy Aure function generated URL and make a new request in Postman. Pass Leads data in request body. For demo purpose I have put only lead basic fields which are FirstName, LastName, Email & Phone.

AF04

 

Once execution completed, New Lead will be created in CRM with these details.

AF05

 

Step 6:

If everything is working as expected we can publish azure function to Azure portal. To publish Azure function from visual studio, Right click on project and then click on publish.

Create new profile and provide Name, Resource Group, Location and Azure storage account then click on Create. Once done new Azure function should be available from Azure Portal.

AF06

Once published you can share Azure function URL to external verndor and they should be able to post Lead data to webhook.

Important Points –

  1. Configurations like Application Id, Client Secret, CRM URL and Azure Tenant ID can be stored in Azure key vault and use it in Azure function instead of hardcoding in code itself
  2. Since this is a HTTP trigger and if somebody get Azure function URL, they can spam your system. It is always better to secure Azure function endpoint.
  3. By default Azure function has code parameter in request query string which can be used for authorization. It is always better to regenerate this key frequently.

Hope this helps!

One thought on “{Azure Function} Integrate External System with Dynamics CRM using Webhook

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