Multi Thread Batch Operation for BPF in Dynamic CRM using Powershell.

Recommended

Retrieve the Contact Record by XML.

$varFetch = @"
<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
<entity name="contact">
<attribute name="fullname" />
<attribute name="contactid" />
<order descending="true" attribute="modifiedon" />
<filter type="and">
  <condition attribute="statecode" operator="eq" value="0" />
</filter>
	<link-entity name="Add Your Flow Name" from="bpf_contactid" to="contactid" link-type="outer" alias="af" />
<filter type="and">
  <condition entityname="af" attribute="bpf_contactid" operator="null" />
</filter>
</entity>
</fetch>
"@ 

Initialize the $counter, $groupSize, $records Variables.

$counter = [pscustomobject] @{ Value = 0 }
$groupSize = 100
$records = (Get-CrmRecordsByFetch -conn $conn -Fetch $varFetch -AllRows).CrmRecords

Pipe to loop the records.

$records | Group-Object -Property {  [math]::Floor($counter.Value++ / $groupSize) } | 
Foreach {
   "Perform Your Operation"
}

Loop the Thread Job.

Start-ThreadJob -ArgumentList @($conn, $_.Group){
		param($conn, $group)
}

Setting the ExecuteMultipleSettings and OrganizationRequestCollection.

$executeReq = [Microsoft.Xrm.Sdk.Messages.ExecuteMultipleRequest]::new()
$executeSettings = [Microsoft.Xrm.Sdk.ExecuteMultipleSettings]::new()
$executeSettings.ContinueOnError = $true
$executeSettings.ReturnResponses = $false
$executeReq.Requests = [Microsoft.Xrm.Sdk.OrganizationRequestCollection]::new()
$executeReq.Settings = $executeSettings

$executeBPFReq = [Microsoft.Xrm.Sdk.Messages.ExecuteMultipleRequest]::new()
$executeBPFReq.Requests = [Microsoft.Xrm.Sdk.OrganizationRequestCollection]::new()
$executeBPFReq.Settings = $executeSettings

Loop through the $group records.

foreach($record in $group){
"Perform your operation."
}

Getting the Active Position Stage Name.

$record=Get-CrmRecord contact $record.contactid * -IncludeNullValue
$parentReference = New-CrmEntityReference -EntityLogicalName "Add Your Flow Name" -Id $record.contactid    
$patientflow1= New-CrmRecord -conn $CRMConnection -EntityLogicalName "Add Your Flow Name" -Fields @{"bpf_contactid"=$parentReference}
$retrieveProcessInstancesRequest = [Microsoft.Crm.Sdk.Messages.RetrieveProcessInstancesRequest]::new()
$retrieveProcessInstancesRequest.EntityId = $record.contactid
$retrieveProcessInstancesRequest.EntityLogicalName = "contact"
$retrieveProcessInstancesResponse = $CRMConnection.Execute($retrieveProcessInstancesRequest)
$processInstance = $retrieveProcessInstancesResponse.Results.Values.Entities | Select -First 1
$retrieveActivePathRequest = [Microsoft.Crm.Sdk.Messages.RetrieveActivePathRequest]::new()
$retrieveActivePathRequest.ProcessInstanceId = $processInstance.Id
$retrieveActivePathResponse = $CRMConnection.Execute($retrieveActivePathRequest)
$activeStagePosition=""

for($i = 0; $i -lt $retrieveActivePathResponse.ProcessStages.Entities.Count; ++$i) { 
	if($retrieveActivePathResponse.ProcessStages.Entities[$i].Attributes["processstageid"].ToString() -eq $processInstance.Attributes["processstageid"].ToString()) {
					$activeStageName = $retrieveActivePathResponse.ProcessStages.Entities[$i].Attributes["stagename"].ToString()
					$activeStagePosition = $i
				}
			}
$processInstance["name"]= $processInstance["name"] -replace " ",""

Move the Stage by updating the "processstage".

$newStageId = $retrieveActivePathResponse.ProcessStages.Entities[$desiredStagePosition].Attributes["processstageid"]
$entity = [Microsoft.Xrm.Sdk.Entity]::new($processInstance["name"].ToLower(), $processInstance.Id)
$entity.Attributes.Add("activestageid", (New-CrmEntityReference -Id $newStageId -EntityLogicalName "processstage"))

Setting the Stage to Finish.

$request = New-Object -TypeName Microsoft.Crm.Sdk.Messages.SetStateRequest
$ProcessSetToFinish = New-Object Microsoft.Xrm.Sdk.EntityReference(""Add Your Flow Name"", $processInstance.Id);
$request.EntityMoniker = $ProcessSetToFinish
$request.State = New-Object -TypeName Microsoft.Xrm.Sdk.OptionSetValue(1)
$request.Status = New-Object -TypeName Microsoft.Xrm.Sdk.OptionSetValue(2)

Adding Update Request and Set Stage Request to Execute Request.

$updateReq = [Microsoft.Xrm.Sdk.Messages.UpdateRequest]::new()
$updateReq.Target = $entity
$executeReq.Requests.Add($updateReq)

$setBPFReq = [Microsoft.Xrm.Sdk.Messages.SetStateRequest]::new()
$setBPFReq.Target = $request
$executeBPFReq.Requests.Add($setBPFReq)

Executing the final Batch request.

$null = $conn.Execute($executeReq)
$null = $conn.Execute($executeBPFReq)

Final Code View

$varFetch = @"
<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
<entity name="contact">
<attribute name="fullname" />
<attribute name="contactid" />
<order descending="true" attribute="modifiedon" />
<filter type="and">
  <condition attribute="statecode" operator="eq" value="0" />
</filter>
	<link-entity name="Add Your Flow Name" from="bpf_contactid" to="contactid" link-type="outer" alias="af" />
<filter type="and">
  <condition entityname="af" attribute="bpf_contactid" operator="null" />
</filter>
</entity>
</fetch>
"@ 

$counter = [pscustomobject] @{ Value = 0 }
$groupSize = 100
$records = (Get-CrmRecordsByFetch -conn $conn -Fetch $varFetch -AllRows).CrmRecords

$records | Group-Object -Property {  [math]::Floor($counter.Value++ / $groupSize) } | 
Foreach {
	Start-ThreadJob -ArgumentList @($conn, $_.Group){
		param($conn, $group)

		$executeReq = [Microsoft.Xrm.Sdk.Messages.ExecuteMultipleRequest]::new()
        $executeSettings = [Microsoft.Xrm.Sdk.ExecuteMultipleSettings]::new()
        $executeSettings.ContinueOnError = $true
        $executeSettings.ReturnResponses = $false
        $executeReq.Requests = [Microsoft.Xrm.Sdk.OrganizationRequestCollection]::new()
        $executeReq.Settings = $executeSettings

		$executeBPFReq = [Microsoft.Xrm.Sdk.Messages.ExecuteMultipleRequest]::new()
        $executeBPFReq.Requests = [Microsoft.Xrm.Sdk.OrganizationRequestCollection]::new()
        $executeBPFReq.Settings = $executeSettings

		foreach($record in $group){
			$record=Get-CrmRecord contact $record.contactid * -IncludeNullValue
			$parentReference = New-CrmEntityReference -EntityLogicalName "Add Your Flow Name" -Id $record.contactid    
			$patientflow1= New-CrmRecord -conn $CRMConnection -EntityLogicalName "Add Your Flow Name" -Fields @{"bpf_contactid"=$parentReference}
			$retrieveProcessInstancesRequest = [Microsoft.Crm.Sdk.Messages.RetrieveProcessInstancesRequest]::new()
			$retrieveProcessInstancesRequest.EntityId = $record.contactid
			$retrieveProcessInstancesRequest.EntityLogicalName = "contact"
			$retrieveProcessInstancesResponse = $CRMConnection.Execute($retrieveProcessInstancesRequest)
			$processInstance = $retrieveProcessInstancesResponse.Results.Values.Entities | Select -First 1
			$retrieveActivePathRequest = [Microsoft.Crm.Sdk.Messages.RetrieveActivePathRequest]::new()
			$retrieveActivePathRequest.ProcessInstanceId = $processInstance.Id
			$retrieveActivePathResponse = $CRMConnection.Execute($retrieveActivePathRequest)

			$activeStagePosition=""

			for($i = 0; $i -lt $retrieveActivePathResponse.ProcessStages.Entities.Count; ++$i) { 

				if($retrieveActivePathResponse.ProcessStages.Entities[$i].Attributes["processstageid"].ToString() -eq $processInstance.Attributes["processstageid"].ToString()) {
					$activeStageName = $retrieveActivePathResponse.ProcessStages.Entities[$i].Attributes["stagename"].ToString()
					$activeStagePosition = $i
				}
			}

			$processInstance["name"]= $processInstance["name"] -replace " ",""

			$newStageId = $retrieveActivePathResponse.ProcessStages.Entities[$desiredStagePosition].Attributes["processstageid"]
			$entity = [Microsoft.Xrm.Sdk.Entity]::new($processInstance["name"].ToLower(), $processInstance.Id)
			$entity.Attributes.Add("activestageid", (New-CrmEntityReference -Id $newStageId -EntityLogicalName "processstage"))
			
			$request = New-Object -TypeName Microsoft.Crm.Sdk.Messages.SetStateRequest
			$ProcessSetToFinish = New-Object Microsoft.Xrm.Sdk.EntityReference(""Add Your Flow Name"", $processInstance.Id);
			$request.EntityMoniker = $ProcessSetToFinish
			$request.State = New-Object -TypeName Microsoft.Xrm.Sdk.OptionSetValue(1)
			$request.Status = New-Object -TypeName Microsoft.Xrm.Sdk.OptionSetValue(2)

			$updateReq = [Microsoft.Xrm.Sdk.Messages.UpdateRequest]::new()
			$updateReq.Target = $entity
			$executeReq.Requests.Add($updateReq)

			$setBPFReq = [Microsoft.Xrm.Sdk.Messages.SetStateRequest]::new()
			$setBPFReq.Target = $request
			$executeBPFReq.Requests.Add($setBPFReq)
		}

		$null = $conn.Execute($executeReq)
		$null = $conn.Execute($executeBPFReq)
	}
}