Audit History Using Azure Function

In this blog we will get to know how to fetch field’s Audit history, I have created this in azure function so we can trigger this by using Plugins, Custom Workflows, JavaScript and MS Flows.

To fetch field audit history, make sure you have enabled auditing in the system and entity settings.

Entity setting:

As I said, I have implemented this in Azure Function, so let’s start and follow the below steps.

Start your Visual Studio and select a new project, and search for an azure function, select it and click on Next.

In the next screen just provide your project name and click on Create.

On the next screen we have multiple options to trigger this Azure function, in my case I created an HTTP trigger, once we publish this function on the azure portal it will create one HTTP link, by using that link we can trigger this function.

Now you can use below code and you can add your business logic as per you requirements.

Microsoft.Xrm.Sdk
Microsoft.Crm.Sdk.Proxy
Microsoft.IdentityModel

First, add below assemblies in your project from CRM SDK

Then create three query parameters as below,

  1. entitylogicalname:  Entity logical name.
  2. RecordId: GUID of the record.
  3. fieldLogicalName: logical name which you want to fetch audit history.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using AzureFunction.CreateWebCalenderBookingSlot;
using Microsoft.Azure;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;


namespace GetAuditHistory
{
    public static class FieldAuditHistory
    {
        [FunctionName("FieldAuditHistory")]
        public static async Task<HttpResponseMessage> 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 entitylogicalname = req.GetQueryNameValuePairs()
                .FirstOrDefault(q => string.Compare(q.Key, "entitylogicalname", true) == 0)
                .Value;

            string RecordId = req.GetQueryNameValuePairs()
               .FirstOrDefault(q => string.Compare(q.Key, "RecordId", true) == 0)
               .Value;

            string fieldLogicalName = req.GetQueryNameValuePairs()
               .FirstOrDefault(q => string.Compare(q.Key, "fieldLogicalName", true) == 0)
               .Value;

            if (entitylogicalname != null && RecordId != null && fieldLogicalName != null)
            {
                 FetchAudit(entitylogicalname, RecordId, fieldLogicalName);
            }
            if (entitylogicalname == null)
            {
                // Get request body
                dynamic data = await req.Content.ReadAsAsync<object>();
                entitylogicalname = data?.entitylogicalname;

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

Now create Organization service by using the below function.

    private static IOrganizationService GetOrganizationService()
        {
            string UserName = CloudConfigurationManager.GetSetting("Prod_Username");
            string Password = CloudConfigurationManager.GetSetting("Prod_Password");
            string SoapOrgServiceUri = CloudConfigurationManager.GetSetting("ORG_URL_PROD");
            OrganizationServiceProxy proxy;
            ServicePointManager.ServerCertificateValidationCallback = delegate (object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; };
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;
            ClientCredentials credentials = new ClientCredentials();
            credentials.UserName.UserName = UserName;
            credentials.UserName.Password = Password;
            Uri serviceUri = new Uri(SoapOrgServiceUri);
            proxy = new OrganizationServiceProxy(serviceUri, null, credentials, null);
            proxy.EnableProxyTypes();          
          
            return proxy;
        }

In the next function, we are fetching audit history using RetrieveAttributeChangeHistory Request

  public static void FetchAudit(string entitylogicalname, string RecordId, string fieldLogicalName)
        {
            IOrganizationService Service = GetOrganizationService();
            var attributeChangeHistoryRequest = new RetrieveAttributeChangeHistoryRequest
            {
                Target = new EntityReference(
                                    entitylogicalname, new Guid(RecordId)),
                AttributeLogicalName = fieldLogicalName
            };

            var attributeChangeHistoryResponse = (RetrieveAttributeChangeHistoryResponse)Service.Execute(attributeChangeHistoryRequest);
            var details2 = attributeChangeHistoryResponse.AuditDetailCollection;
            foreach (var detail in details2.AuditDetails)
            {
                var detailType = detail.GetType();
                if (detailType == typeof(AttributeAuditDetail))
                {
                    // retrieve old & new value of each action of each audit change from AttributeAuditDetail
                    var attributeDetail = (AttributeAuditDetail)detail;
                    foreach (KeyValuePair<string, object> attribute in attributeDetail.NewValue.Attributes)
                    {
                        EntityReference newvalue = ((EntityReference)attributeDetail.NewValue.Attributes[attribute.Key]);

                        if (attributeDetail.OldValue.Contains(attribute.Key))
                        {

                            EntityReference oldValue = ((EntityReference)attributeDetail.OldValue.Attributes[attribute.Key]);
                            if (newvalue.Id.ToString() != oldValue.Id.ToString())
                            {
                                //Your Business logic
                            }
                        }
                    }
                }
            }
        }

As I said you can write your business logic as per your requirements, now just build and publish your azure function in azure portal,

You can test it at your local machine, just run the azure function and copy function URL and run it in your browser or call it using Postman

I am calling this HTTP request using postman, in this you need to pass three parameters and in the output you will get the New value and Old value. You can compare and based on that you can run your logic.

Happy Coding 🙂

Design a site like this with WordPress.com
Get started