Scripts/SolutionDeploy.ps1
# # SolutionDeploy.ps1 # function Write-PPDOMessage { [CmdletBinding()] param ( [Parameter()] [string] $Message, [Parameter()] [ValidateSet('group', 'warning', 'error', 'section', 'debug', 'command', 'endgroup')] [string] $Type, [Parameter()] [bool] $LogError = $false, [Parameter()] [bool] $LogWarning = $false, [Parameter()] [bool] $RunLocally = $false ) switch ($Type) { 'group' { if ($RunLocally) { Write-Host $Message -BackgroundColor Magenta } else { Write-Host "##[group]$Message" } } 'warning' { if ($RunLocally) { Write-Host $Message -ForegroundColor DarkYellow } else { if ($LogWarning) { Write-Host "##vso[task.logissue type=warning]$Message" } else { Write-Host "##[warning]$Message" } } } 'error' { if ($RunLocally) { Write-Host $Message -ForegroundColor Red } else { if ($LogError) { Write-Host "##vso[task.logissue type=error]$Message" } else { Write-Host "##[error]$Message" } } } 'section' { if ($RunLocally) { Write-Host $Message -ForegroundColor Green } else { Write-Host "##[section]$Message" } } 'debug' { if ($RunLocally) { Write-Host $Message -ForegroundColor Magenta } else { Write-Host "##[debug]$Message" } } 'command' { if ($RunLocally) { Write-Host $Message -ForegroundColor Blue } else { Write-Host "##[command]$Message" } } 'endgroup' { if ($RunLocally) { Write-Host ":END:" -BackgroundColor Magenta } else { Write-Host "##[endgroup]" } } Default { Write-Host $Message } } } 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 #region "Dependencies" Write-PPDOMessage -Message "Installing Dependencies" -Type group -RunLocally $RunLocally Install-PAC if (!$RunLocally) { Install-ConfigMigrationModule Install-XrmModule Install-PowerAppsCheckerModule Install-PowerAppsAdmin } else { Write-Host "Preparing local run" } Write-PPDOMessage -Type endgroup -RunLocally $RunLocally #endregion 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 #handle Environments file should it be missing. Same for DeployPackages and SolutionChecker files! $Environments = Get-Content "$PipelinePath\Environments.json" | ConvertFrom-Json $EnvConfig = $Environments | Where-Object { $_.EnvironmentName -eq $EnvironmentName } Write-PPDOMessage "Creating CRM connection" -Type section -RunLocally $RunLocally $CRMConn = Get-CrmConnection -ConnectionString $CrmConnectionString -Verbose #-MaxCrmConnectionTimeOutMinutes 10 #Set-CrmConnectionTimeout -conn $CRMConn -TimeoutInSeconds 600 if ($false -eq $CRMConn.IsReady) { Write-Host "An error occurred: " $CRMConn.LastCrmError Write-Host $CRMConn.LastCrmException.Message Write-Host $CRMConn.LastCrmException.Source Write-Host $CRMConn.LastCrmException.StackTrace throw "Could not establish connection with server" } #ENVIRONMENT PRE-ACTION if ($null -ne $EnvConfig -and $EnvConfig.PreAction -eq $true) { Write-PPDOMessage "Execute Environment Pre Action" -Type section -RunLocally $RunLocally . "$PipelinePath\Common\Environments\Scripts\PreAction.ps1" -Conn $CRMConn -PipelinePath $PipelinePath -EnvironmentName $EnvConfig.EnvironmentName -EnvironmentUrl $DeployServerUrl $EnvConfig.PreFunctions | ForEach-Object { & $_ -Conn $CRMConn } Write-PPDOMessage "Environment Pre Action Complete" -Type command -RunLocally $RunLocally } else { Write-PPDOMessage "Environment Pre Action not registered to execute" -Type warning -RunLocally $RunLocally } foreach ($package in $Packages) { $skipDeploy = $false $anyFailuresInImport = $false; $Deploy = $package.DeployTo | Where-Object { $_.EnvironmentName -eq $EnvironmentName } if ($null -ne $Deploy) { #region 'Preparing Deployment' $PSolution = $package.SolutionName Write-PPDOMessage "Preparing Deployment for $PSolution" -Type group -RunLocally $RunLocally Write-Host "Deployment step manifest - $Deploy" Write-PPDOMessage "Preparing $PSolution Solution as $($Deploy.DeploymentType)" -Type section -RunLocally $RunLocally $fileToPack = "$($Deploy.EnvironmentName)_$($PSolution)_$($Deploy.DeploymentType).zip" $packageFolder = "dataverse_$($PSolution)" Write-PPDOMessage "Packing Solution $PSolution" -Type command -RunLocally $RunLocally #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-PPDOMessage "Importing package" -Type section -RunLocally $RunLocally try { $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() $error.Clear() Write-PPDOMessage "Deploying $($package.SolutionName) as $($Deploy.DeploymentType) to - $EnvironmentName" -Type section -RunLocally $RunLocally #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-PPDOMessage "Version to be deployed : $deployingVersion" -Type command -RunLocally $RunLocally if ($deployingVersion -le $SolutionVersion) { $skipDeploy = $true; Write-PPDOMessage "Skipping Deployment as Target has same or newer" -Type warning -RunLocally $RunLocally } ########################## IMPORT if (!$skipDeploy) { # Powerapps Solution Checker if ($Deploy.PowerAppsChecker -eq $true -and $UseClientSecret -eq $true) { Write-PPDOMessage "Running PowerApps Solution Checker for $PSolution" -Type command -RunLocally $RunLocally Start-SolutionChecker -PipelinePath $PipelinePath -SolutionPath $PipelinePath\$PSolution\$fileToPack -SolutionName $PSolution -ClientId $UserName -ClientSecret $Password -TenantId "$($CRMConn.TenantId)" } else { Write-PPDOMessage "Powerapps Checker not configured. Add PowerAppsChecker: True as a property in the DeployTo section for your Solution" -Type warning -RunLocally $RunLocally -LogWarning $true } # PRE ACTION if ($Deploy.PreAction -eq $true) { if (Test-Path -Path $PipelinePath\$PSolution\Scripts\PreAction.ps1) { Write-PPDOMessage "Execute Pre Action from $PipelinePath\$PSolution\Scripts" -Type section -RunLocally $RunLocally . $PipelinePath\$PSolution\Scripts\PreAction.ps1 -Conn $CRMConn -EnvironmentName $Deploy.EnvironmentName -Path "$PipelinePath\$PSolution\" } else { Write-Host "Deployment PreAction step not registered to excecute" } } $activatePlugIns = $true; $overwriteUnManagedCustomizations = $true; $skipDependancyOnProductUpdateCheckOnInstall = $true; $isInternalUpgrade = $false; Write-PPDOMessage "Initiating Import and deployment to $($DeployServerUrl)" -Type section -RunLocally $RunLocally Write-PPDOMessage "Import as Holding solution : $($deployAsHolding)" -Type command -RunLocally $RunLocally $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-PPDOMessage "Unable to import solution - please check Solution import history in https://make.powerapps.com/environments" -Type error -RunLocally $RunLocally -LogError $true Write-PPDOMessage "Import Failed: $($operation.statuscode)" -Type warning -RunLocally $RunLocally Write-PPDOMessage "Error Code: $($operation.errorcode)" -Type warning -RunLocally $RunLocally Write-PPDOMessage "$($operation.friendlymessage)" -Type warning -RunLocally $RunLocally exit 1 $anyFailuresInImport = $true; } } catch { Write-Host "Retrying Polling import status" $Retrycount = $Retrycount + 1 if ($Retrycount -gt 3) { $statuscode = 32; $anyFailuresInImport = $true; Write-PPDOMessage "Unable to poll status - please check Solution import history in https://make.powerapps.com/environments" -Type error -RunLocally $RunLocally -LogError $true Write-PPDOMessage "$($_.Exception.Message)" -Type error -RunLocally $RunLocally exit 1 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-PPDOMessage "Execute Pre Upgrade from $PipelinePath\$PSolution\Scripts" -Type section -RunLocally $RunLocally . $PipelinePath\$PSolution\Scripts\PreUpgrade.ps1 -Conn $CRMConn -EnvironmentName $Deploy.EnvironmentName -Path "$PipelinePath\$PSolution\" } else { Write-Host "Deployment PreUpgrade step not registered to excecute" } } Write-Host "Applying Upgrade to Solution" $promoteRequestId = $CRMConn.DeleteAndPromoteSolutionAsync($PSolution); if (($null -eq $promoteRequestId) -or ($promoteRequestId -eq [Guid]::Empty)) { Write-PPDOMessage "Unable to promote or delete solution - please check Solution import history in https://make.powerapps.com/environments" -Type error -RunLocally $RunLocally -LogError $true $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-PPDOMessage "Unable to promote or delete solution - please check Solution import history in https://make.powerapps.com/environments" -Type error -RunLocally $RunLocally -LogError $true Write-PPDOMessage "Delete and Promote Failed: #($operation.statuscode)" -Type warning -RunLocally $RunLocally Write-PPDOMessage "Error Code: $($operation.errorcode)" -Type warning -RunLocally $RunLocally Write-PPDOMessage "$($operation.friendlymessage)" -Type warning -RunLocally $RunLocally exit 1 $anyFailuresInImport = $true; } } catch { Write-Host "Retrying Polling Upgrade status" $Retrycount = $Retrycount + 1 if ($Retrycount -gt 3) { $statuscode = 32; $anyFailuresInImport = $true; Write-PPDOMessage "Unable to poll status - please check Solution import history in https://make.powerapps.com/environments" -Type error -RunLocally $RunLocally -LogError $true Write-PPDOMessage "$($_.Exception.Message)" -Type error -RunLocally $RunLocally exit 1 break; } } }until($statuscode -eq 30 -or $statuscode -eq 31 -or $statuscode -eq 32) } # Post UPGRADE if ($Deploy.PostUpgrade -eq $true) { if (Test-Path -Path $PipelinePath\$PSolution\Scripts\PostUpgrade.ps1) { Write-PPDOMessage "Execute Post Upgrade from $PipelinePath\$PSolution\Scripts" -Type section -RunLocally $RunLocally . $PipelinePath\$PSolution\Scripts\PostUpgrade.ps1 -Conn $CRMConn -EnvironmentName $Deploy.EnvironmentName -Path "$PipelinePath\$PSolution\" } else { Write-Host "Deployment PostUpgrade step not registered to excecute" } } } #region 'Reference Data' # DATA CONFIGURATION if ($Deploy.DeployData -eq $true -and $anyFailuresInImport -eq $false) { Write-PPDOMessage "Importing reference Data ..." -Type group -RunLocally $RunLocally 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-PPDOMessage "Unable to import configuration data - please review Pipeline error logs" -Type error -RunLocally $RunLocally -LogError $true Write-PPDOMessage "$($_.Exception.Message)" -Type error -RunLocally $RunLocally exit 1 } } else { Write-PPDOMessage "No Data to Import for $PSolution" -Type section -RunLocally $RunLocally } Write-PPDOMessage -Type endgroup -RunLocally $RunLocally #endregion # POST ACTION if ($Deploy.PostAction -eq $true -and $anyFailuresInImport -eq $false) { if (Test-Path -Path $PipelinePath\$PSolution\Scripts\PostAction.ps1) { Write-PPDOMessage "Execute Post Action from $PipelinePath\$PSolution\Scripts" -Type section -RunLocally $RunLocally . $PipelinePath\$PSolution\Scripts\PostAction.ps1 -Conn $CRMConn -EnvironmentName $Deploy.EnvironmentName -Path "$PipelinePath\$PSolution\" } else { Write-Host "Deployment PostAction step not registered to excecute" } } $ProgressPreference = "SilentlyContinue" # Activate Flows and Establish Connection References Write-Host "Getting Environment Id" $orgs = Get-CrmRecords -conn $CRMConn -EntityLogicalName organization if ($orgs.Count -gt 0) { $orgId = $orgs.CrmRecords[0].organizationid if ($UseClientSecret) { Add-PowerAppsAccount -ApplicationId $UserName -ClientSecret $Password -TenantID $CRMConn.TenantId } else { Add-PowerAppsAccount -Username $UserName -Password $Password } $Environment = Get-AdminPowerAppEnvironment | Where-Object OrganizationId -eq $orgId.Guid $EnvId = $Environment.EnvironmentName Write-Host "Environment Id - $EnvId" Write-Host "A - $UserName" Write-Host "C - $Password" Write-Host "T - $($CRMConn.TenantId)" Write-Host "Checking if there are Connections References in the Solution that Need to be Wired Up" $solutions = Get-CrmRecords -conn $CRMConn -EntityLogicalName solution -FilterAttribute "uniquename" -FilterOperator "eq" -FilterValue "$PSolution" $solutionId = $solutions.CrmRecords[0].solutionid $connRefs = (Get-CrmRecords -conn $CRMConn -EntityLogicalName connectionreference -FilterAttribute "solutionid" -FilterOperator eq -FilterValue $solutionid -Fields connectionreferencelogicalname, connectionid, connectorid, connectionreferenceid).CrmRecords $connRefs | Where-Object { $null -eq $_.connectionid } | ForEach-Object { $connectionType = $_.connectorid.Replace("/providers/Microsoft.PowerApps/apis/", "") Write-Host "Found Connection Reference $($_.connectionreferencelogicalname) without a Connection to $connectionType" Write-Host "Getting Connections in Environment" $connection = Get-AdminPowerAppConnection -EnvironmentName $EnvId | Where-Object ConnectorName -eq $connectionType if ($connection) { # Get Dataverse systemuserid for the system user that maps to the aad user guid that created the connection $systemusers = Get-CrmRecords -conn $conn -EntityLogicalName systemuser -FilterAttribute "azureactivedirectoryobjectid" -FilterOperator "eq" -FilterValue $connection[0].CreatedBy.id -Fields domainname if ($systemusers.Count -gt 0) { Write-Host "Impersonating the Owner of the Connection - $($systemusers.CrmRecords[0].domainname)" # Impersonate the Dataverse systemuser that created the connection when updating the connection reference $impersonationCallerId = $systemusers.CrmRecords[0].systemuserid $impersonationConn = $CRMConn $impersonationConn.OrganizationWebProxyClient.CallerId = $impersonationCallerId Write-PPDOMessage "Setting Connection Reference to use $($connection[0].DisplayName)" -Type command -RunLocally $RunLocally Set-CrmRecord -conn $impersonationConn -EntityLogicalName $_.logicalname -Id $_.connectionreferenceid -Fields @{"connectionid" = $connection[0].ConnectionName } } } else { Write-PPDOMessage "No Connection has been set up of type $connectionType, some of your Flows may not Activate succesfully" -Type warning -RunLocally $RunLocally -LogWarning $true } } Write-Host "Checking if there are Flows that need to be Activated" if ($Deploy.Flows.ActivateFlows -eq $true) { if ($Deploy.Flows.OverrideFile) { Write-Host "Using $($Deploy.Flows.OverrideFile) for Flow Activation" $FlowsToActivate = Get-Content -Path $PipelinePath\$PSolution\$($Deploy.Flows.OverrideFile) | ConvertFrom-Json } else { Write-Host "Using Flows_Default.json for Flow Activation" $FlowsToActivate = Get-Content -Path $PipelinePath\$PSolution\Flows_Default.json | ConvertFrom-Json -ErrorAction SilentlyContinue } Write-Host "There are $($FlowsToActivate.Count) Flows that need activating" $FlowsToActivate | ForEach-Object { $workflow = Get-CrmRecord -conn $CRMConn -EntityLogicalName workflow -Id $_.FlowId -Fields clientdata, category, statecode, name if ($_.ActivateAsUser) { Write-Host "ActivateAsUser defined and set to : $($_.ActivateAsUser), attempting to Active Flow as this user" $systemuserResult = Get-CrmRecords -conn $CRMConn -EntityLogicalName systemuser -FilterAttribute "domainname" -FilterOperator "eq" -FilterValue $_.ActivateAsUser if ($systemuserResult.Count -gt 0) { $systemUserId = $systemuserResult.CrmRecords[0].systemuserid #Activate the workflow using the owner. if ($workflow.statecode -ne "Activated") { $impersonationConn = $CRMConn $impersonationCallerId = $systemUserId $impersonationConn.OrganizationWebProxyClient.CallerId = $impersonationCallerId Write-PPDOMessage "Enabling Flow $($workflow.name)" -Type command -RunLocally $RunLocally try { Set-CrmRecordState -conn $impersonationConn -EntityLogicalName workflow -Id $_.FlowId -StateCode Activated -StatusCode Activated } catch { Write-PPDOMessage "There was an error activating the Flow, please confirm that the user exists and that the appropriate connections have been created as this user in the Environment" -Type warning -RunLocally $RunLocally -LogWarning $true Write-Host $_ } } } Write-PPDOMessage "User $($_.ActivateAsUser) was not found in $($Deploy.EnvironmentName)" -Type warning -RunLocally $RunLocally -LogWarning $true } else { Write-Host "Attempting to Activate Flow as Owner of Connection Reference" $solutions = Get-CrmRecords -conn $CRMConn -EntityLogicalName solution -FilterAttribute "uniquename" -FilterOperator "eq" -FilterValue "$PSolution" $solutionId = $solutions.CrmRecords[0].solutionid $connRefs = (Get-CrmRecords -conn $CRMConn -EntityLogicalName connectionreference -FilterAttribute "solutionid" -FilterOperator eq -FilterValue $solutionid -Fields connectionreferencelogicalname, connectionid, connectorid, connectionreferenceid).CrmRecords $connRefToUse = $connRefs | Where-Object { $null -ne $_.connectionid } | Select-Object -First 1 -ErrorAction SilentlyContinue $connection = Get-AdminPowerAppConnection -EnvironmentName $EnvId -Filter $connRefToUse.ConnectionId # Get Dataverse systemuserid for the system user that maps to the aad user guid that created the connection $systemusers = Get-CrmRecords -conn $conn -EntityLogicalName systemuser -FilterAttribute "azureactivedirectoryobjectid" -FilterOperator "eq" -FilterValue $connection[0].CreatedBy.id if ($systemusers.Count -gt 0) { # Impersonate the Dataverse systemuser that created the connection when updating the connection reference $impersonationCallerId = $systemusers.CrmRecords[0].systemuserid if ($workflow.statecode -ne "Activated") { Write-PPDOMessage "Enabling Flow $($workflow.name)" -Type command -RunLocally $RunLocally $impersonationConn = $CRMConn $impersonationConn.OrganizationWebProxyClient.CallerId = $impersonationCallerId Set-CrmRecordState -conn $impersonationConn -EntityLogicalName workflow -Id $_.FlowId -StateCode Activated -StatusCode Activated } } } } } else { Write-Host @" No Flows were specified for activation. If you wish to include flows for activation, please add the following in deployPackages.json "Flows": { "ActivateFlows": "true", "OverrideFile" : "" } "@ } } $ProgressPreference = "Continue" [int]$elapsedTime = $stopwatch.Elapsed.TotalMinutes $stopwatch.Stop() Write-PPDOMessage "Import Complete in $($elapsedTime) minutes" -Type section -RunLocally $RunLocally } } catch { Write-PPDOMessage "Skipping $PSolution due to Solution import error" -Type section -RunLocally $RunLocally Write-PPDOMessage "$($_.Exception.Message)" -Type error -RunLocally $RunLocally } Write-PPDOMessage -Type endgroup -RunLocally $RunLocally #endregion } else { Write-PPDOMessage "$($package.SolutionName) is not configured for deployment to $env:ENVIRONMENT_NAME in deployPackages.json" -Type warning -RunLocally $RunLocally -LogWarning $true } } #EXECUTE ENVIRONMENT POST ACTION if ($null -ne $EnvConfig -and $EnvConfig.PostAction -eq $true) { Write-PPDOMessage "Execute Environment Post Action" -Type section -RunLocally $RunLocally . "$PipelinePath\Common\Environments\Scripts\PostAction.ps1" -Conn $CRMConn -PipelinePath $PipelinePath -EnvironmentName $EnvConfig.EnvironmentName -EnvironmentUrl $DeployServerUrl $EnvConfig.PostFunctions | ForEach-Object { & $_ -Conn $CRMConn } Write-PPDOMessage "Environment Post Action Complete" -Type command -RunLocally $RunLocally } else { Write-Host "Environment PostAction step not registered to excecute" } } Write-Host Environment $EnvironmentName Import-Package } |