Skip to content

Commit 0ca8691

Browse files
author
Matt Wilkinson
committed
Rework Import-CrmSolutionAsync to use the progress checking logic of Import-CrmSolution, and use $conn.ImportSolutionToCrmAsync call to get both async operation ID and import ID, rather than construct an async operation manually and not get the import ID
1 parent 42b033d commit 0ca8691

File tree

1 file changed

+176
-82
lines changed

1 file changed

+176
-82
lines changed

Microsoft.Xrm.Data.PowerShell/Microsoft.Xrm.Data.PowerShell.psm1

Lines changed: 176 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1970,15 +1970,13 @@ function Import-CrmSolutionAsync {
19701970
)
19711971
$conn = VerifyCrmConnectionParam -conn $conn -pipelineValue ($PSBoundParameters.ContainsKey('conn'))
19721972
$importId = [guid]::Empty
1973-
$asyncResponse = $null
19741973
try
19751974
{
19761975
if (!(Test-Path $SolutionFilePath))
19771976
{
19781977
throw [System.IO.FileNotFoundException] "$SolutionFilePath not found."
19791978
}
1980-
1981-
Write-Verbose "Importing solution file $SolutionFilePath into: $($conn.CrmConnectOrgUriActual)"
1979+
Write-Verbose "Importing solution file $SolutionFilePath into: $($conn.CrmConnectOrgUriActual)"
19821980
Write-Verbose "OverwriteCustomizations: $OverwriteUnManagedCustomizations"
19831981
Write-Verbose "SkipDependancyCheck: $SkipDependancyOnProductUpdateCheckOnInstall"
19841982
Write-Verbose "ImportAsHoldingSolution: $ImportAsHoldingSolution"
@@ -1995,100 +1993,196 @@ function Import-CrmSolutionAsync {
19951993
$PublishChanges = $false;
19961994
}
19971995

1998-
$data = [System.IO.File]::ReadAllBytes($SolutionFilePath)
1999-
2000-
$request = New-Object Microsoft.Crm.Sdk.Messages.ImportSolutionRequest
2001-
$request.CustomizationFile = $data
2002-
$request.PublishWorkflows = $ActivateWorkflows
2003-
$request.OverwriteUnmanagedCustomizations = $OverwriteUnManagedCustomizations
2004-
$request.SkipProductUpdateDependencies = $SkipDependancyOnProductUpdateCheckOnInstall
2005-
$request.HoldingSolution = $ImportAsHoldingSolution
2006-
2007-
$asyncRequest = New-Object Microsoft.Xrm.Sdk.Messages.ExecuteAsyncRequest
2008-
$asyncRequest.Request = $request;
1996+
Write-Verbose "Importing solution. This process can take a while..."
1997+
Write-Progress -Id 1 -Activity "Importing Solution" -CurrentOperation "Initiating import" -PercentComplete -1
1998+
$asyncOperationId = $conn.ImportSolutionToCrmAsync($SolutionFilePath, [ref]$importId, $ActivatePlugIns,
1999+
$OverwriteUnManagedCustomizations, $SkipDependancyOnProductUpdateCheckOnInstall,$ImportAsHoldingSolution)
2000+
Write-Verbose "ImportId: $importId"
2001+
Write-Verbose "AsyncOperationId: $asyncOperationId"
2002+
if ($importId -eq [guid]::Empty) {
2003+
throw LastCrmConnectorException($conn)
2004+
}
20092005

2010-
Write-Verbose "Importing solution. This process can take a while..."
2011-
try
2012-
{
2013-
$asyncResponse = ($conn.ExecuteCrmOrganizationRequest($asyncRequest, "AsyncImportRequest") -as [Microsoft.Xrm.Sdk.Messages.ExecuteAsyncResponse])
2014-
$importId = $asyncResponse.AsyncJobId
2015-
2016-
Write-Verbose "Async Operation ID (asyncoperationid): $importId"
2017-
if($importId -eq $null -or $importId -eq [Guid]::Empty)
2018-
{
2019-
throw "Import request failed for Async Operation {$importId}"
2020-
}
2021-
#if the caller wants to get the ID and does NOT want to wait
2022-
if($BlockUntilImportComplete -eq $false){
2023-
return $asyncResponse;
2024-
}
2025-
}
2026-
catch
2027-
{
2028-
throw LastCrmConnectorException($conn)
2029-
}
2006+
if (-not $BlockUntilImportComplete) {
2007+
try {
2008+
$asyncOperationRecords = (Get-CrmRecords -conn $conn -EntityLogicalName asyncoperation -FilterAttribute asyncoperationid -FilterOperator eq -FilterValue $asyncOperationId -Fields *).CrmRecords
2009+
return if ($asyncOperationRecords.Count -gt 0) { $asyncOperationRecords[0] } else { $null }
2010+
} catch {
2011+
if($transientFailureCount > 5){
2012+
Write-Error "Async operation status check FAILED 5 times this could be due to a bug where the service returns a 401. Throwing lastException:";
2013+
throw $conn.LastCrmException
2014+
}
2015+
Write-Verbose "Async operation status check FAILED this could be due to a bug where the service returns a 401. We'll allow up to 5 failures before aborting.";
2016+
$transientFailureCount++;
2017+
}
2018+
}
20302019

2031-
$pollingStart = Get-Date
20322020
$isProcessing = $true
2033-
$secondsSpentPolling = 0
2021+
$timeSpentPolling = [System.Diagnostics.Stopwatch]::StartNew()
2022+
$timeout = New-TimeSpan -Seconds $MaxWaitTimeInSeconds
2023+
$pollingDelaySeconds = 5
2024+
$TopPrevProcPercent = [double]0
2025+
$isProcPercentReduced = $false
2026+
#this is for a bug where the service will throw a 401 on retrieve of importjob during an import under certain conditions
20342027
$transientFailureCount = 0;
2035-
Write-Verbose "Solution import requested, waiting on completion of Async Operation {$importId}..."
2036-
2028+
Write-Host "Import of file completed, waiting on completion of importId: $importId"
20372029
try{
2038-
while(($isProcessing -and $secondsSpentPolling -lt $MaxWaitTimeInSeconds) -or ($isProcessing -and $MaxWaitTimeInSeconds -eq -1)){
2039-
#delay
2040-
Start-Sleep -Seconds $PollingDelayInSeconds
2041-
2042-
#check the import job for success/fail/inProgress
2043-
try{
2044-
$import = Get-CrmRecord -conn $conn -EntityLogicalName asyncoperation -Id $importId -Fields statuscode,completedon,friendlymessage
2030+
Write-Progress -Id 1 -Activity "Importing Solution" -CurrentOperation "Solution file uploaded" -PercentComplete -1
2031+
while($isProcessing -and $timeSpentPolling.Elapsed -lt $timeout){
2032+
2033+
try {
2034+
$asyncOperationRecords = (Get-CrmRecords -conn $conn -EntityLogicalName asyncoperation -FilterAttribute asyncoperationid -FilterOperator eq -FilterValue $asyncOperationId -Fields friendlymessage,statuscode,message).CrmRecords
2035+
$asyncOperation = if ($asyncOperationRecords.Count -gt 0) { $asyncOperationRecords[0] } else { $null }
20452036
} catch {
2037+
if($transientFailureCount > 5){
2038+
Write-Error "Async operation status check FAILED 5 times this could be due to a bug where the service returns a 401. Throwing lastException:";
2039+
throw $conn.LastCrmException
2040+
}
2041+
Write-Verbose "Async operation status check FAILED this could be due to a bug where the service returns a 401. We'll allow up to 5 failures before aborting.";
20462042
$transientFailureCount++;
2047-
Write-Verbose "Import Job status check did not succeed: $($_.Exception)"
20482043
}
20492044

2050-
$statuscode = $import.statuscode_Property.value.Value;
2051-
2052-
#Check for import completion - https://msdn.microsoft.com/en-us/library/gg309288.aspx
2053-
if($statuscode -lt 30){
2054-
$secondsSpentPolling = ([Int]((Get-Date) - $pollingStart).TotalSeconds)
2055-
Write-Verbose "Executing for $($secondsSPentPolling.ToString("000"))/$MaxWaitTimeInSeconds seconds | Status: $($import.statuscode) ($statuscode)"
2056-
}
2057-
elseif($statuscode -eq 31 -or $statuscode -eq 32 ){
2058-
$isProcessing = $false
2059-
throw "Status: $($import.statuscode) ($statuscode) | Operation has been either cancelled or has failed for Async Operation {$importId}.`n$($import.friendlymessage)"
2060-
break;
2061-
}
2062-
elseif($statuscode -eq 30){
2063-
$isProcessing = $false
2064-
Write-Verbose "Processing Completed at: $($import.completedon)"
2065-
if($PublishChanges){
2066-
Write-Verbose "SUCCESS: PublishChanges set, executing: Publish-CrmAllCustomization using the same connection."
2067-
Publish-CrmAllCustomization -conn $conn
2068-
return $import
2069-
}
2070-
else{
2071-
Write-Verbose "SUCCESS: Import Complete, don't forget to publish customizations."
2072-
return $import
2073-
}
2074-
break;
2075-
}
2076-
}
2077-
2078-
#User provided timeout and exit function with an error
2079-
if($secondsSpentPolling -gt $MaxWaitTimeInSeconds){
2080-
Write-Warning "Import-CrmSolutionAsync exited due to exceeding the maximum timeout of $MaxWaitTimeInSeconds. The import will continue in CRM async until it either succeeds or fails."
2045+
if ($asyncOperation.statuscode -eq "Failed") {
2046+
Write-Error (Coalesce $asyncOperation.friendlyMessage $asyncOperation.message $asyncOperation.statuscode)
2047+
throw $asyncOperation.message
2048+
} elseif ($asyncOperation.statuscode -eq "Canceled") {
2049+
throw "Import cancelled"
2050+
} elseif ($asyncOperation.statuscode -ne "In Progress" -and $asyncOperation -ne "Succeeded") {
2051+
$currentOperation = Coalesce $asyncOperation.friendlyMessage $asyncOperation.statuscode
2052+
Write-Progress -Id 1 -Activity "Importing $($import.solutionname)" -CurrentOperation "Import processing - $currentOperation" -PercentComplete -1
2053+
} else { # In Progress or Succeeded
2054+
2055+
try {
2056+
$importRecords = (Get-CrmRecords -conn $conn -EntityLogicalName importjob -FilterAttribute importjobid -FilterOperator eq -FilterValue $importId -Fields data,completedon,startedon,progress).CrmRecords
2057+
$import = if ($importRecords.Count -gt 0) { $importRecords[0] } else { $null }
2058+
} catch {
2059+
if($transientFailureCount > 5){
2060+
Write-Error "Import Job status check FAILED 5 times this could be due to a bug where the service returns a 401. Throwing lastException:";
2061+
throw $conn.LastCrmException
2062+
}
2063+
Write-Verbose "Import Job status check FAILED this could be due to a bug where the service returns a 401. We'll allow up to 5 failures before aborting.";
2064+
$transientFailureCount++;
2065+
}
2066+
2067+
2068+
$importManifest = ([xml]($import).data).importexportxml.solutionManifests.solutionManifest
2069+
$ProcPercent = [double](Coalesce $import.progress "0")
2070+
2071+
Write-Progress -Id 1 -Activity "Importing $($import.solutionname)" -CurrentOperation "Import processing" -PercentComplete $ProcPercent
2072+
2073+
#check if processing percentage reduced at any given time
2074+
if($TopPrevProcPercent -gt $ProcPercent)
2075+
{
2076+
$isProcPercentReduced = $true
2077+
Write-Verbose "Processing is reversing... import will fail."
2078+
} else {
2079+
$TopPrevProcPercent = $ProcPercent
2080+
}
2081+
2082+
#Check for import completion
2083+
if($import.completedon -eq $null -and $importManifest.result.result -ne "success"){
2084+
$isProcessing = $true
2085+
}
2086+
else {
2087+
Write-Verbose "Processing Completed at: $($import.completedon) with ImportJob%: $ProcPercent"
2088+
Write-Verbose "Import Manifest Result: $($importManifest.result.result) with ImportJob%: $ProcPercent"
2089+
2090+
$solutionImportResults = Select-Xml -Xml ([xml]$import.data) -XPath "//result"
2091+
$anyFailuresInImport = $false;
2092+
$allErrorText = "";
2093+
2094+
foreach($solutionImportResult in $solutionImportResults)
2095+
{
2096+
try{
2097+
$resultParent = ""
2098+
$itemResult = ""
2099+
$resultParent = $($solutionImportResult.Node.ParentNode.ParentNode.Name)
2100+
$itemResult = $($solutionImportResult.Node.result)
2101+
}catch{}
2102+
2103+
Write-Verbose "Item:$resultParent result: $itemResult" # write each item result in result data
2104+
2105+
if ($solutionImportResult.Node.result -ne "success")
2106+
{
2107+
# if any error in result print more error details
2108+
try{
2109+
$errorCode = ""
2110+
$errorText = ""
2111+
$moreErrorDetails = ""
2112+
$errorCode = $($solutionImportResult.Node.errorcode)
2113+
$errorText = $($solutionImportResult.Node.errortext)
2114+
$moreErrorDetails = $solutionImportResult.Node.parameters.InnerXml
2115+
2116+
}catch{}
2117+
2118+
Write-Verbose "errorcode: $errorCode errortext: $errorText more details: $moreErrorDetails"
2119+
if ($solutionImportResult.Node.result -eq "failure") # Fail only on errors, not on warnings
2120+
{
2121+
$anyFailuresInImport = $true; # mark if any failures in solution import
2122+
$allErrorText = $allErrorText + ";" + $errorText;
2123+
}
2124+
}
2125+
}
2126+
if(-not $isProcPercentReduced -and $importManifest.result.result -eq "success" -and (-not $anyFailuresInImport))
2127+
{
2128+
Write-Verbose "Setting to 100% since all results are success"
2129+
$ProcPercent = 100.0
2130+
}
2131+
$isProcessing = $false
2132+
break
2133+
}
2134+
}
2135+
2136+
Write-Progress -Id 2 -ParentId 1 -Activity "Maximum Wait Time ($timeout)" -CurrentOperation "Remaining Time: $($timeout - $timeSpentPolling.Elapsed)" -PercentComplete ([double]($timeout - $timeSpentPolling.Elapsed).TotalSeconds * 100 / $timeout.TotalSeconds)
2137+
Start-Sleep -Seconds $pollingDelaySeconds
20812138
}
2082-
#at this point we appear to have imported successfully
2083-
return $asyncResponse;
20842139
} Catch {
2085-
throw "AsyncOperation with ID: $importId has encountered an exception: $_"
2140+
Write-Error "ImportJob with ID: $importId has encountered an exception: $_ "
2141+
} Finally{
2142+
$ProcPercent = ([double](Coalesce $ProcPercent 0))
2143+
Write-Progress -Id 2 -Completed -Activity "_"
2144+
$timeSpentPolling.Stop()
2145+
}
2146+
#User provided timeout and exit function with an error
2147+
if($timeSpentPolling.Elapsed -gt $timeout){
2148+
throw "Import-CrmSolutionAsync halted due to exceeding the maximum timeout of $timeout."
20862149
}
2150+
#detect a failure by a failure result OR the percent being less than 100%
2151+
if(($importManifest.result.result -eq "failure") -or ($ProcPercent -lt 100) -or $anyFailuresInImport) #Must look at %age instead of this result as the result is usually wrong!
2152+
{
2153+
Write-Verbose "Import result: failed - job with ID: $importId failed at $ProcPercent complete."
2154+
throw $allErrorText
2155+
}
2156+
else
2157+
{
2158+
#at this point we appear to have imported successfully
2159+
$managedsolution = $importManifest.Managed
2160+
if($managedsolution -ne 1)
2161+
{
2162+
if($PublishChanges){
2163+
Write-Verbose "PublishChanges set, executing: Publish-CrmAllCustomization using the same connection."
2164+
Publish-CrmAllCustomization -conn $conn
2165+
Write-Progress -Id 1 -Activity "Importing $($import.solutionname)" -CurrentOperation "Publishing Customizations" -PercentComplete 100
2166+
}
2167+
else{
2168+
Write-Output "Import Complete, don't forget to publish customizations."
2169+
}
2170+
}
2171+
else{
2172+
#managed
2173+
Write-Output "Import of managed solution complete."
2174+
}
2175+
}
20872176
}
20882177
catch
20892178
{
2090-
throw $_.Exception
2091-
}
2179+
Write-Error $_.Exception
2180+
}
2181+
finally
2182+
{
2183+
Write-Progress -Id 1 -Completed -Activity "_"
2184+
Write-Progress -Id 2 -Completed -Activity "_"
2185+
}
20922186
}
20932187

20942188
#ImportSolutionToCrm

0 commit comments

Comments
 (0)