Copy-DiskToGCP.ps1

<#

.SYNOPSIS
Copy a disk to Google Cloud.

.DESCRIPTION
Copy a disk to a Google Cloud image.

.PARAMETER FileName
Specifies the filename of the disk to be copied. The file can be either local or on a share.

.PARAMETER ImageName
Specifies the name of the Google Cloud image to copy the disk to.

.PARAMETER LogFile
Specifies the path to the file to log to. ".\Upload.log" is the default.

.PARAMETER OverwriteLog
If specified the log file is overwritten otherwise it is appended to.

.PARAMETER Force
If the destination of the copy already exists delete it before doing the copy.

.PARAMETER ServiceAccountKeyFile
Specifies the name of a file containing Google Cloud service principal credentials.

.INPUTS
None.

.OUTPUTS
None

.EXAMPLE

Copy-DiskToGCP -FileName C:\demo\disk.vhd -ImageName demo -ServiceAccountKeyFile demo-project-af94dadb30a1.json

#>


Function Copy-DiskToGCP
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $True)]
        [string] $FileName,

        [Parameter(Mandatory = $True)]
        [string] $ImageName,

        [Parameter(Mandatory = $True)]
        [string] $ServiceAccountKeyFile,

        [Parameter()]
        [int] $Threads,

        [Parameter()]
        [string] $LogFile,

        [Parameter()]
        [switch] $OverwriteLog,

        [Parameter()]
        [switch] $Force
    )

    Begin
    {
        InitUploadLog $LogFile $OverwriteLog
    }

    Process
    {
        try
        {
            # Temporary solution to redirecting 1.48 assemblies that are compile time referenced in the Google.Api.Gax.Rest assemblies
            # to the 1.49 assemblies that the other Google assemblies reference
            # This is the powershell way to accomplish Binding Redirects, that are typically done in the configuration file.
            $modulePath = (Get-Item (Get-Module -Name Citrix.Image.Uploader).Path).DirectoryName
            $GoogleApis = [reflection.assembly]::LoadFrom($modulePath + "\bin\netstandard2.0\Google.Apis.dll")
            $GoogleApisCore = [reflection.assembly]::LoadFrom($modulePath + "\bin\netstandard2.0\Google.Apis.Core.dll")
            $GoogleApisAuth = [reflection.assembly]::LoadFrom($modulePath + "\bin\netstandard2.0\Google.Apis.Auth.dll")
            $OnAssemblyResolve = [System.ResolveEventHandler] {
                param($s, $e)
                Log "Resolving assembly '$($e.Name)'" $False
                if (($e.Name.StartsWith("Google.Apis.Core, Version=1.49.0.0")) -or
                    ($e.Name.StartsWith("Google.Apis.Auth, Version=1.49.0.0")) -or
                    ($e.Name.StartsWith("Google.Apis, Version=1.49.0.0")))
                {
                    Log ("This workaround may no longer be necessary. The Google assemblies may now be referencing the correct assemblies. " +
                         "Try removing this event handler (OnAssemblyResolve) and try again.") $False
                }
                if ($e.Name -eq "Google.Apis.Core, Version=1.48.0.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab")
                {
                    Log ("Forcing the Assembly '$($e.Name)' to be '$($GoogleApisCore.GetName().Name), " +
                         "Version=$($GoogleApisCore.GetName().Version)'") $False
                    return $GoogleApisCore
                }
                if ($e.Name -eq "Google.Apis.Auth, Version=1.48.0.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab")
                {
                    Log ("Forcing the Assembly '$($e.Name)' to be '$($GoogleApisAuth.GetName().Name), " +
                         "Version=$($GoogleApisAuth.GetName().Version)'") $False
                    return $GoogleApisAuth
                }
                if ($e.Name -eq "Google.Apis, Version=1.48.0.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab")
                {
                    Log "Forcing the Assembly '$($e.Name)' to be '$($GoogleApis.GetName().Name), Version=$($GoogleApis.GetName().Version)'" $False
                    return $GoogleApis
                }
                foreach($a in [System.AppDomain]::CurrentDomain.GetAssemblies())
                {
                    if ($a.FullName -eq $e.Name)
                    {
                        return $a
                    }
                }
                return $null
            }
            Log "Registering AssemblyResolve event handling" $False
            [System.AppDomain]::CurrentDomain.add_AssemblyResolve($OnAssemblyResolve)

            if ($Force)
            {
                CleanUpGcpDisk $ServiceAccountKeyFile $ImageName
            }
            $InformationPreference = "Continue"
            try
            {
                UploadToGcp $ImageName $FileName $ServiceAccountKeyFile $Global:UploadLogFile
            }
            catch [System.Reflection.ReflectionTypeLoadException]
            {
                Log "Message: $($_.Exception.Message)" $False
                Log "StackTrace: $($_.Exception.StackTrace)" $False
                Log "LoaderExceptions: $($_.Exception.LoaderExceptions)" $False
                try
                {
                    Log "Redirected Google SDK package version and retry uploading process"
                    UploadToGcp $ImageName $FileName $ServiceAccountKeyFile $Global:UploadLogFile
                }
                catch
                {
                    ThrowError ([UploaderException]::new("Failed to copy the disk to Google Cloud", $_.Exception))
                }
            }
            catch
            {
                ThrowError ([UploaderException]::new("Failed to copy the disk to Google Cloud", $_.Exception))
            }
        }
        catch [UploaderException]
        {
            LogIfSslError $_
            $PSCmdlet.ThrowTerminatingError($_)
        }
        catch
        {
            Log $_
            LogIfSslError $_
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }
}