In this post, we already learned how to make a plugin library for getting resx web-resource. Now we will build a simple automation program (a little bit DevOps) to read an excel file and create/update resx web-resource with a single click. Also, we will use Power Automate Desktop which enables us to a more complex scenario in the future.
Create Exe File
Before we set up our Desktop Flow, we need to create first our c# code (What? C# code? Yes. We still need an exe program to helps connect to our CRM instance). Here is the code:
using Entities;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Tooling.Connector;
using Niam.XRM.Framework;
using Niam.XRM.Framework.Data;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Resources;
using System.Threading;
namespace UpdateResxWebResource
{
public class ExcelData
{
public string Name { get; set; }
public string Value { get; set; }
}
class Program
{
static void Main(string[] args)
{
var header = string.Join(" ", args).Split(new[] { "||" },
StringSplitOptions.RemoveEmptyEntries);
var data = header[1].Split(new[] { Environment.NewLine },
StringSplitOptions.RemoveEmptyEntries).ToArray();
var rows = GetExcelData(data).Where(e => !string.IsNullOrEmpty(e.Name))
.Where(e => !string.IsNullOrEmpty(e.Name)).ToArray();
var fileName = header[0];
var conSetting = ConfigurationManager.AppSettings["connectionString"];
var service = new CrmServiceClient(conSetting);
var webResource = GetWebResource(service, fileName) ?? CreateWebResource(fileName);
SetWebResourceContent(webResource, rows);
if (webResource.Id == Guid.Empty)
{
webResource.Id = service.Create(webResource);
Console.WriteLine($"{fileName} is created with id {webResource.Id }..");
}
else
{
service.Update(webResource);
Console.WriteLine($"{fileName} is updated with id {webResource.Id}..");
}
service.Execute(new PublishXmlRequest
{
ParameterXml =
$"<importexportxml><webresources><webresource>{webResource.Id}</webresource>" +
"</webresources></importexportxml>"
});
Thread.Sleep(5000);
}
private static void SetWebResourceContent(WebResource webResource, ExcelData[] rows)
{
var temp = ConfigurationManager.AppSettings["fileFolder"] + "temp.resx";
using (var resx = new ResXResourceWriter(temp))
{
foreach (var row in rows)
{
Console.WriteLine($"Add Resx with Name: {row.Name} with Value: {row.Value}");
resx.AddResource(row.Name, row.Value);
}
}
var text = File.ReadAllBytes(temp).Base64Encode();
webResource.Set(e => e.Content, text);
}
private static WebResource CreateWebResource(string fileName)
{
return new WebResource().Set(e => e.Name, fileName)
.Set(e => e.DisplayName, fileName)
.Set(e => e.WebResourceType, WebResource.Options.WebResourceType.StringRESX);
}
private static WebResource GetWebResource(IOrganizationService service, string fileName)
{
var query = new QueryExpression(WebResource.EntityLogicalName)
{
ColumnSet = new ColumnSet<WebResource>(e => e.Content),
TopCount = 1,
NoLock = true
};
query.Criteria.AddCondition<WebResource>(e => e.Name, ConditionOperator.Equal, fileName);
var result = service.RetrieveMultiple(query);
return result.Entities.Any() ? result.Entities[0].ToEntity<WebResource>() : null;
}
private static IEnumerable<ExcelData> GetExcelData(string[] data)
{
foreach (var text in data)
{
var info = text.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
.Select(e => e.Trim()).ToArray();
yield return new ExcelData
{
Name = info.Length > 1 ? info[0] : "",
Value = info.Length > 2 ? info[1] : ""
};
}
}
}
public static class StringExtensions
{
public static string Base64Encode(this byte[] bytesData)
{
var base64Data = Convert.ToBase64String(bytesData);
return base64Data;
}
}
}
In that code, we will need 2 appSettings:
- connectionString for connection string for our Dynamics instance
- fileFolder for store result of resx before updated to our Dynamics instance
So the logic on the exe is pretty simple:
- Power Automate Desktop will array strings of the data that we need it.
- We compile the data and transform it into the data model that we already prepare (fileName and array of ExcelData).
- We connect to our Dynamics Instance.
- Get the resx web-resource base on the name.
- Create/Update the web-resource.
- Publish
Create Excel Format
Here is the excel format. Pretty simple. Make a table with 2 columns: Name and Value.

Set-up Power Automate Desktop
You must download and install Power Automate Desktop on this link. Once you already install it, you can get trial access for it (around 90 days). Create a new flow for Power Automate Desktop. Name it whatever suitable for you. And these are my steps:

First, you must create 2 input variables. These inputs will be for excelFilePath(location for your excel file) and fileName(file name for your resx web-resource. Remember must follow the format {name}.{languageCode}.resx). We also need to add pre-defined value for these inputs for now.

Then we start creating the steps:
Create step Launch Excel > Launch Excel as “and open the following document” > Document Path as “%excelFilePath%” > Save.

Create step Read From Excel worksheet > Excel instance as “%ExcelInstance%” > Retrieve as “Values from a Range Cells” > Start column as “A” > Start row as “1” > End column as “B” > End row as “1000” > in Advanced set First line of range contains column names as true > Save.

Create step Close Excel > Excel Instance as “%ExcelInstance%” > Before closing Excel as “Do not save document” > Save.

Create step Run Application > Application Path to your exe file > Command line arguments as “%fileName%||%ExcelData%” > Save.

Demonstration
Following those steps above, every time you run the flow the automation will be running for creating/updating the web resource. Here is the demonstration for the result:

Here is my testing result (calling Xrm.Utility.getResourceString on the screen):

Tips
If you working resx file in javascript using function Xrm.Utility.getResourceString, you need to clear your cookies and cached because CRM got a mechanism to cache them. So every time you run the automation process to create/update the resx web-resource you must do this step.

Also if you calling from javascript, you must remember to add your resx as dependency per this article.

2 thoughts on “Dynamics CRM 365: Build Automation for Create/Update RESX WebResource + Power Automate Desktop”