Learn how to configure Azure Key Vault for Power Automate using a service account. In cloud flow we read secret from Key Vault
Row Update After SharePoint File Upload
A common requirement is to update the record row in Dataverse table after a file has been uploaded to SharePoint. This small post documents the steps and expressions for it. For demonstration, I m using “Account” table and have created “URL” a custom column in it. After file uploads, I will update the row and safe URL in this field.
Summary
This post do not cover the steps required to configure SharePoint for document storage with Model-Driven App. Follow this link for integration details). Cloud flow triggers when a file is uploaded from Model Driven App and created in the SharePoint document library.
- Create a cloud flow and add “When a file is created (properties only)” trigger. Configure the following properties:
- Site Address
- Library Name
2. To upload Dataverse row, we need GUID. Unfortunately it is not available as plain text, though we can drive it from trigger’s Folder path property.
Its value will be similar to account/test_67B9406FCBB7EB118236000D3A6A4A8A/
we need to extract GUID from it. Expression which I have used uses three functions and here is complete expression:
replace(last(split(triggerOutputs()?[‘body/{Path}’],’_’)),’/’,”)
Need explanation ?
split(triggerOutputs()?[‘body/{Path}’],’_’) will divide account/test_67B9406FCBB7EB118236000D3A6A4A8A/ into two from ‘_’. Wrapping it under last function will return 67B9406FCBB7EB118236000D3A6A4A8A/ only. I have used replace function to remove last ‘/’.
3. In step 2, we have got the GUID but it is not in format that we can used to fetch or update row. Instead of
67B9406FCBB7EB118236000D3A6A4A8A
we need it in 67B9406F-CBB7-EB11-8236-000D3A6A4A8A format.
Here is expression that will do this:
concat(substring(variables(‘StringGUID’),0,8),’-‘,substring(variables(‘StringGUID’),8,4),’-‘,substring(variables(‘StringGUID’),12,4),’-‘,substring(variables(‘StringGUID’),16,4),’-‘,substring(variables(‘StringGUID’),20,12),”)
In this post I have explain this expression.
4. Last steps is to use FormatedGUID and update record. For demo I have created a custom field “URL” and populating document URL in it.
Lets test the Flow after turning it on.
Let’s Connect
![]() |
![]() |
![]() |
![]() |
Convert SharePoint Uploaded Word File to Pdf
Did you ever receive a requirement that a Word file uploaded through a model-driven app in SharePoint should be converted and saved as Pdf? I had this requirement in multiple projects. Recently I have used cloud flow to achieve this. It was a model-driven app that has integration with SharePoint for document storage. In this post, I m documenting steps.
Summary
The app has SharePoint integration enabled for the “Account” table (this post does not cover how to integrate SharePoint with Model Driven Apps, follow this link for integration details). Flow triggers when a file is uploaded from Model Driven App and created in SharePoint document library. It then uses that uploaded Word file and creates Pdf copy, and finally delete the Word version. For explaining steps, I will use the following document URL, this file was uploaded by my model-driven app:
Steps
- Create a cloud flow and add “When a file is created (properties only)” trigger. Configure the following properties:
- Site Address
- Library Name
We don’t want to trigger this flow on any file upload; rather, we want to trigger it only when a word file is created. Configure “Trigger Conditions” for word file extensions “.doc” and “docx”.
Here are expressions:
@contains(triggerBody()?[‘{FilenameWithExtension}’],’.docx’)
@contains(triggerBody()?[‘{FilenameWithExtension}’],’.doc’)
2. Initialize a string type variable “DocumentsFolderName ” to hold the subdirectory name. This is the container folder for all attachments of Model-Driven App table. Since I have configured document upload for “Account” table, its value is “account” for me. The expression in the following action will use this variable.
3. Initialize another string variable “FileNameWithPath” and use the following expression as value:
concat(replace(triggerOutputs()?[‘body/{Path}’],variables(‘DocumentsFolderName’), ”), triggerOutputs()?[‘body/{FilenameWithExtension}’])
What does this expression do?
Let’s use our example document URL, which is:
this variable will hold /test_67B9406FCBB7EB118236000D3A6A4A8A/VR.docx as value to be used in next steps.
4. Initialize string variable “NewFileNameWithExtension” and assign given expression as value:
concat(triggerOutputs()?[‘body/{Name}’], ‘.pdf’)
5. Convert Word document to Pdf, use action and do the following configs:
“Location” and “Document Library” should remain same (where Word document was uploaded). Assign “FileNameWithPath” variable as value in “File”.
6. Use SharePoint Create file action and configure as below:
- Site Address should be same SharePoint site
- Folder Path: assign trigger’s “Folder path”
- File Name: use “NewFileNameWithExtension” variable
- File Content: use output from, Convert Word Document to Pdf, previous step
7. Configure SharePoint (Delete file action) to delete original Word file
Use trigger’s Identifier in “File Identifier” field.
and that’s it, this flow will convert any word document uploaded to Pdf asynchronously.
Let’s Connect
![]() |
![]() |
![]() |
![]() |
Power Automate Convert a String to GUID
Suppose you need to create a new globally unique identifier (GUID). In that case, Power Automate provides guid() function which generates and returns a new GUID as string. It is a useful function, but it doesn’t create a GUID based on string we provide, the way it works in C#. This post will explain how to convert a string into GUID format with dashes (with or without curly braces).
I m making this cloud flow manually triggerable for the sake of post, initializing a variable with the GUID string.
Next, I m converting this string to GUID (with dashes) using expression and storing in a variable.
Here is expression I used, for me variable name is StringGuid, please change as per your situation.
concat(substring(variables('StringGuid'),0,8),'-',substring(variables('StringGuid'),8,4),'-',substring(variables('StringGuid'),12,4),'-',substring(variables('StringGuid'),16,4),'-',substring(variables('StringGuid'),20,12),'')
Same way to convert this string to a formated GUID with curly braces I m using this expression with a variable.
Here is expression to generate GUID with curly braces
concat('{',concat(substring(variables('StringGuid'),0,8),'-',substring(variables('StringGuid'),8,4),'-',substring(variables('StringGuid'),12,4),'-',substring(variables('StringGuid'),16,4),'-',substring(variables('StringGuid'),20,12),''),'}')
Output:
I hope you found this post helpful. If you like to see this available as a feature please vote this idea.
Let’s Connect
![]() |
![]() |
![]() |
![]() |
Fiddler for Model Driven Apps
While developing ‘PowerApp Components’ and traditional ‘Web Resources’, a common challenge is that testing is not straight forward. To test a change in HTML or JS code, one needs to deploy files, which is time-consuming. There are commercial tools available for model-driven apps to make it simpler but still using Fiddler have advantages. This first post of this series will explain how it works and how to get started with it.
What is Fiddler ?
Fiddler is a web proxy debugging tool often used by web developers. Basic version which we need is free forever and can be downloaded from here.
How does it work?
First, we add a web resource shell in the model-driven app, so if it JS, a file with just function name is fine. We still need to configure that function to trigger on load or save events in form properties. From this point to on we can start using Fiddler. We code in Visual Studio, configure Fiddler and open model-driven app in a browser. Fiddler will interrupt incoming request and replace JS file coming from cloud with file open in Visual Studio. Do a code change, just refresh and see the latest JS change without deploying it in app.
Benefits
- We can test JS/ HTML code without deployment. Do code change in Visual Studio, refresh your browser and verify changes.
- One can debug or develop without affecting other developers or users. Complete your work and when done deploy for testing.
Steps
- Add web resource to model-driven app if it is a new resource. For details about web resources see this link and links under “See Also” section of it.
- Install Fiddler
- From Fiddler > Tools > Options > HTTPS, do the following:
- Ensure “Capture HTTPS CONNECTs” and “Decrypt HTTPS traffic” are checked
- In drop down “…from browsers only” is selected
- “Certificates generated by” has “CertEnroll engine”
- Click “Actions > Reset Certificates” and accept all prompts

- Under Filters tab do the following configurations, these will help in targeting only relevant requests:
- “Use Filters” check box is checked
- Ensure for “Hosts”, “Show only Internet Hosts” is selected
- Add your app URLs
- “Show only if URL contains” is checked and it has “/webresources/”

- In “AutoResponder” tab, do the following configs. Here we are telling which web resource we are working and what is the location of development version:
- “Enable rules” is checked
- Click Add Rule
- Enter regular expression with name of JS file like “regex:(?insx).+/account.js”
- Enter path to development version of file on local disk
- Press save button
- Ensure rule created is enabled

- Check “Capture Traffic” under file menu:

- Refreshing browser Fiddler should start capturing traffic. If we open the browser’s dev tools we can see code changes reflected without deploying them.


I hope it was helpful.
Querying Audit History
Audit history is a great out of box feature in model-driven apps. However, querying audit history is a bit tricky. Unfortunately commonly used querying mechanisms like Power Automate CDS connectors, LinQ, or simply FetchXml doesn’t support it. This post will discuss options we have and sample code for it.
Options
- Using SDK messages, RetrieveRecordChangeHistoryRequest & RetrieveRecordChangeHistoryResponse, covered in this post
- Using Kingswaysoft’s Integration Toolkit for D365 (not covering in this post)
Scenario
I will query audit history for contact entity records and read audit details for its email address attribute. Audit details are available under these four heads:
- Changed date
- Changed field
- Old value
- New value
If auditing is enabled, this code will work for almost any entity and attributes.
How it works
We need ids (GUID) of entities and using those we will query audit history. I m using a fetchxml query to retrieve ids, but it can be mechanism of your choice depending on implementation and requirement.
var targetEntites_query = @"<fetch {0}>
<entity name='contact'>
</entity>
</fetch>";
Generally we know FetchXml can return maximum 5000 entities, but this code will handle and return even if there are more then 5000 records in the result.
public List<Entity> RetrieveAllRecords(string fetch)
{
var moreRecords = false;
int page = 1;
var cookie = string.Empty;
List<Entity> Entities = new List<Entity>();
do
{
var xml = string.Format(fetch, cookie);
var collection = CrmClient.RetrieveMultiple(new FetchExpression(xml));
if (collection.Entities.Count >= 0) Entities.AddRange(collection.Entities);
moreRecords = collection.MoreRecords;
if (moreRecords)
{
page++;
cookie = string.Format("paging-cookie='{0}' page='{1}'", System.Security.SecurityElement.Escape(collection.PagingCookie), page);
}
} while (moreRecords);
return Entities;
}
Tip: FetchXml query must have {0} if query will return more then 5000 records. Additional columns can be added in fetch if required.
Next, I m looping through these ids and read audit history for records using this code:
public AuditDetailCollection GetAuditHistory(string entityLogicalName, Guid recordId)
{
var changeRequest = new RetrieveRecordChangeHistoryRequest();
changeRequest.Target = new EntityReference(entityLogicalName, recordId);
var changeResponse = (RetrieveRecordChangeHistoryResponse)this.CrmClient.Execute(changeRequest);
return changeResponse.AuditDetailCollection;
}
Above function returns AuditDetailCollection which has a collection of AuditDetails. One Audit detail represents one entry in audit history. Please note audit history records are in the same order as they appear in UI (descending).
Every audit details record will have a changed date, and collection of new and old values with field names which we will need to loop through and read.
Below is code to accomplish this:
//Collection of entities for which we are going to read audit history
var AllTargetEnteties = this.RetrieveAllRecords(targetEntites_query);
foreach (var targetComplaint in AllTargetEnteties)
{
//Now pass id(guid) of record with entity name to retrieve audit history
var audit_history_entries = this.GetAuditHistory(targetComplaint.LogicalName, targetComplaint.Id);
foreach (AuditDetail auditDetail in audit_history_entries.AuditDetails)
{
if ((auditDetail.GetType())?.Name == "AttributeAuditDetail")
{
//Below code reads Changed Date
var changeDate = auditDetail.AuditRecord.GetAttributeValue<DateTime>("createdon");
var newValueEntity = ((AttributeAuditDetail)auditDetail)?.NewValue;
if (newValueEntity.Attributes.Count > 0)
{
{
foreach (var attrNewValue in newValueEntity?.Attributes)
{
//Here we will need to match attribute name to read new value.
//In this case I m reading emailaddress1
if (attrNewValue.Key == "emailaddress1")
{
var newEmailAddress = attrNewValue.Value;
//Custom Logic for New Value here
}
}
}
}
var oldValueEntity = ((AttributeAuditDetail)auditDetail)?.OldValue;
if (oldValueEntity.Attributes.Count > 0)
{
foreach (var attrOldValue in oldValueEntity?.Attributes)
{
//Here we will need to match attribute name to read old value.
//In this case I m reading emailaddress1
if (attrOldValue.Key == "emailaddress1")
{
var oldEmailAddress = attrOldValue.Value;
//Custom logic for Old value will be here
}
}
}
}
}
}
Let’s Connect
![]() |
![]() |
![]() |
![]() |
Set Lookup Field in Java Script
Adding this post for someone who needs it and for my own reference.
In Java Script lookup can be populated in one of three ways depending on requirements:
- Query lookup data to set in form
- Get lookup details from data available in form (from other fields)
- Hard code lookup values
This is example using second option. It uses logged in user’s id and name to populate lookup. A lookup can be populated by providing its id (GUID) , name and entity type.
var approver = new Array();
approver[0] = new Object();
approver[0].id = Xrm.Page.context.getUserId(); // Guid as "{a004b16f-4bae-4445-9b15-438449a170d3}";
approver[0].name = Xrm.Utility.getGlobalContext().getUserName();
approver[0].entityType = "systemuser";
Xrm.Page.getAttribute("sofhof_approvedby").setValue(approver);
I hope it is helpful.
Let’s Connect
![]() |
![]() |
![]() |
![]() |
Accessibility Resources
Accessibility is an essential consideration for a public sector or enterprise implementation. In this post I m sharing some resources generally about accessibility and also particularly about accessibility in Power Platform Projects. Some of the resources are from Microsoft and others are links to the work I have been doing in previous years. While preparing this list I m feeling very hopeful and excited, why ? because few years back there were not many resources available but now we have good collection, isn’t great 🙂
Resources
- Accessibility Fundamentals learning path at Microsoft Learn
- I presented ‘Accessibility in Power Platform Projects’ at Scottish Summit, here is link to recording
- Similar to above another session at our own “The Good Citizen Developers”, here is recording.
- In Oct 2020 I presented “Accessibility in PowerApp Projects” at Washington DC – User Group. Thanks to lovely organizers and supporters of UG Kylie Kiser, Aiden Kaskela, Nelson Johnson, Mike Ochs and others. I covered things like what accessibility is, why it is important and how to make a PowerApp project accessibility compliance. Here is recording https://www.youtube.com/watch?v=GdK2eV8oOLU
- Microsoft Accessibility Conformance Reports can be downloaded from here
- My blog posts on accessibility:
“Accessibility” for D365 Projects
“Accessibility” for D365 Projects 2 - Scottish Summit 2021 accessibility sessions
Let’s Connect
![]() |
![]() |
![]() |
![]() |
XrmToolBox 2FA Authentication and Proxy Server Settings
2FA Authentications
When connecting XrmToolBox to an environment where 2FA is enabled, the following error can occur:
Error: An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail.

I found this issue when I started working on a new project and didn’t realise 2FA is enabled. Thankfully found Nishant Rana’s post that gave me hint.
Solution ?
Log in using “SDK Login Control” instead of “Connection Wizard” and follow these instructions.



and in next window select your D365 CE instance.
Proxy Settings
In environments where proxy server is placed, again XrmToolBox might not connect. To setup XrmToolBox with proxy server navigate to:
Configuration > Settings > Proxy

If Proxy configurations are set in IE “Use Internet Explorer Configured Proxy” option can be selected. Otherwise, choose “Use custom proxy” and complete the configurations with your proxy server details.

I hope you will find these tips helpful
Let’s Connect
![]() |
![]() |
![]() |
![]() |
Binding CDS Data using Knockout (more tips)

This post is second in a series of querying CDS entity data and displaying it in a model-driven app web resource. A few days back, I wrote first post in this series CDS Data Binding using Knockout. Here I m answering a couple of questions and sharing a few more tips.
Though I have fetched data using WebApi and J-Query but any mechanism that returns object array will work. Yes, we can use FetchXml for querying data too.
The second thing is we can query data from multiple related entities using expand oData function. In this scenario, query returns cases with customer details. Customer can be an account or a contact:
var query = "/api/data/v9.1/incidents?$select=title&$expand=customerid_account($select=name),customerid_contact($select=fullname)";
While doing data binding a few times, I got an error:
Cannot ready property ‘name’ of undefined
The reason was in data few records don’t have customerid_account (parent object was null). To solve this, I have used if binding (Knockout offer different bindings) which checks and only bind the data if the object is not null.
<td>
<!--ko if:customerid_account -->
<span id="accountNamee" data-bind="text:customerid_account.name"> </span>
<!--/ko-->
</td>
Knockout ‘with’ binding can also be used for this:
<td>
<!--ko with:customerid_contact -->
<span id="contactName" data-bind="text:fullname"></span>
<!--/ko-->
</td>
We can do few tricks by using if and ifnot (else) bindings too. Query above is perfect example we either have customerid_account or customerid_contact not both. My requirement was to display combined data from both fields as one:

Here is complete sample code:
<!DOCTYPE html>
<html>
<head>
https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js
https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.1.0.js
http://../../../ClientGlobalContext.js.aspx
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<script>
$(document).ready(function () {
var query = "/api/data/v9.1/incidents?$select=title&$expand=customerid_account($select=name),customerid_contact($select=fullname)";
var oDataUrl = top.Xrm.Page.context.getClientUrl() + query;
var _data = null;
$.getJSON(oDataUrl)
.done(function (data) {
if (data != undefined && data.value.length > 0) {
ko.applyBindings(new AppViewModel(data));
debugger;
}
})
.fail(function (error) {
debugger;
});
});
function AppViewModel(data) {
var self = this;
self.Results = ko.observableArray([]);
ko.utils.arrayForEach(data.value, function (d) {
self.Results.push(d);
});
}
</script>
</head>
<body>
<div style="overflow-x:auto;">
<h4 class="text-center">List of Cases</h4>
<table id="tblContainer" class="table table-sm table-striped table-hover table-borderless">
<thead>
<th>Title</th>
<th>Account</th>
<th>Contact</th>
<th>Client</th>
</thead>
<tbody data-bind="foreach: Results">
<tr>
<td><span id="title" data-bind="text:title"></span></td>
<td>
<!--ko if:customerid_account -->
<span id="accountNamee" data-bind="text:customerid_account.name"></span>
<!--/ko-->
</td>
<td>
<!--ko with:customerid_contact -->
<span id="contactName" data-bind="text:fullname"></span>
<!--/ko-->
</td>
<td>
<!--ko if:customerid_account -->
<span id="accountNamee" data-bind="text:customerid_account.name"></span>
<!--/ko-->
<!--ko with:customerid_contact -->
<span id="contactName" data-bind="text:fullname"></span>
<!--/ko-->
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
I hope you find this useful.
Let’s Connect
![]() |
![]() |
![]() |
![]() |