At times we come across a scenario where we may have changed field values for multiple records and need an automated solution to revert it back.

Well we can use the below solution to fetch the Audit History Details for Audit Enabled entities and use the data to revert back our unwanted changes. The solution will pull the audit details. These details can be used to update the field back as per our requirement.

Here goes the code using Console Application:

Step 1: Create A Console Application

Step 2: Use below code to connect to CRM

public IOrganizationService ConnectionOrgService()
{
IOrganizationService OrganizationService;
try
{
#region Use Discovery Service to connect to return the list of Org Names
// GetOrganizations(ConfigurationManager.AppSettings["discoveryURL"], username, password, domain);
#endregion

#region Approach 1 - Connect CRM
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ClientCredentials cred = new ClientCredentials();
cred.UserName.UserName = userName;
cred.UserName.Password = password;
OrganizationServiceProxy serviceProxy = new OrganizationServiceProxy(new Uri(organizationUrl), null, cred, null);
serviceProxy.EnableProxyTypes();
OrganizationService = (IOrganizationService)serviceProxy;
#endregion Approach 1 - Connect CRM

#region Approach 2 - Connect CRM
//ClientCredentials credentials = new ClientCredentials();

//credentials.Windows.ClientCredential = new System.Net.NetworkCredential(username, password, domain);
//using (var orgServiceProxy = new OrganizationServiceProxy(new Uri(organizationUrl), null, credentials, null))
//{
// orgServiceProxy.Authenticate();
// OrganizationService = (IOrganizationService)orgServiceProxy;
//}
#endregion Approach 2 - Connect CRM

#region Approach 3 - Connect CRM IFD
//Uri organizationUriIFD = new Uri(organizationUrl);

//ClientCredentials credentials = new ClientCredentials();
//credentials.UserName.UserName = userName;
//credentials.UserName.Password = password;

//IServiceConfiguration<IOrganizationService> config = ServiceConfigurationFactory.CreateConfiguration<IOrganizationService>(organizationUriIFD);

//using (Microsoft.Xrm.Sdk.Client.OrganizationServiceProxy _serviceProxy = new OrganizationServiceProxy(config, credentials))
//{
// // This statement is required to enable early-bound type support.
// _serviceProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior());

// IOrganizationService _service = (IOrganizationService)_serviceProxy;
// OrganizationService = _service;

// WhoAmIResponse response = (WhoAmIResponse)_service.Execute(new WhoAmIRequest());
// Console.WriteLine(response.UserId.ToString());

// //Console.ReadLine();
//}
#endregion Approach 3 - Connect CRM IFD

}
catch (Exception ex)
{
string msg = ex.Message;
return null;
}

return OrganizationService;

}

//================================================================================================================

internal static void RetrieveAuditHistory(IOrganizationService _service, Guid RecordId, string entityname)
{
#region Retrieve the Record Change History
Console.WriteLine("Retrieving the "+ entityname + " change history.\n");
// Retrieve the audit history for the account and display it.
RetrieveRecordChangeHistoryRequest changeRequest = new RetrieveRecordChangeHistoryRequest();
changeRequest.Target = new EntityReference(entityname, RecordId);

RetrieveRecordChangeHistoryResponse changeResponse =
(RetrieveRecordChangeHistoryResponse)_service.Execute(changeRequest);

AuditDetailCollection details = changeResponse.AuditDetailCollection;

foreach (AttributeAuditDetail detail in details.AuditDetails)
{
// Display some of the detail information in each audit record.
DisplayAuditDetails(detail);
}
#endregion Retrieve the Record Change History

#region Retrieve the Attribute Change History

// Retrieve the attribute change history.
Console.WriteLine("Retrieving the attribute change history for a particular field.");

var attributeChangeHistoryRequest = new RetrieveAttributeChangeHistoryRequest
{
Target = new EntityReference(entityname, RecordId),
AttributeLogicalName = "regardingobjectid"
};

var attributeChangeHistoryResponse =
(RetrieveAttributeChangeHistoryResponse)_service.Execute(attributeChangeHistoryRequest);

// Display the attribute change history.
details = attributeChangeHistoryResponse.AuditDetailCollection;

foreach (var detail in details.AuditDetails)
{
DisplayAuditDetails(detail);
}

// Save an Audit record ID for later use.
Guid auditSampleId = details.AuditDetails.First().AuditRecord.Id;
#endregion Retrieve the Attribute Change History

#region Retrieve the Audit Details
Console.WriteLine("Retrieving audit details for an audit record.");

// Retrieve the audit details and display them.
var auditDetailsRequest = new RetrieveAuditDetailsRequest
{
AuditId = auditSampleId
};

var auditDetailsResponse =
(RetrieveAuditDetailsResponse)_service.Execute(auditDetailsRequest);
DisplayAuditDetails(auditDetailsResponse.AuditDetail);

#endregion Retrieve the Audit Details
}

//================================================================================================================

private static void DisplayAuditDetails(AuditDetail detail)
{
var detailType = detail.GetType();
if (detailType == typeof(AttributeAuditDetail))
{
var attributeDetail = (AttributeAuditDetail)detail;

// Display the old and new attribute values.
if (attributeDetail.NewValue == null || attributeDetail.NewValue.Attributes.Count == 0)
return;

foreach (KeyValuePair<String, object> attribute in attributeDetail.NewValue.Attributes)
{
String oldValue = "(no value)", newValue = "(no value)";

//TODO Display the lookup values of those attributes that do not contain strings.

if (attributeDetail.OldValue.Contains(attribute.Key))
{
#region LookUp Values
if (attributeDetail.OldValue[attribute.Key].ToString().Contains("Microsoft.Xrm.Sdk.EntityReference"))
{
oldValue = ((EntityReference)attributeDetail.OldValue[attribute.Key]).Id.ToString();
string oldValueName = ((EntityReference)attributeDetail.OldValue[attribute.Key]).Name;


newValue = ((EntityReference)attributeDetail.NewValue[attribute.Key]).Id.ToString();
string newValueName = ((EntityReference)attributeDetail.NewValue[attribute.Key]).Name;

Console.WriteLine("Attribute: {0}, old value: {1}, new value: {2}",
attribute.Key, oldValueName, newValueName);
}
#endregion End LookUp Values

else
{
oldValue = attributeDetail.OldValue[attribute.Key].ToString();

newValue = attributeDetail.NewValue[attribute.Key].ToString();
}

//For Optionsets use below
//oldValue = ((OptionSetValue)attributeDetail.OldValue[attribute.Key]).Value.ToString();
//newValue = ((OptionSetValue)attributeDetail.NewValue[attribute.Key]).Value.ToString();
}

Console.WriteLine("Attribute: {0}, old value: {1}, new value: {2}",
attribute.Key, oldValue, newValue);
}

foreach (KeyValuePair<String, object> attribute in attributeDetail.OldValue.Attributes)
{
if (!attributeDetail.NewValue.Contains(attribute.Key))
{
String newValue = "(no value)";

//TODO Display the lookup values of those attributes that do not contain strings.
String oldValue = attributeDetail.OldValue[attribute.Key].ToString();

Console.WriteLine("Attribute: {0}, old value: {1}, new value: {2}",
attribute.Key, oldValue, newValue);
}
}
}
Console.WriteLine();
}

Reference Links For More Details:

https://docs.microsoft.com/en-us/previous-versions/dynamicscrm-2016/developers-guide/gg309735(v=crm.8)

Related Discussions:

https://community.dynamics.com/crm/f/microsoft-dynamics-crm-forum/307721/retrieving-audit-data-via-console-app/893448

https://cloudblogs.microsoft.com/dynamics365/no-audience/2011/05/23/recover-your-deleted-crm-data-and-recreate-them-using-crm-api/

Similar Topics:

AUDIT User Access

https://docs.microsoft.com/en-us/previous-versions/dynamicscrm-2016/developers-guide/hh547448(v=crm.8)?redirectedfrom=MSDN

Recover your deleted CRM data and recreate them using CRM API

https://cloudblogs.microsoft.com/dynamics365/no-audience/2011/05/23/recover-your-deleted-crm-data-and-recreate-them-using-crm-api/