Scripts/SolutionDeploy.ps1
# # SolutionDeploy.ps1 # function Start-DeploySolution { Param( [string] [Parameter(Mandatory = $true)] $DeployServerUrl, [string] [Parameter(Mandatory = $true)] $UserName, [string] [Parameter(Mandatory = $false)] $Password = "", [string] [Parameter(Mandatory = $true)] $PipelinePath, [bool] [Parameter(Mandatory = $false)] $UseClientSecret = $false, [bool] [Parameter(Mandatory = $false)] $RunLocally = $false, [string] [Parameter(Mandatory = $false)] $EnvironmentName = $env:ENVIRONMENT_NAME ) ######################## SETUP . "$PSScriptRoot\..\Private\_SetupTools.ps1" Write-Host "Using Microsoft.PowerPlatform.DevOps version :" (Get-Module -Name Microsoft.PowerPlatform.DevOps -ListAvailable).Version Install-PAC if (!$RunLocally) { Install-ConfigMigrationModule Install-XrmModule Install-PowerAppsCheckerModule } else { Write-Host "Preparing local run" } function Import-Package { if ($UseClientSecret) { [string]$CrmConnectionString = "AuthType=ClientSecret;Url=$DeployServerUrl;ClientId=$UserName;ClientSecret=$Password" } else { [string]$CrmConnectionString = "AuthType=OAuth;Username=$UserName;Password=$Password;Url=$DeployServerUrl;AppId=51f81489-12ee-4a9e-aaae-a2591f45987d;RedirectUri=app://58145B91-0C36-4500-8554-080854F2AC97;LoginPrompt=never" } $Packages = Get-Content "$PipelinePath\deployPackages.json" | ConvertFrom-Json Write-Host "##[section] Creating CRM connection" $CRMConn = Get-CrmConnection -ConnectionString $CrmConnectionString -Verbose #-MaxCrmConnectionTimeOutMinutes 10 #Set-CrmConnectionTimeout -conn $CRMConn -TimeoutInSeconds 600 if ($false -eq $CRMConn.IsReady) { Write-Error "An error occurred: " $CRMConn.LastCrmError Write-Error $CRMConn.LastCrmException.Message Write-Error $CRMConn.LastCrmException.Source Write-Error $CRMConn.LastCrmException.StackTrace throw "Could not establish connection with server" } foreach ($package in $Packages) { $skipDeploy = $false $anyFailuresInImport = $false; $Deploy = $package.DeployTo | Where-Object { $_.EnvironmentName -eq $EnvironmentName } if ($null -ne $Deploy) { Write-Host "Deployment step manifest" Write-Host $Deploy $PSolution = $package.SolutionName Write-Host "##[group] Preparing Deployment for $PSolution" Write-Host "##[section] Preparing $PSolution Solution as $($Deploy.DeploymentType)" $fileToPack = "$($Deploy.EnvironmentName)_$($PSolution)_$($Deploy.DeploymentType).zip" $packageFolder = "dataverse_$($PSolution)" Write-Host "##[command] Packing Solution $PSolution" #Checking for Canvas App $canvasApps = Get-ChildItem -Path $PipelinePath\$PSolution\$packageFolder\CanvasApps\ -Directory -ErrorAction SilentlyContinue # pack canvas apps $canvasApps | ForEach-Object { Write-Host "Packing Canvas App $($_.name)"; & $env:APPDATA\Microsoft.PowerPlatform.DevOps\PACTools\tools\pac.exe canvas pack --sources $_.FullName --msapp "$($_.FullName).msapp" Remove-Item $_.FullName -Recurse -ErrorAction SilentlyContinue } if ($Deploy.DeploymentType.ToLower() -eq "unmanaged") { & $env:APPDATA\Microsoft.PowerPlatform.DevOps\PACTools\tools\pac.exe solution pack -f $PipelinePath\$PSolution\$packageFolder -z $PipelinePath\$PSolution\$fileToPack -p Unmanaged } else { & $env:APPDATA\Microsoft.PowerPlatform.DevOps\PACTools\tools\pac.exe solution pack -f $PipelinePath\$PSolution\$packageFolder -z $PipelinePath\$PSolution\$fileToPack -p Managed -same } Write-Host "##[section] Importing package" try { $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() $error.Clear() Write-Host "##[section] Deploying $($package.SolutionName) as $($Deploy.DeploymentType) to - $EnvironmentName" #Get Currently Deployed Solution Version Write-Host "Getting Current Solution Version from Target" $SolutionQuery = Get-CrmRecords -conn $CRMConn -EntityLogicalName solution -Fields 'solutionid', 'friendlyname', 'version', 'uniquename' -FilterAttribute uniquename -FilterOperator eq -FilterValue $($package.SolutionName) $Solution = $SolutionQuery.CrmRecords[0] if (!$Solution) { $deployAsHolding = $false; Write-Host "Solution not found in Target, Importing as New" } else { $SolutionVersion = $Solution.version Write-Host "Found: $SolutionVersion in $EnvironmentName" if ($null -ne $Deploy.DeployAsHolding) { [bool]$deployAsHolding = [System.Convert]::ToBoolean($Deploy.DeployAsHolding) } else { $deployAsHolding = $false } } Write-Host "Getting Version to be Deployed..." $deployingVersion = Get-Content -Path $PipelinePath\$PSolution\$PSolution.version Write-Host "Version to be deployed : $deployingVersion" if ($deployingVersion -le $SolutionVersion) { $skipDeploy = $true; Write-Host "Skipping Deployment as Target has same or newer" } ########################## IMPORT if (!$skipDeploy) { # Powerapps Solution Checker if ($Deploy.PowerAppsChecker -eq $true -and $UseClientSecret -eq $true) { Start-SolutionChecker -PipelinePath $PipelinePath -SolutionPath $PipelinePath\$PSolution\$fileToPack -SolutionName $PSolution -ClientId $UserName -ClientSecret $Password -TenantId "$($CRMConn.TenantId)" } else { Write-Host "Powerapps Checker not configured. Add PowerAppsChecker: True as a property in the DeployTo section for your Solution" } # PRE ACTION if ($Deploy.PreAction -eq $true) { if (Test-Path -Path $PipelinePath\$PSolution\Scripts\PreAction.ps1) { Write-Host "##[section] Execute Pre Action from $PipelinePath\$PSolution\Scripts" . $PipelinePath\$PSolution\Scripts\PreAction.ps1 -Conn $CRMConn -EnvironmentName $Deploy.EnvironmentName -Path "$PipelinePath\$PSolution\" } } $activatePlugIns = $true; $overwriteUnManagedCustomizations = $true; $skipDependancyOnProductUpdateCheckOnInstall = $true; $isInternalUpgrade = $false; Write-Host "Initiating Import and deployment to $($DeployServerUrl)" Write-Host "Import as Holding solution : $($deployAsHolding)" $importId = [guid]::Empty $result = $CRMConn.ImportSolutionToCrmAsync("$PipelinePath\$PSolution\$fileToPack", [ref]$importId, $activatePlugIns, $overwriteUnManagedCustomizations, $skipDependancyOnProductUpdateCheckOnInstall, $deployAsHolding, $isInternalUpgrade) Write-Host Async Operation ID: $result Write-Host Import Job ID: $importId $Retrycount = 0; # IMPORT do { try { Start-Sleep -Seconds 5 $operation = Get-CrmRecord -conn $CRMConn -EntityLogicalName asyncoperation -Id ($result) -Fields name, statuscode, friendlymessage, completedon, errorcode [int]$statuscode = $operation.statuscode_Property.value.Value; if ($statuscode -le 30) { $job = Get-CrmRecord -conn $CRMConn -EntityLogicalName importjob -Id ($importId) -Fields progress Write-Host "Polling Import for Solution: $($PSolution) : $($operation.statuscode) - $($job.progress)%" $anyFailuresInImport = $false; } elseif ($statuscode -eq 31 -or $statuscode -eq 32) { Write-Error "##[error]: Unable to import solution - please check Solution import history in https://make.powerapps.com/environments" Write-Warning "##[warning] Import Failed: $($operation.statuscode)" Write-Warning "##[warning] Error Code: $($operation.errorcode)" Write-Warning "##[warning] $($operation.friendlymessage)" $anyFailuresInImport = $true; } } catch { Write-Host "Retrying Polling import status" $Retrycount = $Retrycount + 1 if ($Retrycount -gt 3) { $statuscode = 32; $anyFailuresInImport = $true; Write-Error "##[error]: Unable to polling or import solution - please check Solution import history in https://make.powerapps.com/environments" Write-Error "##[error]:$($_.Exception.Message)" break; } } } until ($statuscode -eq 30 -or $statuscode -eq 31 -or $statuscode -eq 32) $Retrycount = 0; # UPGRADE if ($deployAsHolding -eq $true -and $anyFailuresInImport -eq $false) { # PRE UPGRADE if ($Deploy.PreUpgrade -eq $true) { if (Test-Path -Path $PipelinePath\$PSolution\Scripts\PreUpgrade.ps1) { Write-Host "##[section] Execute Pre Upgrade from $PipelinePath\$PSolution\Scripts" . $PipelinePath\$PSolution\Scripts\PreUpgrade.ps1 -Conn $CRMConn -EnvironmentName $Deploy.EnvironmentName -Path "$PipelinePath\$PSolution\" } } Write-Host "Applying Upgrade to Solution" $promoteRequestId = $CRMConn.DeleteAndPromoteSolutionAsync($PSolution); if (($null -eq $promoteRequestId) -or ($promoteRequestId -eq [Guid]::Empty)) { Write-Error "##[error]: Unable to promote or delete solution - please check Solution import history in https://make.powerapps.com/environments" $anyFailuresInImport = $true; } else { Write-Host Async Operation ID: $promoteRequestId do { try { Start-Sleep -Seconds 5 $operation = Get-CrmRecord -conn $CRMConn -EntityLogicalName asyncoperation -Id ($promoteRequestId) -Fields name, statuscode, friendlymessage, completedon, errorcode [int]$statuscode = $operation.statuscode_Property.value.Value; if ($statuscode -le 30) { Write-Host "Polling Promotion status for Solution: $($PSolution) : $($operation.statuscode)" $anyFailuresInImport = $false; } elseif ($statuscode -eq 31 -or $statuscode -eq 32) { Write-Error "##[error]: Unable to promote or delete solution - please check Solution import history in https://make.powerapps.com/environments" Write-Warning "##[warning] Delete and Promote Failed: #($operation.statuscode)" Write-Warning "##[warning] Error Code: $($operation.errorcode)" Write-Warning "##[warning] $($operation.friendlymessage)" $anyFailuresInImport = $true; } } catch { Write-Host "Retrying Polling Upgrade status" $Retrycount = $Retrycount + 1 if ($Retrycount -gt 3) { $statuscode = 32; $anyFailuresInImport = $true; Write-Error "##[error]: Unable to polling or Upgrade of solution - please check Solution import history in https://make.powerapps.com/environments" Write-Error "##[error]:$($_.Exception.Message)" break; } } }until($statuscode -eq 30 -or $statuscode -eq 31 -or $statuscode -eq 32) } # Post UPGRADE if ($Deploy.PreUpgrade -eq $true) { if (Test-Path -Path $PipelinePath\$PSolution\Scripts\PostUpgrade.ps1) { Write-Host "##[section] Execute Post Upgrade from $PipelinePath\$PSolution\Scripts" . $PipelinePath\$PSolution\Scripts\PostUpgrade.ps1 -Conn $CRMConn -EnvironmentName $Deploy.EnvironmentName -Path "$PipelinePath\$PSolution\" } } } # DATA CONFIGURATION if ($Deploy.DeployData -eq $true -and $anyFailuresInImport -eq $false) { Write-Host "##[group] Importing reference Data ..." try { if (Test-Path -Path $PipelinePath\$PSolution\ReferenceData\data.zip) { Import-CrmDataFile -CrmConnection $CRMConn -DataFile $PipelinePath\$PSolution\ReferenceData\data.zip -EnabledBatchMode -Verbose } else { Write-Host "Config Data file does not Exist" } } catch { Write-Error "##[error]: Unable to import configuration data - please review Pipeline error logs" Write-Error "##[error]:$($_.Exception.Message)" } } else { Write-Host "##[section] No Data to Import for $PSolution" } # POST ACTION if ($Deploy.PostAction -eq $true -and $anyFailuresInImport -eq $false) { if (Test-Path -Path $PipelinePath\$PSolution\Scripts\PostAction.ps1) { Write-Host "##[section] Execute Post Action from $PipelinePath\$PSolution\Scripts" . $PipelinePath\$PSolution\Scripts\PostAction.ps1 -Conn $CRMConn -EnvironmentName $Deploy.EnvironmentName -Path "$PipelinePath\$PSolution\" } } } [int]$elapsedTime = $stopwatch.Elapsed.TotalMinutes $stopwatch.Stop() Write-Host "##[section] Import Complete in $($elapsedTime) minutes" } catch { Write-Host "##[section] Skipping $PSolution due to Solution import error" Write-Error "##[error]:$($_.Exception.Message)" } } else { Write-Host "##[warning] $($package.SolutionName) is not configured for deployment to $env:ENVIRONMENT_NAME in deployPackages.json" } Write-Host "##[endgroup]" } } Write-Host Environment $EnvironmentName Import-Package } |