Skip to content

Commit 29ae645

Browse files
authored
[Issue #406] Delete and Promote request (Apply holding solution upgrade) (#407)
* Added Merge-HoldingSolution function + Fixed log issues in Import-CrmSolutionAsync function * Implemented requested changes from PR review * Co-authored-by: Rui Sousa <[email protected]>
1 parent 774575f commit 29ae645

File tree

1 file changed

+127
-25
lines changed

1 file changed

+127
-25
lines changed

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

Lines changed: 127 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1851,7 +1851,7 @@ function Get-CrmEntityOptionSet{
18511851
}
18521852

18531853
#ImportSolutionToCrmAsync
1854-
function Import-CrmSolutionAsync{
1854+
function Import-CrmSolutionAsync {
18551855
# .ExternalHelp Microsoft.Xrm.Data.PowerShell.Help.xml
18561856
[CmdletBinding()]
18571857
PARAM(
@@ -1872,7 +1872,9 @@ function Import-CrmSolutionAsync{
18721872
[parameter(Mandatory=$false, Position=7)]
18731873
[switch]$ImportAsHoldingSolution,
18741874
[parameter(Mandatory=$false, Position=8)]
1875-
[switch]$BlockUntilImportComplete
1875+
[switch]$BlockUntilImportComplete,
1876+
[parameter(Mandatory=$false, Position=9)]
1877+
[int64]$PollingDelayInSeconds = 5
18761878
)
18771879
$conn = VerifyCrmConnectionParam $conn
18781880
$importId = [guid]::Empty
@@ -1884,7 +1886,7 @@ function Import-CrmSolutionAsync{
18841886
throw [System.IO.FileNotFoundException] "$SolutionFilePath not found."
18851887
}
18861888

1887-
Write-Output "Importing solution file $SolutionFilePath into: $($conn.CrmConnectOrgUriActual)"
1889+
Write-Verbose "Importing solution file $SolutionFilePath into: $($conn.CrmConnectOrgUriActual)"
18881890
Write-Verbose "OverwriteCustomizations: $OverwriteUnManagedCustomizations"
18891891
Write-Verbose "SkipDependancyCheck: $SkipDependancyOnProductUpdateCheckOnInstall"
18901892
Write-Verbose "ImportAsHoldingSolution: $ImportAsHoldingSolution"
@@ -1913,16 +1915,16 @@ function Import-CrmSolutionAsync{
19131915
$asyncRequest = New-Object Microsoft.Xrm.Sdk.Messages.ExecuteAsyncRequest
19141916
$asyncRequest.Request = $request;
19151917

1916-
Write-Verbose "ExecuteCrmOrganizationRequest with ExecuteAsyncRequest containing ImportSolutionRequest() this process can take a while..."
1918+
Write-Verbose "Importing solution. This process can take a while..."
19171919
try
19181920
{
19191921
$asyncResponse = ($conn.ExecuteCrmOrganizationRequest($asyncRequest, "AsyncImportRequest") -as [Microsoft.Xrm.Sdk.Messages.ExecuteAsyncResponse])
19201922
$importId = $asyncResponse.AsyncJobId
19211923

1922-
Write-Verbose "ImportId (asyncoperationid): $importId"
1924+
Write-Verbose "Async Operation ID (asyncoperationid): $importId"
19231925
if($importId -eq $null -or $importId -eq [Guid]::Empty)
19241926
{
1925-
throw "Import request failed, asyncoperationid is: $importId"
1927+
throw "Import request failed for Async Operation {$importId}"
19261928
}
19271929
#if the caller wants to get the ID and does NOT want to wait
19281930
if($BlockUntilImportComplete -eq $false){
@@ -1932,52 +1934,55 @@ function Import-CrmSolutionAsync{
19321934
catch
19331935
{
19341936
throw LastCrmConnectorException($conn)
1935-
}
1937+
}
1938+
19361939
$pollingStart = Get-Date
19371940
$isProcessing = $true
19381941
$secondsSpentPolling = 0
1939-
$pollingDelaySeconds = 5
19401942
$transientFailureCount = 0;
1941-
Write-Verbose "Import of file completed, waiting on completion of AsyncOperationId: $importId"
1943+
Write-Verbose "Solution import requested, waiting on completion of Async Operation {$importId}..."
19421944

19431945
try{
19441946
while(($isProcessing -and $secondsSpentPolling -lt $MaxWaitTimeInSeconds) -or ($isProcessing -and $MaxWaitTimeInSeconds -eq -1)){
19451947
#delay
1946-
Start-Sleep -Seconds $pollingDelaySeconds
1948+
Start-Sleep -Seconds $PollingDelayInSeconds
1949+
19471950
#check the import job for success/fail/inProgress
19481951
try{
1949-
$import = Get-CrmRecord -conn $conn -EntityLogicalName asyncoperation -Id $importId -Fields statuscode
1952+
$import = Get-CrmRecord -conn $conn -EntityLogicalName asyncoperation -Id $importId -Fields statuscode,completedon,friendlymessage
19501953
} catch {
19511954
$transientFailureCount++;
19521955
Write-Verbose "Import Job status check did not succeed: $($_.Exception)"
19531956
}
1954-
$status = $import.statuscode_Property.value.Value;
1957+
1958+
$statuscode = $import.statuscode_Property.value.Value;
1959+
19551960
#Check for import completion - https://msdn.microsoft.com/en-us/library/gg309288.aspx
1956-
if($status -lt 30){
1957-
$isProcessing = $true
1961+
if($statuscode -lt 30){
19581962
$secondsSpentPolling = ([Int]((Get-Date) - $pollingStart).TotalSeconds)
1959-
Write-Output "$($secondsSPentPolling.ToString("000")) sec of: $MaxWaitTimeInSeconds - ImportStatus: $($import.statuscode)"
1963+
Write-Verbose "Executing for $($secondsSPentPolling.ToString("000"))/$MaxWaitTimeInSeconds seconds | Status: $($import.statuscode) ($statuscode)"
19601964
}
1961-
elseif($status -eq 31 -or $status -eq 32 ){
1965+
elseif($statuscode -eq 31 -or $statuscode -eq 32 ){
19621966
$isProcessing = $false
1963-
throw "$($import.statuscode) - AsyncOperation with Id: $importId has been either cancelled or has failed."
1967+
throw "Status: $($import.statuscode) ($statuscode) | Operation has been either cancelled or has failed for Async Operation {$importId}.`n$($import.friendlymessage)"
19641968
break;
19651969
}
1966-
elseif($status -eq 30){
1970+
elseif($statuscode -eq 30){
19671971
$isProcessing = $false
1968-
Write-Verbose "Processing Completed at: $($import.completedon)"
1972+
Write-Verbose "Processing Completed at: $($import.completedon)"
19691973
if($PublishChanges){
1970-
Write-Verbose "PublishChanges set, executing: Publish-CrmAllCustomization using the same connection."
1974+
Write-Verbose "SUCCESS: PublishChanges set, executing: Publish-CrmAllCustomization using the same connection."
19711975
Publish-CrmAllCustomization -conn $conn
1972-
return $asyncResponse
1976+
return $import
19731977
}
19741978
else{
1975-
Write-Output "Import Complete, don't forget to publish customizations."
1976-
return $asyncResponse
1979+
Write-Verbose "SUCCESS: Import Complete, don't forget to publish customizations."
1980+
return $import
19771981
}
19781982
break;
19791983
}
19801984
}
1985+
19811986
#User provided timeout and exit function with an error
19821987
if($secondsSpentPolling -gt $MaxWaitTimeInSeconds){
19831988
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."
@@ -2026,7 +2031,7 @@ function Import-CrmSolution{
20262031
{
20272032
throw [System.IO.FileNotFoundException] "$SolutionFilePath not found."
20282033
}
2029-
Write-Host "Importing solution file $SolutionFilePath into: $($conn.CrmConnectOrgUriActual)"
2034+
Write-Verbose "Importing solution file $SolutionFilePath into: $($conn.CrmConnectOrgUriActual)"
20302035
Write-Verbose "OverwriteCustomizations: $OverwriteUnManagedCustomizations"
20312036
Write-Verbose "SkipDependancyCheck: $SkipDependancyOnProductUpdateCheckOnInstall"
20322037
Write-Verbose "ImportAsHoldingSolution: $ImportAsHoldingSolution"
@@ -2189,6 +2194,103 @@ function Import-CrmSolution{
21892194
}
21902195
}
21912196

2197+
#MergeHoldingSolution
2198+
function Merge-HoldingSolutionAsync {
2199+
[CmdletBinding()]
2200+
PARAM(
2201+
[parameter(Mandatory=$true)]
2202+
[Microsoft.Xrm.Tooling.Connector.CrmServiceClient]$conn,
2203+
[parameter(Mandatory=$true, Position=1)]
2204+
[string]$CrmSolutionName,
2205+
[parameter(Mandatory=$false, Position=2)]
2206+
[int64]$TimeoutInSeconds,
2207+
[parameter(Mandatory=$false, Position=3)]
2208+
[switch]$BlockAsync,
2209+
[parameter(Mandatory=$false, Position=4)]
2210+
[int64]$PollingDelayInSeconds = 5
2211+
)
2212+
2213+
$conn = VerifyCrmConnectionParam $conn
2214+
2215+
$request = New-Object Microsoft.Crm.Sdk.Messages.DeleteAndPromoteRequest
2216+
$request.UniqueName = $CrmSolutionName
2217+
2218+
$asyncRequest = New-Object Microsoft.Xrm.Sdk.Messages.ExecuteAsyncRequest
2219+
$asyncRequest.Request = $request;
2220+
2221+
try
2222+
{
2223+
Write-Verbose "Applying async solution upgrade for solution '$CrmSolutionName'"
2224+
$asyncResponse = ($conn.ExecuteCrmOrganizationRequest($asyncRequest, "AsyncImportRequest") -as [Microsoft.Xrm.Sdk.Messages.ExecuteAsyncResponse])
2225+
$asyncOperationId = $asyncResponse.AsyncJobId
2226+
2227+
Write-Verbose "Async Operation ID (asyncoperationid): $asyncOperationId"
2228+
if(($asyncOperationId -eq $null) -or ($asyncOperationId -eq [Guid]::Empty))
2229+
{
2230+
throw "Async request failed!"
2231+
}
2232+
2233+
#if the caller wants to get the ID and does NOT want to wait
2234+
if($BlockAsync -eq $false){
2235+
return $asyncResponse;
2236+
}
2237+
}
2238+
catch
2239+
{
2240+
throw LastCrmConnectorException($conn)
2241+
}
2242+
2243+
$pollingStart = Get-Date
2244+
$isProcessing = $true
2245+
$secondsSpentPolling = 0
2246+
$transientFailureCount = 0;
2247+
Write-Verbose "Async Delete and Promote requested, waiting on completion..."
2248+
2249+
try{
2250+
while(($isProcessing -and $secondsSpentPolling -lt $TimeoutInSeconds) -or ($isProcessing -and $TimeoutInSeconds -eq -1)) {
2251+
#delay
2252+
Start-Sleep -Seconds $PollingDelayInSeconds
2253+
2254+
#check the async job for success/fail/inProgress
2255+
try{
2256+
$asyncOperation = Get-CrmRecord -conn $conn -EntityLogicalName asyncoperation -Id $asyncOperationId -Fields statuscode,completedon,friendlymessage
2257+
} catch {
2258+
$transientFailureCount++;
2259+
Write-Verbose "Async job status check did not succeed: $($_.Exception)"
2260+
}
2261+
2262+
$statuscode = $asyncOperation.statuscode_Property.value.Value;
2263+
2264+
#Check for import completion - https://msdn.microsoft.com/en-us/library/gg309288.aspx
2265+
if($statuscode -lt 30){
2266+
$secondsSpentPolling = ([Int]((Get-Date) - $pollingStart).TotalSeconds)
2267+
Write-Verbose "Executing for $($secondsSPentPolling.ToString("000"))/$TimeoutInSeconds seconds | Status: $($asyncOperation.statuscode) ($statuscode)"
2268+
}
2269+
elseif($statuscode -eq 31 -or $statuscode -eq 32 ){
2270+
$isProcessing = $false
2271+
throw "Status: $($asyncOperation.statuscode) ($statuscode) | Operation has been either cancelled or has failed.`n$($asyncOperation.friendlymessage)"
2272+
break;
2273+
}
2274+
elseif($statuscode -eq 30){
2275+
$isProcessing = $false
2276+
Write-Verbose "Processing Completed at: $($asyncOperation.completedon)"
2277+
Write-Verbose "SUCCESS: Delete and Promote successfully completed."
2278+
return $asyncOperation
2279+
}
2280+
}
2281+
2282+
#User provided timeout and exit function with an error
2283+
if($secondsSpentPolling -gt $TimeoutInSeconds){
2284+
Write-Warning "Delete and Promote request exited due to exceeding the maximum timeout of $TimeoutInSeconds. The import will continue in CRM asynchronously until it either succeeds or fails."
2285+
}
2286+
2287+
#at this point request appears to have succeeded
2288+
return $asyncResponse;
2289+
} catch {
2290+
throw "AsyncOperation with ID: $asyncOperationId has encountered an exception: $_"
2291+
}
2292+
}
2293+
21922294
#InstallSampleDataToCrm
21932295
function Add-CrmSampleData{
21942296
# .ExternalHelp Microsoft.Xrm.Data.PowerShell.Help.xml
@@ -5506,4 +5608,4 @@ function UnzipCrmRibbon {
55065608
$reader = $null
55075609
}
55085610
}
5509-
}
5611+
}

0 commit comments

Comments
 (0)