Clone Record with Custom Button and Workflow Action in Dynamics 365 CRM
This blog will demonstrate how to build a solution in Microsoft Dynamics 365 CRM where a custom button triggers a workflow action that will clone a record and then open it. I’m writing this blog to be a one-stop shop to help others execute the same set of functionality. I found bits and pieces about this topic over several different websites, and I had to piece the information all together.
The following items will be used to make this happen:
- Dataverse
- CE Workflow Action
- JavaScript Web Resource
- Dynamics 365 Workflow Tools
- Ribbon Workbench
Note: There are now 5 different Dynamics 365 apps that serve various CRM needs. In some contexts, you may also see those products referred to as “Customer Engagement” or “CE” apps. Cloning records works the same no matter which of those apps you’re using.
How It Works
The user opens an existing Order and clicks the “Clone Order” custom button at the top of the form. This button is associated to a JavaScript function [Encore.OrderLibrary.cloneOrder], that when triggered, it will call a workflow action [enc_CloneOrder] that will in turn call a 3rd-party plugin [msdyncrmWorkflowTools.CloneRecord] that will clone the record and then open it.
Dynamics-365-Workflow-Tools
This amazing tool has been around for some time and is needed to clone the CRM record. You can download it from here: https://github.com/demianrasko/Dynamics-365-Workflow-Tools

Ensure you download the managed zip file. Navigate to make.powerapps.com, login, navigate to the desired environment, navigate to solutions, and then import it into your Power Platform system as you would any other solution. You will view it in the list of solutions:

This will be needed for our workflow action.
Workflow Action
I suggest creating a solution in your environment to hold all customizations related to the project at hand. Here I created a new solution in which to work:

Once the solution is created, open it up and create a new workflow action. You do this by selecting the “Switch to classic” option from the top navigation.

It will open the traditional solution window. Select “Processes” in the left navigation and then click “New.”

Ensure “Action” is selected. Provide an appropriate name for the workflow action and select the desired table.

Here I have created a workflow action that is tied to the Order table and have named it Clone Order:

You will need the Unique Name of the workflow action – enc_CloneOrder – for the JavaScript Web Resource, so copy it and paste it somewhere for later retrieval.
The JavaScript Web Resource that will be created in the next step will need the new Order GUID; this value is generated when a new Order is created in the Dataverse. Here we create an Output parameter named NewOrderId.

There are only two steps to make this work:

The first step is from the Workflow Tools solution above. When clicking Add Step, the Workflow Tools option will appear; select CloneRecord from the list. Next, click on the Set Properties button when it appears.
For the first value, select Record URL for the related table (Order). Identify any prefix you would like that will be prepended to the name column of the new record. In this case, I added “Cloned – ” to the prefix. If you need to ignore any columns; add the logical name of the column, separated by a semi-colon. In my case, I had to add ordernumber, or an error would occur, referencing a duplicate value.

Save and close. Select “Add Step” and then select “Assign Value.” After the new line appears, click “Set Properties.”

Enter the “Statement Label” and select the Output Parameter you created from “Name.”
To set the “Value,” change “Look for” to the name of your workflow action (Clone Order in this case), and then select “Cloned Guid.”

The final result will look something like this:

Save the workflow action and then activate it.
REMEMBER: This workflow action is called by the JavaScript function and will clone the record.
JavaScript Web Resource
I typically manage Web Resources with a Visual Studio web project for my clients. It is a nice way to keep them all in one location, and Visual Studio provides the IntelliSense and formatting when developing JavaScript.
Whether or not Visual Studio is used, create a new JavaScript file, add the below code, and save. We will add the rest of the code later.
if (typeof (Encore) == 'undefined') {
Encore = {
__namespace: true
}
}
if (typeof (Encore.OrderLibrary) == 'undefined') {
Encore.OrderLibrary = {}
}
Encore.OrderLibrary = (function () {
var cloneOrder = function (executionContext) {
} // cloneOrder
return {
cloneOrder: cloneOrder
}
} // Encore.OrderLibrary
)()

Navigate to your solution (classic in my case) and create a new Web Resource. From the left navigation, select Web Resources and then select New.
Enter a Name and Display Name; select Script for Type, select Language, and then upload the new JavaScript file you just created. The new Web Resource will look something like this:

Navigate to the desired form, open it up, click on Properties, and add the newly added Web Resource.

It will then show up in the form libraries.

Since we are not calling this function on load of the form or on edit of any of the columns, we do not need to add any event handlers; we will call the function from the button.

Save and close and publish all customizations. Next, navigate to your JavaScript code and add all the following.
if (typeof (Encore) == 'undefined') {
Encore = {
__namespace: true
}
}
if (typeof (Encore.OrderLibrary) == 'undefined') {
Encore.OrderLibrary = {}
}
Encore.OrderLibrary = (function () {
var cloneOrder = function (executionContext) {
console.info("Begin cloneOrder");
var formContext = null;
try {
formContext = executionContext.getFormContext(); // call from home or sub grid
console.log("Called from Home or Sub Grid");
}
catch (e) {
console.warn("Not being called from Home or Sub Grid");
try {
formContext = executionContext;
console.log("Called from Form");
}
catch (e) {
console.warn("Not being called from Form");
}
}
try {
if (formContext != null) {
var entity_id = formContext.data.entity.getId();
entity_id = StripGUID(entity_id);
console.log("entity_id: " + entity_id);
var globalContext = Xrm.Utility.getGlobalContext();
var serverURL = globalContext.getClientUrl();
console.log("serverURL: " + serverURL);
var query = "salesorders(" + entity_id + ")/Microsoft.Dynamics.CRM.enc_CloneOrder";
console.log("query: " + query);
var data;
var req = new XMLHttpRequest();
req.open("POST", serverURL + "/api/data/v9.1/" + query, true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (req.status >= 200 && req.status <= 300) {
try {
console.dirxml("this.response: " + this.response);
data = JSON.parse(this.response);
console.dirxml("data: " + data);
console.dirxml("data: " + data.toString());
var new_order_id = data.NewOrderId;
console.log("new_order_id: " + new_order_id);
// OPEN FORM
var entityFormOptions = {};
entityFormOptions["entityName"] = "salesorder";
entityFormOptions["entityId"] = new_order_id;
Xrm.Navigation.openForm(entityFormOptions);
}
catch (e) {
console.error("error in req.onreadystatechange: " + e);
}
} else {
var error = JSON.parse(this.response).error;
console.error("error: " + error);
}
}
};
req.send(window.JSON.stringify(data));
}
}
catch (e) {
console.error("ERROR - Encore.OrderLibrary.onLoad: " + e);
}
} // cloneOrder
var StripGUID = function (_guid) {
var rtn = "";
rtn = _guid.replace("{", "").replace("}", "");
return rtn;
} // StripGUID
return {
cloneOrder: cloneOrder
}
} // Encore.OrderLibrary
)()
Explanation of JavaScript Code
Remember, the clicking of the Clone Order button calls the cloneOrder JavaScript function. This function needs to call the enc_CloneOrder custom action that will in turn call the WorkflowTools CloneRecord plugin.
This statement builds the call to the custom action:
var query = "salesorders(" + entity_id + ")/Microsoft.Dynamics.CRM.enc_CloneOrder";
These lines build the parameters needed to open up the newly created record:
var new_order_id = data.NewOrderId;
var entityFormOptions = {};
entityFormOptions["entityName"] = "salesorder";
entityFormOptions["entityId"] = new_order_id;
And here is the call to open the newly created record:
Xrm.Navigation.openForm(entityFormOptions);
Finally, we will add the custom button to the Order form.
Custom Button
Use the following resource to download, install, and learn the basics of the Ribbon Workbench tool: https://www.develop1.net/public/rwb/ribbonworkbench.aspx
Download and install the Ribbon Workbench solution:

Create a new solution that will house only the ribbon customizations. Click Add Existing -> Table, find your table, and do not add any components.

While in make.powerapps.com, ensure you are in the right environment, click the gear settings icon at the top right and click Advanced Settings:

Click Settings and then Customizations and then click Ribbon Workbench 2016:

Search for your ribbon workbench solution, select it, and then click OK.

Once it loads, follow Workbench instructions to add a button.


Add a command:


Add a custom JavaScript action:

Ensure that you have selected/entered the correct JavaScript file and call the complete function name. For illustration purposes the function name would be something like Encore.OrderLibrary.cloneOrder, assuming you are using namespacing in your JavaScript.
Save and publish all changes.
See It In Action
So what do we have? We created a custom button, that when clicked, will trigger a JavaScript function. That function will call a workflow action that will call a 3rd-party plugin that will clone the record.
Navigate to the desired form and click the “Clone Order” button.

Showing the Developer Tools in your browser (F12) will demonstrate the work taking place behind the scenes. Below is a log representing the call to the workflow action:

After a few seconds, a new Order record will display that is ready for editing and saving:

As mentioned at the start of this blog, I wrote this to illustrate the ability to clone a Dynamics 365 CE record without a ton of effort. There are a few components involved that need to communicate to each other, but all in all, it is a fairly simple solution.
If you’d like to learn more about your Dynamics 365 CE solution, please get in touch with us.
41 pages of step-by-step instructions for 6 different key tasks in Dynamics 365 CRM apps. Includes interactions with Power Apps and Power Automate!