Sunday, August 2, 2020

Check User Roles - Power Automate

We'd have come across requirements to check the roles of a User in Power automate, but not have found any message to get a User Roles directly from the flow. So the way to do it is to retrieve the System User: Role N:N intermediate entity records and check the count.

There's a different challenge going this way! the intermediate entity name to be used for list Records action in the flow is not same as the name that we see in the CRM solution, we need to get that from the API service. You can get the same by searching for the relation in the page: https://[OrganisationName].api.crm [Version] .dynamics.com/api/data/v9.0/         (Source : https://ajitpatra.com/2019/08/26/microsoft-flow-retrieve-nn-records-in-d365/)
For a System user roles the entity name is systemuserrolescollection

Below is the list records action fetch xml to retrieve No. of roles out of roleName1/roleName2/roleName3 that a User with guid "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" this guid can be passed directly or  can also be replaced with a variable as in the snapshot.
fetchxml for list records:

<fetch>
  <entity name="systemuserroles" >
    <attribute name="systemuserroleid" />
    <filter>
      <condition attribute="systemuserid" operator="eq" value="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" />
    </filter>
    <link-entity name="role" from="roleid" to="roleid" link-type="inner" alias="Roles" >
      <filter type="or" >
        <condition attribute="name" operator="eq" value="roleName1" />
        <condition attribute="name" operator="eq" value="roleName2" />
        <condition attribute="name" operator="eq" value="roleName3 />
      </filter>
    </link-entity>
  </entity>
</fetch> 

The Length of the list - in this case  length(outputs('Retrieve_Creating_User_Roles_intent_roles_count')?['body']?['value']) returns non zero if User has at least one role out of above 3  mentioned. Replace Retrieve_Creating_User_Roles_intent_roles_count with the name of list records step.
And you are ready!

Wednesday, June 10, 2020

Set a Field on the Editable grid disabled.

Add the below script to JS webresource and add that to Form Libraries:
function setChildGridEditability(gridContext) {
    var childGrid = gridContext.getFormContext().data.entity;
    childGrid.attributes.getByName("attributeLogicalName").controls.get(0).setDisabled(true);
}
Register event handler - function setChildGridEditability on Editable Grid - OnRecordSelect event.

Set Pass execution context as first parameter  checked... and Ready!

Monday, March 16, 2020

Useful Powershell scripts

To Connect to CRM

1. Connect to CRM from Powershell Script using Command-line parameters(Ideal for using Script for automation purposes.)
# This PS snippet retreieves the Active Duplicate DEtection Rules frmo the crmDevOrg and activates them in crmTestOrg.
# The User  Name, Password and the Organization Name are to be updated before executing this.

## pass arguments OrganizationURL, UserName, Password
    [CmdletBinding()]
    param(
        [Parameter(Position=0, Mandatory=$true)] [string]$SourceOrganizationName,
        [Parameter(Position=1, Mandatory=$true)] [string]$SourceUserName,
        [Parameter(Position=2, Mandatory=$true)] [string]$SourcePassword,


Install-Module -Name Microsoft.Xrm.Data.Powershell -RequiredVersion 2.8.7 -Scope CurrentUser
$password = ConvertTo-SecureString $SourcePassword -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential($SourceUserName,$password)
$crmDevOrg = Get-CrmConnection -Credential $credentials -DeploymentRegion NorthAmerica -OnlineType Office365 -OrganizationName $SourceOrganizationName  -MaxCrmConnectionTimeOutMinutes 5
 
2. Using interactive mode

Install-Module -Name Microsoft.Xrm.Data.Powershell -RequiredVersion 2.8.7 -Scope CurrentUser
$crmDevOrg = Get-CrmConnection –InteractiveMode 


3.Activate Duplicate detection rules with Powershell shell Script depending on the activatesd Rules in another Orgs - say - check form Source Org and ACtivate in the Destination Org

 This PS snippet retreieves the Active Duplicate DEtection Rules frmo the crmDevOrg and activates them in crmTestOrg.
# The User  Name, Password and the Organization Name are to be updated before executing this.

## pass arguments OrganizationURL, UserName, Password
    [CmdletBinding()]
    param(
        [Parameter(Position=0, Mandatory=$true)] [string]$DevOrganizationName,
        [Parameter(Position=1, Mandatory=$true)] [string]$DevUserName,
        [Parameter(Position=2, Mandatory=$true)] [string]$DevPassword,
 [Parameter(Position=3, Mandatory=$true)] [string]$TestOrganizationName,
        [Parameter(Position=4, Mandatory=$true)] [string]$TestUserName,
        [Parameter(Position=5, Mandatory=$true)] [string]$TestPassword
    )

Install-Module -Name Microsoft.Xrm.Data.Powershell -RequiredVersion 2.8.7 -Scope CurrentUser
$password = ConvertTo-SecureString $DevPassword -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential($DevUserName,$password)
$crmDevOrg = Get-CrmConnection -Credential $credentials -DeploymentRegion NorthAmerica -OnlineType Office365 -OrganizationName $DevOrganizationName  -MaxCrmConnectionTimeOutMinutes 5
 
$duplicaterules = Get-CrmRecords -conn $crmDevOrg 'duplicaterule' -FilterAttribute statecode -FilterOperator eq -FilterValue Active
$duplicateruleIds = $duplicaterules['CrmRecords'] | select -ExpandProperty duplicateruleid

$password = ConvertTo-SecureString $TestPassword -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential($TestUserName,$password)
$crmTestOrg = Get-CrmConnection -Credential $credentials -DeploymentRegion NorthAmerica -OnlineType Office365 -OrganizationName $TestOrganizationName  -MaxCrmConnectionTimeOutMinutes 5
 
foreach($ruleId in $duplicateruleIds)
        {
            write-host "publishing rule id: " $ruleId
            $ddRule_toPublish = New-Object Microsoft.Crm.Sdk.Messages.PublishDuplicateRuleRequest            
            $ddRule_toPublish.DuplicateRuleId= $ruleId
            $crmTestOrg.ExecuteCrmOrganizationRequest($ddRule_toPublish,$trace)  
            Write-Host "Rule Published"       
        }

Thursday, March 12, 2020

Set the Lookup Field value in Power automate, while using Common Data Service Connector (CDS)

When we try to set a Lookup field from the Flow, using a Guid field available in the flow, we get a Resource not found Segment for the segment error.
This is because, when the Unique If field is added directly for a Lookup, the Entity Schema name is not detected by the flow service.  Here's a Work around for it.
If the lookup target entity name is abc_list, then, first type in -  abc_lists/   and then choose the Guid field from the Dynamics entry window.

Friday, February 7, 2020

Set the BPF and Stage through Power Automate for Dynamics 365 CRM

The MS documentation clearly specifies, that the setting BPF/stage is not supported.
This method uses the deprecated Process Id field of an entity for which the BPF is to be set(Only way).
Hence, with future releases the functionality might not work.

Let's consider a Custom entity- Application as the target to set the BPF for.
AS we created a workflow of category BPF (Let's assume we create one with Name "Application Flow")for an entity, System automatically creates an entity to store the BPF information.
For each of the Application record, one record(instance) of the activated BPF will be created and associated automatically.
The  last BPF instance created with Status "Active" is what shows up on the Form, also filtered additionally by the Security Role.
The stages of all the BPF in the System are referenced using Process Stage entity.

The BPF instance has a lookup to :
-The main entity
-Stage entity
-Process (The GUID of the Workflow)
which helps have the references.

To set the BPF  stage for an entity, we need to :
-Retrieve the existing Flow instances associated with the record and delete them,
-Retrieve the stage Id - passing the name of the stage.
-Create a new instance of the BPF , setting the main entity lookup field, Name, Stage Id, Status -Active,Workflow Id and then associate this new instance with the main entity additionally - by setting the Process lookup Field.
Please see the steps oulined below.

Tuesday, January 7, 2020

Create N:N Association, if not already present.
//Code follows

public void associateWithCheck(){
EntityCollection recordCollection = RetrieveMultiple_Attribute(entityLogicalName, new ColumnSet(entityField), entityField, sourceDataFields[0], client);

                            EntityCollection relatedRecordCollection = CrmHelper.RetrieveMultiple_Attribute(relatedEntityLogicalName, new ColumnSet(relatedEntityField), relatedEntityField, sourceDataFields[1], client);
                            if (recordCollection.Entities.Count > 1 || relatedRecordCollection.Entities.Count > 1)
                            {
                                // If you have more than one corresponding record in CRM do something
                                Console.WriteLine("More than 1 match for {0} - {1} please update CRM Data/Data Imported.", entityLogicalName, compareValue);
                            }

                            // Add to the update list - Match found
                            else if (recordCollection.Entities.Count == 1 && relatedRecordCollection.Entities.Count == 1)
                            {
                                Entity record = recordCollection.Entities[0];
                                Entity relatedRecord = relatedRecordCollection.Entities[0];
                                bool areEntitiesAlreadyAssociated = CrmHelper.RelationshipExists(relationshipName, record.Id, entityLogicalName, relatedRecord.Id, relatedEntityLogicalName,client);

                                if (!areEntitiesAlreadyAssociated)
                                {
                                    // entitiesToUpdate.Entities.Add(recordForUpdate);

                                    AssociateRequest associateRequest = new AssociateRequest
                                    {
                                        Target = new EntityReference(entityLogicalName, record.Id),
                                        RelatedEntities = new EntityReferenceCollection
                                        {

                                            new EntityReference(relatedEntityLogicalName, relatedRecord.Id)
                                        },
                                        Relationship = new Relationship(relationshipName)
                                    };
                                    associateRequestWithResults.Requests.Add(associateRequest);
                                    Console.WriteLine(entityLogicalName + " " + sourceDataFields[0] + relatedEntityLogicalName + " " + sourceDataFields[1] + "added to association list");
                                }
                                else {
                                    Console.WriteLine(entityLogicalName + " " + sourceDataFields[0] + relatedEntityLogicalName + " " + sourceDataFields[1] + "already related");
                                        }
                            }
}

Sunday, April 21, 2019

Import Past Dated- Created On Records

We may be wondering how to have a Past Date as Created on - on the records,
This is needed while importing legacy data and we want to have accurate Created on Date, especially if we are building Reports on this data.
The trick is to add the desired Created  on Date on to the Overriddencreatedon field.
It is not possible to achieve this by using Import Wizard/ Plugins/WFs directly.
We can get it to work by using:

1)A Console Application , which creates the records, with the attribute Overriddencreatedon set to the desired past date in the Entity created.

2)A Console Application to create the records, and have a PreCreate Plugin/WF to add the desired past date to Overriddencreatedon attribute of the Target entity.

Below are some samples

1)Console App Only

Supply username, password and url in the app.config
<add key="username" value="example@example.onmicrosoft.com" />
<add key="password" value="password" />
<add key="url" value="https://example.crm.dynamics.com" />
   
//Code Follows as below
namespace sampleUsingOverriddencreatedon
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Linq;

    /// <summary>
    /// Class for the Core importer Logic
    /// </summary>
    public class ImporterLogic
    {
        /// <summary>
        /// Adds past dated lead
        /// </summary>
        /// <returns>Output Message</returns>
        public void createPastLead()
        {

            string connectionString  = $"Url={RetrieveFromconfig("url")};Username={RetrieveFromconfig("username")};Password={RetrieveFromconfig("password")};AuthType=Office365;RequireNewInstance=true";
            CrmServiceClient client = new CrmServiceClient(connectionString);
            Entity lead = new Entity("lead");
            lead["lastname"] = "Harri " + i;
            lead["subject"] = "testoverridencreatedon";
            lead["overriddencreatedon"] = new DateTime(year, i, 1);
            client.Create(lead);
        }
 public static string RetrieveFromconfig(string key)
        {
            string value;
            value = ConfigurationManager.AppSettings[key];
            return value;
        }
    }
}

2) Pre-Create Plugin which would override the Created of the Record Created from any external application
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xrm.Sdk;
namespace OverRidePlugin
{
    public class UpdatedCreatedOn :IPlugin
    {
        public void Execute(IServiceProvider serviceProvider) {
            ITracingService tracingService =
                (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            // Obtain the execution context from the service provider.
            IPluginExecutionContext context = (IPluginExecutionContext)
                serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
            if (context.InputParameters["Target"] is Entity && context.Depth==1) {
                Entity entity = (Entity)context.InputParameters["Target"] ;
                entity["overriddencreatedon"] = new DateTime(2012, 2, 22);
            }

        }
    }
}