Functions/Solutions/Import-CdsSolution.ps1

<#
    .SYNOPSIS
    Import solution.
#>

function Import-CdsSolution {
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$false, ValueFromPipeline)]
        [Microsoft.Xrm.Tooling.Connector.CrmServiceClient]
        $CdsClient = $Global:CdsClient,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $SolutionUniqueName,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( { Test-Path $_ })]
        [String]
        $SolutionFilePath,
        
        [Parameter(Mandatory = $false)]
        [Boolean]
        $PublishWorkflows = $true,
        
        [Parameter(Mandatory = $false)]
        [Boolean]
        $OverwriteUnmanagedCustomizations = $true,

        [Parameter(Mandatory = $false)]
        [Boolean]
        $ConvertToManaged = $false,
        
        [Parameter(Mandatory = $false)]
        [Boolean]
        $Upgrade = $false
    )
    begin {   
        $StopWatch = [System.Diagnostics.Stopwatch]::StartNew(); 
        Trace-CdsFunction -Name $MyInvocation.MyCommand.Name -Stage Start -Parameters ($MyInvocation.MyCommand.Parameters); 
    }    
    process {

        # Retrieve solution content
        $solutionContent = [System.IO.File]::ReadAllBytes($SolutionFilePath);
        
        # Initialize import solution request
        $importJobId = New-Guid;

        $request = New-Object -TypeName Microsoft.Crm.Sdk.Messages.ImportSolutionRequest;
        $request.CustomizationFile = $solutionContent;
        $request.PublishWorkflows = $PublishWorkflows;
        $request.OverwriteUnmanagedCustomizations = $OverwriteUnmanagedCustomizations;
        $request.ConvertToManaged = $ConvertToManaged;
        $request.ImportJobId = $importJobId;
        $request.RequestId = $importJobId;

        # Prepare async job for server side solution import
        $asyncRequest = New-Object -TypeName Microsoft.Xrm.Sdk.Messages.ExecuteAsyncRequest;
        $asyncRequest.Request = $request;
        $asyncRequest.RequestId = New-Guid;

        # Delay in seconds
        $secondBeforeCheckingSystemJob = 5;
        try {   
            $CdsClient | Invoke-CdsRequest -Request $asyncRequest | Out-Null;

            $completed = $false;
            $data = "";
            $lastProgress = 0;
            $loopFailedCount = 0;
            while (!$completed) {
                try {
                    $importJob = $CdsClient | Get-CdsRecord -LogicalName "importjob" -Id $importJobId -Columns "completedon", "data", "progress";
                
                    $data = $importJob.data;
                    $completedOn = $importJob.completedon;
                    $progress = $importJob.progress;
                    $progressString = $progress.ToString("f");
                    if ($progress -ne $lastProgress) {
                        Write-HostAndLog "$($MyInvocation.MyCommand.Name) => $SolutionUniqueName import in progress... ($progressString %)" -ForegroundColor Cyan;
                    }            
                    $lastProgress = $progress;

                    Write-Progress -Activity $($MyInvocation.MyCommand.Name) -Status "Importing solution $SolutionUniqueName...($progressString %)" -PercentComplete $progress;
                    if ($null -ne $completedOn) {
                        $completed = $true;
                        break;
                    }
                }
                catch {                    
                    # Ignore : import job is not probably created yet
                    $loopFailedCount++;
                    if ($loopFailedCount -gt 12) {
                        throw $_.Exception;
                    }
                }
                Start-Sleep -s $secondBeforeCheckingSystemJob;
            }
            write-progress one one -completed;

            $xmlData = [xml] $data;
            $resultNode = $xmlData.importexportxml.solutionManifests.solutionManifest.result;
            if ($resultNode.result -eq "failure") {        
                throw "$($resultNode.errorcode): $($resultNode.errortext)";
            }
        }
        catch {
            $errorMessage = $_.Exception.Message;
            Write-HostAndLog "$($MyInvocation.MyCommand.Name) => KO : [Error: $errorMessage]" -ForegroundColor Red;
            write-progress one one -completed;
            throw $errorMessage;
        }  
    }
    end {
        $StopWatch.Stop();
        Trace-CdsFunction -Name $MyInvocation.MyCommand.Name -Stage Stop -StopWatch $StopWatch;
    }    
}

Export-ModuleMember -Function Import-CdsSolution -Alias *;