Friday, February 27, 2026

Converting Dynamics 365 Security Roles to Read-Only with PowerShell

 In Dynamics 365 / Dataverse, security roles often contain unintended modification privileges (Create, Write, Delete, etc.) even when they are meant to be read-only. Manually correcting them is time-consuming and error-prone — especially in environments with many custom entities.

This post shows how to safely convert existing roles into read-only roles using PowerShell.


The Problem

Roles labeled as “Read Only” still had privileges such as:

  • Create

  • Write

  • Delete

  • Append

  • AppendTo

  • Assign

  • Share

The goal was to remove modification privileges while preserving:

  • All Read privileges

  • System-level privileges

  • App and workflow functionality


The Solution

Using the Microsoft.Xrm.Data.PowerShell module:

  1. Connect via Client Secret authentication

  2. Retrieve the role

  3. Fetch associated privileges

  4. Remove only modification privileges

  5. Leave system and read privileges intact




Import-Module Microsoft.Xrm.Data.PowerShell

$crmUrl = "https://yourorg.crm.dynamics.com"
$clientId = ""
$clientSecret = ""
$tenantId = ""

$conn = Get-CrmConnection -ConnectionString "
AuthType=ClientSecret;
Url=$crmUrl;
ClientId=$clientId;
ClientSecret=$clientSecret;
TenantId=$tenantId"

$rolesToFix = @(
    "AdvancedTemp - Read Only",
    "StandardTemp - Read Only"
)

# Only remove modification privileges
$removePattern = "^(prvCreate|prvWrite|prvDelete|prvAppend$|prvAppendTo|prvAssign|prvShare)"

foreach ($roleName in $rolesToFix) {

    $role = Get-CrmRecords `
        -conn $conn `
        -EntityLogicalName role `
        -FilterAttribute name `
        -FilterOperator eq `
        -FilterValue $roleName `
        -Fields roleid

    if ($role.CrmRecords.Count -eq 0) { continue }

    $roleId = $role.CrmRecords[0].roleid

    $fetch = @"
<fetch>
  <entity name='roleprivileges'>
    <filter>
      <condition attribute='roleid' operator='eq' value='$roleId' />
    </filter>
    <link-entity name='privilege'
                 from='privilegeid'
                 to='privilegeid'
                 alias='p'>
      <attribute name='privilegeid'/>
      <attribute name='name'/>
    </link-entity>
  </entity>
</fetch>
"@

    $privileges = Get-CrmRecordsByFetch -conn $conn -Fetch $fetch

    foreach ($priv in $privileges.CrmRecords) {

        $privId = $priv."p.privilegeid"
        $privName = $priv."p.name"

        if (-not $privId -or -not $privName) { continue }

        if ($privName -match $removePattern) {

            Remove-CrmRecordAssociation `
                -conn $conn `
                -EntityLogicalName1 role `
                -Id1 ([Guid]$roleId) `
                -EntityLogicalName2 privilege `
                -Id2 ([Guid]$privId) `
                -RelationshipName roleprivileges_association
        }
    }
}



Result

This script:

  • Removes all modification privileges

  • Preserves read and system access

  • Prevents app or workflow breakage

  • Automates role cleanup at scale

PowerShell provides a reliable and repeatable way to enforce true read-only security roles in Dynamics 365.

Friday, January 30, 2026

Impact of Dataverse Multiplexing on licenses

 impact of Dataverse Multiplexing on licenses, especially when customers log into the Portal to access data from Dynamics 365 CRM, and its impact on licensing requirements.

Scenario: Customers(external users) Accessing Data via Portal


In this scenario, your customers log into the Portal, which retrieves and displays data from Dynamics 365 CRM. This data access is managed through a service account that connects the portal to Dataverse.

Impact on Licensing

  1. Service Account and Multiplexing: The service account acts as a single connection point to Dataverse, multiplexing multiple customer requests through one account. While this simplifies connection management, it does not change the underlying licensing requirements.
  2. Customer Licensing: Each customer accessing data from Dynamics 365 CRM through the Portal must have the appropriate license. Using a service account to handle these connections does not reduce the number of required licenses.
  3. Compliance: According to Microsoft’s licensing policies, each individual who accesses CRM data must be properly licensed. Therefore, you must ensure that every customer using the Portal has the necessary licenses, despite the service account managing the connection.

Conclusion

While Dataverse Multiplexing using a service account offers efficiency, it’s essential to remember that it doesn’t change licensing requirements. Every customer accessing Dynamics 365 CRM data via the Portal must have a valid license to ensure compliance with Microsoft’s terms.


undefined

In the current context of the portal, Assume that User 4 is a Customer(External User), User 3 is a Service Account, Manually Forward Email is the external(Cache DB ), and Pooling Hardware/Software will be the API to access Dataverse

Wednesday, August 7, 2024

Power Automate - Overwrite existing file in SP using create file control

if the file already exists at the target with the same name then the power automate will thorws an error. if the expectation is to overwrite the file in the target then a simple setting needs to be turned on in Sharepoint Create File control Turn Off Allow Chunking

Thursday, January 12, 2023

D365 Clear Power Apps Portal Cache

 In Dynamics 365 Power Apps portals, you can clear the cache of the portal using the PortalCacheService class. The PortalCacheService class provides methods to clear the cache for specific entities or for the entire portal.

Here is an example of how you might use C# code to clear the cache for a specific entity in a Dynamics 365 Power Apps portal:


using Microsoft.PowerApps.Portals; // ... public void ClearCacheForEntity(string entityName) { var cacheService = new PortalCacheService(); cacheService.ClearCacheForEntity(entityName); }

And here's how you can clear the entire portal cache:

using Microsoft.PowerApps.Portals; // ... public void ClearAllCache() { var cacheService = new PortalCacheService(); cacheService.ClearAllCache(); }

You can call above methods wherever you want to clear the cache, like in a button click event or on some specific conditions.

Please note that, this code sample is for the portal which is build using Power App portals, if you are using Dynamics 365 portals, the approach will be different. Also, keep in mind that clearing the cache can cause a temporary performance hit on the portal, so it is generally a good idea to clear the cache during off-peak hours or when the portal is not heavily used.

Update content setting in live customer journey in d365 marketing

 In Dynamics 365 Marketing, you can use the Live Customer Journey feature to update the content of an active customer journey while it is running. This allows you to make changes to the journey without having to stop and restart it, which can save time and minimize disruption to your customers.

Here is an example of how you might update the content of an active customer journey using the Dynamics 365 Marketing API

// Update the content of an active customer journey function updateContentSetting(customerJourneyId: string, contentSettingId: string) { // Update the content setting var update = { content: "<p>New Content</p>" }; var req = new XMLHttpRequest(); req.open("PATCH", Xrm.Page.context.getClientUrl() + "/api/data/v9.0/marketing/customerjourneys(" + customerJourneyId + ")/Microsoft.Dynamics.Marketing.LiveCustomerJourney/contentSettings("+contentSettingId+")", true); req.setRequestHeader("OData-MaxVersion", "4.0"); req.setRequestHeader("OData-Version", "4.0"); req.setRequestHeader("Accept", "application/json"); req.setRequestHeader("Content-Type", "application/json; charset=utf-8"); req.onreadystatechange = function () { if (this.readyState === 4) { req.onreadystatechange = null; if (this.status === 204) { // Content setting updated successfully console.log("Content setting updated successfully"); } else { console.log(this.statusText); } } }; req.send(JSON.stringify(update)); }


In this sample, you pass the customerJourneyId and contentSettingId of the customer journey to update and it will update the content of the customer journey. This is a simplified example you may need to import the required types and configure the appropriate options as per your need.

Please note that in order to use this sample code, you must have the appropriate permissions to update customer journeys in Dynamics 365 Marketing, and you must have an active customer journey with a content setting that you want to update.

Also, keep in mind that modifying an active customer journey can impact the ongoing customer interactions and cause unexpected results, so be sure to thoroughly test any changes you make before applying them to a live customer journey.

Create and Send SMS in D365 Marketing

 In Dynamics 365 Marketing, you can use the SMS message type to create and send SMS messages to contacts or leads in your database. The process for creating and sending an SMS message involves several steps:

  1. Create an SMS message template: In Marketing, navigate to Email and SMS > SMS message templates, and create a new template. In the template, you can specify the message content, and include personalization fields to dynamically insert information from the contact or lead record.

  2. Create an SMS message: In Marketing, navigate to Email and SMS > SMS messages, and create a new message. In the message, you can specify the recipient list, select the SMS message template, and set the send options.

  3. Send the SMS message: After creating the SMS message, you can send it immediately or schedule it to be sent at a later time.

Here's a sample process on how to send SMS message by using a sample code

var smsMessage = { recipientList: { query: "contacts", filter: "statuscode eq 1" }, template: { id: "cf5b5f0f-cbc4-45c9-9edf-e8a0da22b2d4" }, sender: { id: "c1b5f0f-cbc4-45c9-9edf-e8a0da22b2d3" }, sendImmediately: true }; var smsMessageId = Xrm.WebApi.createRecord("sms", smsMessage).then( function success(result) { var smsId = result.id; console.log("SMS created with ID: " + smsId); }, function (error) { console.log(error.message); });

This is just a simplified example, in your real scenario you may have to customize it as per your need, like you may have to import required types and apply the appropriate configurations options. Please note that in order to use this sample code, you must have the appropriate permissions to create and send SMS messages in Dynamics 365 Marketing, and you must have an active SMS message template and SMS sender account set up in your system.

Please be aware that SMS messaging is generally regulated, and laws and regulations vary by country. Before sending SMS messages to contacts or leads, you should familiarize yourself with any relevant laws and regulations, and obtain any necessary consent from your contacts or leads.

Friday, May 24, 2019

SDK: Create Email Using Template

        private static Guid CreateEmailMessageFromTemplate(IOrganizationService service, EntityReference contact, Guid templateId, EntityReference fromQueue)
        {
            InstantiateTemplateRequest request = new InstantiateTemplateRequest()

            {
                TemplateId = templateId,
                ObjectId = contact.Id,
                ObjectType = contact.LogicalName
            };
            InstantiateTemplateResponse response = (InstantiateTemplateResponse)service.Execute(request);
            Entity email = response.EntityCollection[0];
            var toActivityParty = new Entity("activityparty");
            toActivityParty["partyid"] = new EntityReference("contact", contact.Id);
            EntityCollection toParty = new EntityCollection() { EntityName = "activityparty" };
            toParty.Entities.Add(toActivityParty);

            var fromActivityParty = new Entity("activityparty");
            fromActivityParty["partyid"] = fromQueue;
            EntityCollection fromParty = new EntityCollection() { EntityName = "activityparty" };
            fromParty.Entities.Add(fromActivityParty);

            email.Attributes["to"] = toParty;
            email.Attributes["from"] = fromParty;
            return service.Create(email);
        }

Restrict Browser Back button

history.pushState(null, null, location.href);
    window.onpopstate = function () {
        history.go(1);
    };

Use the above code to restrict going to the back page from the current page.

This piece of code comes very handy when you are working on dynamics portals and you have logic where you should not allow users to navigate to back page.

Tuesday, October 23, 2018

D365 - Retreive more than 5000 records using fetchxml

 private static List RetrieveAllRecords(IOrganizationService service, string fetch)  
     {  
       var moreRecords = false;  
       int page = 1;  
       var cookie = string.Empty;  
       List<Entity> Entities = new List();  
       do  
       {  
         var xml = string.Format(fetch, cookie);  
         var collection = service.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;  
     }  

Usage

  var fetch = "<fetch {0}>" +  
             "  <entity name='accounts' >" +  
             "    <attribute name='accountid' />" +  
             "    <attribute name='name' />" +  
             "  </entity>" +  
             "</fetch>";  
       var accountCol = RetrieveAllRecords(service, fetch);  

Monday, August 27, 2018

Dynamics Portals Caching issue

When you change any portal configuration in CRM it doesn't gets reflected on the portals unless the portal is restated from the admin portal. This is very painful when we tend to do some small changes as it takes at least 2-3 mins to restart the portal.

Portal provides about page which has the clear cache option which can be used in this senarions. to access the page append the Portal URL + /_services/about














Make sure the user accessing this page is logged into the portal and have "administrator" web role

Tuesday, March 13, 2018

Error Connecting to Dynamics 365 Online from Report Viewer in VS

After entering connection details the connection dialog prompts for the credentials multiple times and this is because of the recent changes by Microsoft to he overall platform

ROOT CAUSE:
Its all because of the latest update in the Microsoft TSL(Transport Security Layer) Protocol in SDK assemblies..Microsoft allowed the TSL connection 1.0  and 1.1 for the browsers or client to connect the CRM org.Now Microsoft will support only TSL 1.2 or above going forward(Reference) . If you are connecting your org with the old version of plugin registration tool , then you may face this issue.


Uninstall Report Authoring Extension and install the latest.(Make sure Installed dll is the latest SDK 9.0). if this doesnt resolve the issue then follow the below step.


On the machine where VS is installed go to the start menu, then type run and then enter. Type in regedit and then OK.

Once the Registry Editor is open, go to: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319
Right click on the name of the folder (the v4.0.30319 folder) and select New, then DWORD. Give it the name of SchUseStrongCrypto and the Value of 1. Exit the Registry Editor, then restart your machine. 

Wednesday, February 14, 2018

SDK - Send Email Using Template with attachement

 InstantiateTemplateRequest instTemplateReq = new InstantiateTemplateRequest  
           {  
             TemplateId = templateId,  //template guid
             ObjectId = incidentId,   // template type id
             ObjectType = "incident"  
           };  
 InstantiateTemplateResponse instTemplateResp = (InstantiateTemplateResponse)service.Execute(instTemplateReq);  
 Entity instTemplate = (Entity)instTemplateResp.EntityCollection.Entities[0];  
 Guid emailGuid = service.Create(instTemplate);  
 //Add attachement  
 Entity emailAttachment = new Entity("activitymimeattachment");  
 //attachement in byte[]  
 emailAttachment["body"] = Convert.ToBase64String(attachement);  
 emailAttachment["objectid"] = new EntityReference("email", emailGuid);  
 emailAttachment["objecttypecode"] = "email";  
 emailAttachment["filename"] = item.Name;  
 service.Create(emailAttachment);  //Creates attachement to email
 SendEmailRequest sendEmailReq = new SendEmailRequest()  
           {  
             EmailId = emailGuid,  
             IssueSend = true  
           };  
  SendEmailResponse sendEmailResp = (SendEmailResponse)service.Execute(sendEmailReq);  

Saturday, September 23, 2017

Retrieve EntityImage using SDK

In this example we will retrieve contact entity image

var Contact=Service.Retreive("contact","GUID",new ColumnSet(true));
if(Contact.Attributes.Contains("entityimage"))
{
byte[] entityimage=Contact["entityimage"] as byte[];
}

the above piece of code has no issues and ideally should Retreiveall the attributes of the contact as new ColumnSet(true) is specified but this will not get the entity image 

When you use RetrieveMultiple or Retrieve the EntityImage is not included when the ColumnSet.AllColumns property is set to true. Because of the potential size of data in this attribute, to return this attribute you must explicitly request it.
The binary data representing the image isn’t returned using the deprecated ExecuteFetchRequest class. You should use RetrieveMultipleRequestinstead.

Reference - https://msdn.microsoft.com/en-in/library/dn817886.aspx

Friday, July 7, 2017

SetStateRequest depricated in dynamics 365 online

SetStateRequest being deprecated now in Dynamics 365 (online).
use UpdateRequest to modify specialized fields.

Refer to the below link
https://msdn.microsoft.com/en-us/library/dn932124.aspx

Tuesday, August 23, 2016

CRM 2016 Web API Retrieve Attribute Names By Entity Name

GetEntityMetaData function will retrieve the list of attributes from he Account entity.

 function GetEntityMetaData() {  
   var entityType = "Account";  
   var metaDataId = getMetaDataIdByName(entityType);  
   var schemaNames = getAttributeList(metaDataId);  
 }  
 function getAttributeList(metaDataId) {  
   var attributesList;  
   $.ajax({  
     type: "GET",  
     contentType: "application/json; charset=utf-8",  
     datatype: "json",  
     url: Xrm.Page.context.getClientUrl() + "/api/data/v8.1/EntityDefinitions(" + metaDataId + ")?$select=LogicalName&$expand=Attributes($select=LogicalName)",  
     beforeSend: function (XMLHttpRequest) {  
       XMLHttpRequest.setRequestHeader("OData-MaxVersion", "4.0");  
       XMLHttpRequest.setRequestHeader("OData-Version", "4.0");  
       XMLHttpRequest.setRequestHeader("Accept", "application/json");  
       XMLHttpRequest.setRequestHeader("Prefer", "odata.include-annotations=\"OData.Community.Display.V1.FormattedValue\"");  
     },  
     async: false,  
     success: function (data, textStatus, xhr) {  
       attributesList = data.Attributes;  
     },  
     error: function (xhr, textStatus, errorThrown) {  
       alert(textStatus + " " + errorThrown);  
     }  
   });  
   return attributesList;  
 }  
 function getMetaDataIdByName(entityType) {  
   var MetadataId;  
   $.ajax({  
     type: "GET",  
     contentType: "application/json; charset=utf-8",  
     datatype: "json",  
     url: Xrm.Page.context.getClientUrl() + "/api/data/v8.1/EntityDefinitions?$select=DisplayName,IsKnowledgeManagementEnabled,EntitySetName&$filter=SchemaName eq '" + entityType + "'",  
     beforeSend: function (XMLHttpRequest) {  
       XMLHttpRequest.setRequestHeader("OData-MaxVersion", "4.0");  
       XMLHttpRequest.setRequestHeader("OData-Version", "4.0");  
       XMLHttpRequest.setRequestHeader("Accept", "application/json");  
       XMLHttpRequest.setRequestHeader("Prefer", "odata.include-annotations=\"OData.Community.Display.V1.FormattedValue\"");  
     },  
     async: false,  
     success: function (data, textStatus, xhr) {  
       var result = data.value[0];  
       MetadataId = result.MetadataId;  
     },  
     error: function (xhr, textStatus, errorThrown) {  
       alert(textStatus + " " + errorThrown);  
     }  
   });  
   return MetadataId;  
 }  

Monday, February 1, 2016

Retrieve Team Members using RetrieveMultipleRequest

As we know that CRM Sdk deprecated RetrieveMembersTeamRequestmessage (https://msdn.microsoft.com/en-us/library/gg309360.aspx). So there is only way to retrieve Team Members using RetrieveMultipleRequest. and here is the method for the same. It works for the AccessTeam too.


 private static void RetrieveTeamMembers(IOrganizationService service, EntityReference teamEntityReference)  
 {  
 QueryExpression userQuery = new QueryExpression("systemuser");  
 userQuery.ColumnSet = new ColumnSet("fullname");  
 LinkEntity teamLink = new LinkEntity("systemuser", "teammembership", "systemuserid", "systemuserid", JoinOperator.Inner);  
 ConditionExpression teamCondition = new ConditionExpression("teamid", ConditionOperator.Equal, teamEntityReference.Id);  
 teamLink.LinkCriteria.AddCondition(teamCondition);  
 userQuery.LinkEntities.Add(teamLink);  
 EntityCollection retrievedUsers = service.RetrieveMultiple(userQuery);  
 foreach (Entity user in retrievedUsers.Entities)  
 {  
 Console.WriteLine(String.Format("User: {0} with GUID {1}", user.GetAttributeValue<string>("fullname"),  
 user.Id));  
 }  
 }  

Tuesday, July 28, 2015

CRM 2011/2013/2015 Javascript: Get Logged in User Full Name

CRM 2011

 function getUserFullName() {  
   var serverUrl = getCrmServerUrl();  
   var userRequest = GetRequestObject();  
   userRequest.open("GET", serverUrl + "/SystemUserSet(guid'" + Xrm.Page.context.getUserId() + "')?$select=FullName", false);  
   userRequest.setRequestHeader("Accept", "application/json");  
   userRequest.setRequestHeader("Content-Type", "application/json; charset=utf-8");  
   userRequest.send();  
   if (userRequest.status === 200) {  
     var retrievedUser = JSON.parse(userRequest.responseText).d;  
     var userFullName = retrievedUser.FullName;  
     return userFullName;  
   }  
   else {  
     return "";  
   }  
 }  
 function getCrmServerUrl() {  
   if (Xrm.Page.context.getClientUrl) // CRM 2011 UR 12+ and CRM 2013  
   {  
     serverUrl = Xrm.Page.context.getClientUrl() + "/";  
   }  
   else // CRM 2011 UR 11 or below  
   {  
     serverUrl = Xrm.Page.context.getServerUrl();  
   }  
   // Adjust URL for differences between on premise and online  
   if (serverUrl.match(/\/$/)) {  
     serverUrl = serverUrl.substring(0, serverUrl.length - 1);  
   }  
   return serverUrl + "/XRMServices/2011/OrganizationData.svc";  
 }  
 function GetRequestObject() {  
   if (window.XMLHttpRequest) {  
     return new window.XMLHttpRequest;  
   }  
   else {  
     try {  
       return new ActiveXObject("MSXML2.XMLHTTP.3.0");  
     }  
     catch (ex) {  
       return null;  
     }  
   }  
 }  


CRM 2013/2015

Xrm.Page.context.getUserName()

Thursday, July 9, 2015

How to Analyze the Performance of Dynamics CRM

The Performance Tool offers a simple and efficient method to analyze (and improve) the performance of Microsoft Dynamics CRM online and on premise environments.
You do need to have CRM 2013 SP1(or above) or 2015. It can be used on both forms and views.
The following steps activates this hidden feature.
Open a CRM form from your browser (IE or Chrome). For example, open an Account form.
Press Ctrl + Shift + Q to view the performance analyzer. This analyzer pop up window appears on your browserAnalyze Performance of CRM
To activate the tool, click the Enable button.
Press F5 to refresh your CRM form. The Performance analyzer will close.
Press Ctrl + Shift + Q to open the performance analyzer
Analyze performance of Dynamics CRM

What does this mean?

The different sections are all code that is triggered prior to a user having a functioning form to work on.
When an environment has many customizations, plug-ins, business rules, iframes or subgrid on a form, this is a great way to determine how quickly it is loading.The time is a breakdown of milliseconds.
In addition to the graphical view, when you click the “Select Major” button you’ll also see the breakdown in a text format.
  • Form Load Start (> 0 ms)
  • Read-Ready (> 1272 ms)
  • Initialize Controls – ViewportInlineEditControlInitializer (> 4583 ms)
  • Initialize Controls – NonViewportFormBodyInlineEditInitializer (> 4954 ms)
  • Initialize Controls – DeferredQuickFormInlineEditInitializer (> 5106 ms)
  • Onload Handler Start (> 5406 ms)
  • Onload Handler Finish (> 5579 ms)
  • Form Full Controls Init (> 5663 ms)
Finally, it is a good idea to disable the tool by clicking the “Disable” button when you have completed your analysis.

How to Close a Form in CRM 2013 using javascript

With reference to CRM 2013 SDK, following is the code snippet to close the form.

Xrm.Page.ui.close();

Please note the following important points.

"The HTML Window.close method is suppressed. To close a form window you 

must use this method. If there are any unsaved changes in the form the user 

will be prompted whether they want to save their changes before the window closes." 

( Ref: CRM 2013 SDK )


Whereas if we need to save the record using javascript, 

we could use the same methods which we were using in CRM 2011.

Xrm.Page.data.entity.save( null | "saveandclose" |"saveandnew" );

 

Arguments values:


save()

If no parameter is included the record will simply be saved. This is the equivalent of using the 

Save command.


save("saveandclose")

This is the equivalent of using the Save and Close command.


save("saveandnew")

This is the equivalent of the using the Save and New command.

( Ref: CRM 2013 SDK )