Migration/GCP/GCP.psm1

using module '../../Common/Result'
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath MigrationProfile | Join-Path -ChildPath GCPMigrationProfile)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath CloudAccount | Join-Path -ChildPath CloudAccount)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath Preflight | Join-Path -ChildPath Preflight)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath Util | Join-Path -ChildPath Util)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath Common | Join-Path -ChildPath Common)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath RiverMeadow.Development.Source | Join-Path -ChildPath SourceUtil | Join-Path -ChildPath SourceUtil)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath Common | Join-Path -ChildPath Wrappers | Join-Path -ChildPath Wrappers)
Import-Module -Name @(Join-Path $PSScriptRoot GCPUtil)

function Start-RMGCPOSBasedInteractiveMigration {
    param()
    $UserInput = @{}
    $CloudAccount = Read-RMCloudAccount -AccountType "gcp" -UserMessage "Enter target cloud"
    $UserInput.Add("CloudAccount", $CloudAccount)

    $CloudAttributes = Get-RMCloudAttribute -CloudAccount $CloudAccount

    $Entitlement = Get-RMEntitlement
    $UserInput.Add("Entitlement", $Entitlement)

    $CloudAttributesSummary = Get-CloudAttributesSummary -CloudAccount $CloudAccount
   
    $Source = Read-RMSource -UserMessage "Enter the IP address of the source machine to be migrated" -ParameterName "Source IP address" -IsRequired $true

    $IgnoreValidationErrors = Read-RMBoolean -UserMessage "Ignore validation errors" -DefaultValue "false"

    [RMMigrationReturn] $RMMigrationReturn = [RMMigrationReturn]::new()

    $Source, $ShouldExit, $OverrideExistingMigrationWarning, $OverrideExistingMigrationError = `
        Get-RMSourceWithAttribute -Source $Source -CloudAccount $CloudAccount `
            -IgnoreValidationErrors $IgnoreValidationErrors -RMMigrationReturn $RMMigrationReturn  -AccountType "gcp"
    if ($ShouldExit) {
        return $RMMigrationReturn
    }

    $UserInput.Add("Source", $Source)

    if ($OverrideExistingMigrationError) {
        $OverrideExistingMigrationError = Read-RMBoolean -UserMessage "Would you like to override the previous migration attempt" -DefaultValue "false"
        if (!$IgnoreValidationErrors -and !$OverrideExistingMigrationError) {
            return $RMMigrationReturn
        }
    }

    $ScheduledAt = Read-RMMigrationSchedule
    $UserInput.Add("ScheduledAt", $ScheduledAt)

    $TargetVMName = Read-RMString -UserMessage "Enter target VM Name" -DefaultValue $Source.hostname `
        -ParameterName "Target VM Name" -IsRequired $false
    $UserInput.Add("TargetVMName", $TargetVMName)

    $MountPoint = Get-MountPoint -Source $Source
    if (0 -eq $MountPoint.count) {
        throw "Source has no mount points, cannot be migrated"
    }

    $MountPointsAsString = $MountPoint.values -join ", "
    Write-Output "Mount points to be migrated [$MountPointsAsString]" | Out-Host
    $ExcludedMountPoint = Get-RMExcludedMountPoint -OSType $Source.os_type -MountPoints $MountPoints.Values
    $SelectedMountPoint = $MountPoint
    if ("" -ne $ExcludedMountPoint) {
        $ExcludedList = $ExcludedMountPoint.Split(",").Trim()
        $SelectedMountPoint = Get-RMSelectedMount -MountPoints $MountPoints -DifferenceList $ExcludedList -IncludeEqual $false
    }
    $UserInput.Add("SelectedMount", $SelectedMountPoint)

    $ReadValue = Read-RMBoolean -UserMessage "Resize mount points" -DefaultValue "false"

    $MountsResize = @{}
    $TransferMethod = "file-based"
    if ($ReadValue) {
        $MountsResize = Get-RMInteractiveMountsResize -SelectedMountPoints $SelectedMountPoint -Source $Source
    } else {
        $TransferMethod = (Get-RMTransferMethod -Source $Source -SelectedMountPoints $SelectedMountPoint -IsInteractive $true)[0]
    }

    $UserInput.Add("ResizeMountPoint", $MountsResize)
    $UserInput.Add("TransferMethod", $TransferMethod)

    $ProjectMapping = Get-RMProjectIDToNameMapping -CloudAttribute $CloudAttributesSummary
    $DefaultProject = ""
    if ($ProjectMapping.ContainsKey($CloudAccount.appliance.cloud_properties.project_id)) {
        $DefaultProject = $ProjectMapping[$CloudAccount.appliance.cloud_properties.project_id]
    }
    $ProjectName = Read-RMString -UserMessage "Enter project name" -DefaultValue $DefaultProject -Options $ProjectMapping.values `
        -ParameterName "Project Name" -IsRequired $false

    $ProjectId = ($ProjectMapping.GetEnumerator() | Where-Object {$_.Value -ieq $ProjectName}).Key
    $UserInput.Add("ProjectId", $ProjectId)
    $RegionsAndZones = Get-RMCloudAttributesByProjectId -CloudAccount $CloudAccount -ProjectId $ProjectId

    $RegionAndZoneMapping = Get-RMRegionToZonesMapping -RegionAndZone $RegionsAndZones.properties.projects[0].regions
    $Region = Read-RMString -UserMessage "Enter region name" -DefaultValue $CloudAccount.appliance.cloud_properties.region -Options $RegionAndZoneMapping.Keys `
        -ParameterName "Region Name" -IsRequired $false
    $UserInput.Add("Region", $Region)

    $Zone = ""
    if ($Region -ieq $CloudAccount.appliance.cloud_properties.region) {
        $Zone = Read-RMString -UserMessage "Enter zone name" -DefaultValue $CloudAccount.appliance.cloud_properties.az `
            -Options $RegionAndZoneMapping[$Region] -ParameterName "Zone Name" -IsRequired $false
    } else {
        $Zone = Read-RMString -UserMessage "Enter zone name" -Options $RegionAndZoneMapping[$Region] `
            -ParameterName "Zone Name" -IsRequired $true
    }
    $UserInput.Add("Zone", $Zone)

    $DefaultNetworkName = $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.network_name
    if ($Region -ine $CloudAccount.appliance.cloud_properties.region -or `
            $Zone -ine $CloudAccount.appliance.cloud_properties.az -or `
            $ProjectId -ine $CloudAccount.appliance.cloud_properties.project_id) {
        $CloudAttributes = Get-RMCloudAttributesByProjectIdAndRegionAndZone -CloudAccount $CloudAccount `
            -ProjectId $ProjectId -Region $Region -Zone $Zone
        $DefaultNetworkName = ""
    }

    $UserInput.Add("NodeGroupName", (Read-RMNodeGroupName -CloudAttribute $CloudAttributes))
    $UserInput.Add("MachineType", (Read-RMMachineType -CloudAttribute $CloudAttributes))
    $UserInput.Add("DiskType", (Get-RMDiskTypeBySource -Source $Source -IsInteractive $true))

    $ReadValue = Read-RMPair -UserMessage "Enter one or more instance labels in the format 'key=value' and separated by commas" `
        -Separator "=" -DefaultValue "None"
    $UserInput.Add("InstanceLabel", (Get-RMStringAsHashtable -InputString $ReadValue))

    $NetworksWithSubnets = Get-RMNetworkToSubnetMapping -CloudAttribute $CloudAttributes
    $DestinationNetworkName = ""
    if ([string]::IsNullOrWhiteSpace($DefaultNetworkName)) {
        $DestinationNetworkName = Read-RMString -UserMessage "Enter destination network name" `
            -Options $NetworksWithSubnets.keys -ParameterName "Destination Network Name" -IsRequired $true
    } else {
        $DestinationNetworkName = Read-RMString -UserMessage "Enter destination network name" `
            -Options $NetworksWithSubnets.keys -DefaultValue $DefaultNetworkName `
            -ParameterName "Destination Network Name" -IsRequired $false
    }

    $UserInput.Add("DestinationNetworkName", (Get-RMNetworkNameByUserInput -NetworkName $DestinationNetworkName))

    if ($null -eq $NetworksWithSubnets) {
        Throw "No networks were found, please check if the networks exist in the selected project, region and zone"
    }

    $DefaultSubnetName = ""
    if ($DestinationNetworkName -ieq $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.network_name) {
        $DefaultSubnetName = $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.internal_subnet_id
    }

    $SubnetNames = Get-RMSubnetNameByNetworkName -NetworkName $DestinationNetworkName -NetworkToSubnetMapping $NetworksWithSubnets
    if ([string]::IsNullOrWhiteSpace($DefaultSubnetName)) {
        $SubnetName = Read-RMString -UserMessage "Enter subnet name" -Options $SubnetNames `
            -ParameterName "Subnet Name" -IsRequired $true
    } else {
        $SubnetName = Read-RMString -UserMessage "Enter subnet name" -DefaultValue $DefaultSubnetName `
            -Options $SubnetNames -ParameterName "Subnet Name" -IsRequired $false
    }
    $UserInput.Add("SubnetName", $SubnetName)

    $IPType = "Ephemeral_Automatic"
    $IPtype = Read-RMString -UserMessage "Enter primary internal IP type" -Options "Ephemeral_Automatic", "Ephemeral_Custom" `
        -DefaultValue "Ephemeral_Automatic" -ParameterName "Primary Internal IP" -IsRequired $false
    if ($IPtype -ieq "Ephemeral_Custom") {
        $UserInput.Add("CustomPrimaryInternalIPType", $true)
        #TODO: validate that the given IP address is in subnet's cidr block
        $PrimaryInternalIP = Read-RMIPAddress -UserMessage "Enter custom Ephemeral IP address" -IsRequired $true `
            -ParameterName "Custom Ephemeral IP Address"
        $UserInput.Add("PrimaryInternalIP", $PrimaryInternalIP)
    }

    $ReadValue = Read-RMString -UserMessage "Do you want to assign external IP address to the target machine" `
        -Options "None", "Automatic" -DefaultValue "None" -ParameterName "External IP Address" -IsRequired $false
    if ($ReadValue -ieq "Automatic") {
        $UserInput.Add("AssignPublicIP", $true)
    }

    $UserInput.Add("NetworkTier", (Read-RMString -UserMessage "Enter network tier" -Options "Premium", "Standard" `
        -DefaultValue "Premium" -ParameterName "Network Tier" -IsRequired $false).ToUpper())

    $DisableTargetDNSRegistration = $false
    if ("windows" -ieq $Source.os_type) {
        $DisableTargetDNSRegistration =  Read-RMBoolean -UserMessage "Disable automatic DNS registration on the target" -DefaultValue "false"
    }
    $UserInput.Add("DisableAutomaticDNSRegistrationOnTheTarget", $DisableTargetDNSRegistration)

    $UserInput.Add("EnableSerialPortAccess", (Read-RMBoolean -UserMessage "Enable remote access through serial port" -DefaultValue "false"))

    $NetworkTags = @()
    if ((Read-RMBoolean -UserMessage "Allow HTTP traffic through firewalls" -DefaultValue "false")) {
        $NetworkTags += "http-server"
    }
    if ((Read-RMBoolean -UserMessage "Allow HTTPS traffic through firewalls" -DefaultValue "false")) {
        $NetworkTags += "https-servers"
    }

    $EnforceTargetNetworkIsolation = Read-RMBoolean -UserMessage "Enforce target network isolation" -DefaultValue "true"
    if (!$EnforceTargetNetworkIsolation) {
        $NetworkNameToNetworkTagsMapping = Get-RMNetworkNameToNetworkTagsMapping -CloudAttribute $CloudAttributes
        $NetworkTagOptions = $NetworkNameToNetworkTagsMapping[$DestinationNetworkName]
        $NetworkTagOptions += "rivermeadow-tw"
        $DefaultValue = ""
        if ("windows" -ieq $Source.os_type) {
            $NetworkTagOptions += "rivermeadow-ft-windows"
            $DefaultValue = "rivermeadow-tw, rivermeadow-ft-windows"
        } else {
            if ($null -ne $Source.control_connection_type -and "agent" -ieq $Source.control_connection_type) {
                $NetworkTagOptions += "rivermeadow-agent", "rivermeadow-ft-linux"
                $DefaultValue = "rivermeadow-tw, rivermeadow-agent, rivermeadow-ft-linux"    
            } else {
                $NetworkTagOptions += "rivermeadow-ft-linux"
                $DefaultValue = "rivermeadow-tw, rivermeadow-ft-linux"    
            }
        }

        $ReadValue = Read-RMToken -UserMessage "Enter one or more network tags separated by commas" `
            -Options $NetworkTagOptions -DefaultValue $DefaultValue -Separator "," `
            -ParameterName "Network Tags" -IsRequired $false

        if ($ReadValue -is [string]) {
            $NetworkTags += @($ReadValue.split(",").Trim())
        } else {
            $NetworkTags += $ReadValue
        }

        if ("linux" -ieq $Source.os_type -and $NetworkTags -notcontains "rivermeadow-agent") {
            $NetworkTags += "rivermeadow-agent"
        }

        if ($NetworkTags -notcontains "rivermeadow-tw") {
            $NetworkTags += "rivermeadow-tw"
        }
    } else {
        $NetworkTags += "rivermeadow-tw"
        if ("linux" -ieq $Source.os_type) {
            if ($null -ne $Source.control_connection_type -and "agent" -ieq $Source.control_connection_type) {
                $NetworkTags += "rivermeadow-agent"
            }
        }
    }

    $UserInput.Add("NetworkTag", $NetworkTags)
    $UserInput.Add("EnforceTargetNetworkIsolation", $EnforceTargetNetworkIsolation)

    $UserInput.Add("ShutdownSource", (Read-RMBoolean -UserMessage "Shutdown source after data is fully migrated" -DefaultValue "false"))
    $UserInput.Add("ShutdownTarget", (Read-RMBoolean -UserMessage "Shutdown target after data is fully migrated" -DefaultValue "false"))
    $LinuxOSLicensing = $true
    $WindowsOSLicensing = $false
    if ("linux" -ieq $Source.os_type) {
        $LinuxOSLicensing = Read-RMBoolean -UserMessage "Premium OS licensing" -DefaultValue "true"
    } else {
        $WindowsOSLicensing = Read-RMBoolean -UserMessage "Bring your own OS license" -DefaultValue "false"
    }
    if (!$LinuxOSLicensing -or $WindowsOSLicensing) {
        $UserInput.Add("OSBYOL", "byol")
    } else {
        $UserInput.Add("OSBYOL", "")
    }
    
    if ("windows" -ieq $Source.os_type) {
        $SQLLicenses = Get-RMSQLLicenseMapping
        $SQLLicense = Read-RMString -UserMessage "Enter SQL license" -Options $SQLLicenses.Keys `
            -DefaultValue "None" -IsRequired $false -ParameterName "SQL License"
        $UserInput.Add("SQLLicense", $SQLLicenses[$SQLLicense])
    } else {
        $UserInput.Add("SQLLicense", "")
    }

    $OSMUpgradeUserInput = @{}
    Read-RMOSMUpgradeOption -Source $Source -UpdatedUserInput $OSMUpgradeUserInput
    $UserInput.Add("UpgradeOSVersion", $OSMUpgradeUserInput["UpgradeOSVersion"])
    $UserInput.Add("InPlaceUpgrade", $OSMUpgradeUserInput["InPlaceUpgrade"])

    $SQLServerUpgrade =  Read-RMSQLServerUpgradeOption -Source $Source
    $UserInput.Add("SQLServerUpgrade", $SQLServerUpgrade)

    $UserInput.Add("RemoveRMSAgent", (Read-RMBoolean -UserMessage "Remove RMS agent post migration" -DefaultValue "false"))

    $ReadValue = Read-RMPair -UserMessage "Enter migration instructions in the format 'key=value' and separated by commas" `
        -Separator "=" -DefaultValue "None"
    if (($IgnoreValidationErrors -and $OverrideExistingMigrationError) -or `
            (!$IgnoreValidationErrors -and $OverrideExistingMigrationError) -or `
            $OverrideExistingMigrationWarning) {
        if ([string]::IsNullOrWhiteSpace($ReadValue)) {
            $ReadValue = "override_source_migration=true"
        } else {
            $ReadValue += ",override_source_migration=true"
        }
    }
    $MigrationInstructions = Get-RMStringAsHashtable -InputString $ReadValue
    $UserInput.Add("MigrationInstruction", $MigrationInstructions)

    $Response = New-RMGCPMigrationProfile @UserInput
    $ShouldExit = Start-RMMigrationPreflight -MigrationProfileId $Response.id `
        -IgnoreValidationErrors $IgnoreValidationErrors -RMMigrationReturn $RMMigrationReturn
    if ($ShouldExit) {
        return $RMMigrationReturn
    }

    $IsScheduled = ![string]::IsNullOrWhiteSpace($ScheduledAt)
    $MigrationResponse = Invoke-RMMigrationPost -MigrationProfileResponse $Response
    return Update-RMMigrationReturnAsSuccess -MigrationResponse $MigrationResponse `
        -RMMigrationReturn $RMMigrationReturn -IsScheduledMigration $IsScheduled `
        -ReturnMessage "Migration started successfully, migration ID"
}

function Start-RMGCPOSBasedNonInteractiveMigration {
    param(
        [string] $TargetCloud,
        [string] $SourceIP,
        [string] $ScheduledAt,
        [string] $TargetVMName,
        [string[]] $MountPoint,
        [string[]] $ResizeMountPoint,
        [string] $TransferMethod,
        [string] $Project,
        [string] $Region,
        [string] $Zone,
        [bool] $MigrateToSoleTenantNode,
        [string] $NodeGroupName,
        [string] $MachineType,
        [string[]] $DiskType,
        [string[]] $InstanceLabel,
        [string] $DestinationNetworkName,
        [string] $SubnetName,
        [string] $PrimaryInternalIP,
        [string] $CustomEphemeralIPAddress,
        [string] $ExternalIP,
        [string] $NetworkTier,
        [bool] $DisableAutomaticDNSRegistrationOnTheTarget,
        [bool] $EnableSerialPortAccess,
        [bool] $AllowHTTPTraffic,
        [bool] $AllowHTTPSTraffic,
        [bool] $EnforceTargetNetworkIsolation,
        [string[]] $NetworkTag,
        [bool] $ShutdownSource,
        [bool] $ShutdownTarget,
        [bool] $OSBYOL,
        [string] $SQLLicense,
        [string] $OSModernization,
        [string] $UpgradeOSVersion,
        [hashtable[]] $UpgradeSQLServer,
        [bool] $RemoveRMSAgent,
        [string[]] $MigrationInstruction,
        [bool] $IgnoreValidationError,
        [bool] $OverrideExistingMigration
    )
    $CloudAccount = $null
    $Source = $null
    $IsSourceAndTargetCloudPresent = $true
    $Errors = @()
    [RMMigrationReturn] $RMMigrationReturn = [RMMigrationReturn]::new()

    $Errors, $Warnings, $IsSourceAndTargetCloudPresent, $IsRequiredParametersPresentAndValid = Confirm-RMGCPFullMigrationParameter $PSBoundParameters 
    
    try {
        if (![string]::IsNullOrWhiteSpace($TargetCloud)) {
            $CloudAccount, $ErrorString = Get-RMCloudAccountByName -CloudAccountName $TargetCloud -AccountType "gcp"
            if (![string]::IsNullOrWhiteSpace($ErrorString)) {
                $Errors += $ErrorString
                $IsSourceAndTargetCloudPresent = $false
            } else {    
                #TODO: Move this call after adding validation of user input through FE endpoint, at that time
                # this call should be just before we trigger validation.
                $CloudAttributes = Get-RMCloudAttribute -CloudAccount $CloudAccount
            }
        }
    } catch [System.Management.Automation.ItemNotFoundException] {
        $Errors += $PSItem.Exception.Message
        $IsSourceAndTargetCloudPresent = $false
    }

    $ShouldExit = $false
    try {
        if (![string]::IsNullOrWhiteSpace($SourceIP)) {
            $Source = Get-RMSourceByIP -IPAddress $SourceIP
        }
    } catch [System.Management.Automation.ItemNotFoundException] {
        $Errors += $PSItem.Exception.Message
        $IsSourceAndTargetCloudPresent = $false
    }

    if (!$IsSourceAndTargetCloudPresent) {
        Add-RMErrorAndWarning -RMReturnObject $RMMigrationReturn -ErrorMessage $Errors -WarningMessage $Warnings
        Out-RMUserParameterResult -ErrorMessage $Errors -WarningMessage $Warnings
        return $RMMigrationReturn
    }

    try {
        $Source, $ShouldExit, $OverrideExistingMigrationWarning, $OverrideExistingMigrationError = `
            Get-RMSourceWithAttribute -Source $Source -CloudAccount $CloudAccount `
            -IgnoreValidationErrors $IgnoreValidationError -RMMigrationReturn $RMMigrationReturn -AccountType "gcp"
        if (!$IgnoreValidationError -and $OverrideExistingMigrationError -and !$OverrideExistingMigration) {
            $Errors += "Please set 'OverrideExistingMigration' to true and try again."
        }
    } catch [System.InvalidOperationException], [System.Management.Automation.RuntimeException] {
        $Errors += $PSItem.Exception.Message
        $IsRequiredParametersPresentAndValid = $false
    }

    $Entitlement = Get-RMEntitlement
    $SourceSQLServerMapping = Get-RMSQLMMappingBySource -Source $Source
    $Errors += Confirm-RMGCPFullMigrationParameterWithSource -UserParameter $PSBoundParameters `
        -Source $Source -SourceSQLServerMapping $SourceSQLServerMapping

    $CloudAttributesSummary = Get-CloudAttributesSummary -CloudAccount $CloudAccount
    $CloudAttributesErrors += Confirm-RMCloudAttributes -UserParameter $PSBoundParameters -CloudAttribute $CloudAttributesSummary
    
    $ProjectId = $CloudAccount.appliance.cloud_properties.project_id
    $CloudAttributesProjectErrors = @()
    if ($CloudAttributesErrors.Count -eq 0) {
        if (![string]::IsNullOrWhiteSpace($Project)) {
            $ProjectObject = Get-RMProjectIdentifierByProjectName -ProjectObject  $CloudAttributesSummary.properties.projects `
                -ProjectName $Project
            $ProjectId = $ProjectObject.identifier
        }
        
        $CloudAttributesProject = Get-RMCloudAttributesByProjectId -CloudAccount $CloudAccount -ProjectId $ProjectId
        $CloudAttributesProjectErrors += Confirm-RMCloudAttributesProject -UserParameter $PSBoundParameters `
            -CloudAttribute $CloudAttributesSummary -CloudAttributesProject $CloudAttributesProject
        if ($CloudAttributesProjectErrors.Count -gt 0) {
            $Errors += $CloudAttributesProjectErrors
        }            
    } else {
        $Errors += $CloudAttributesErrors
    }

    if ([string]::IsNullOrWhiteSpace($TargetVMName)) {
        $TargetVMName = $Source.hostname
    }
    
    $SelectedMountPoint = $null
    $SourceMountPoint = Get-MountPoint -Source $Source
    if ($MountPoint.Count -gt 0) {
        $SelectedMountPoint = Get-RMSelectedMount -MountPoints $SourceMountPoint -DifferenceList $MountPoint -IncludeEqual $true
    } else {
        # If no mount points are given then take all mount points on the source as the selected mount points
        $SelectedMountPoint = $SourceMountPoint
    }

    $MountsResize = @{}
    if ($null -ne $ResizeMountPoint -and $ResizeMountPoint.Count -gt 0) {
        $MountsResize, $MountsResizeErrors = Get-RMResizeMountsPoint -ResizeMountPoints $ResizeMountPoint -SelectedMountPoints $SelectedMountPoint -Source $Source
        if ($null -eq $MountsResize) {
            $Errors += $MountsResizeErrors
        }

        if ("" -ne $TransferMethod -and "block-based" -eq $TransferMethod) {
            $Errors += "TransferMethod needs to be 'file-based' if you want to resize the mount points."
        }
        $TransferMethod = "file-based"
    } else {
        $TransferMethod, $ErrorString = Get-RMTransferMethod -Source $Source -SelectedMountPoints $SelectedMountPoint `
            -TransferMethod $TransferMethod
        if (![string]::IsNullOrWhiteSpace($ErrorString)) {
            $Errors += $ErrorString
        }
    }   

    if  ($CloudAttributes.properties.projects[0].identifier -ieq $ProjectId) {
        if ([string]::IsNullOrWhiteSpace($Region)) {
            $Region = $CloudAttributes.properties.projects[0].regions[0].name
        }

        if ([string]::IsNullOrWhiteSpace($Zone)) {
            $Zone = $CloudAttributes.properties.projects[0].regions[0].zones[0].name
        } 
    }
   
    if ($CloudAttributesProjectErrors.Count -eq 0) {
        $CloudAttributesProject = Get-RMCloudAttributesByProjectIdAndRegionAndZone -CloudAccount $CloudAccount `
            -ProjectId $ProjectId -Region $Region -Zone $Zone
        $Errors += Confirm-RMCloudAttributesProjectAndRegionAndZone -UserParameter $PSBoundParameters `
            -CloudAttributesProject $CloudAttributesProject 
    }

    $DiskType = Get-RMDiskTypeBySource -Source $Source -IsInteractive $false -DiskTypeName $DiskType -IsVM $false
    $InstanceLabelAsHashtable = @{}
    try {
        $InstanceLabelAsHashtable = Get-RMStringArrayAsHashtable -InputItems $InstanceLabel -ParameterName "InstanceLabel"
    } catch {
        $Errors += $PSItem.Exception.Message
    }

    $UserInput = @{}
    Add-RMOSUpgrade -UserParameter $PSBoundParameters -UpdatedParameter $UserInput -Source $Source

    if ([string]::IsNullOrWhiteSpace($DestinationNetworkName)) {
        $DestinationNetworkName = $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.network_name
    }    
    if ([string]::IsNullOrWhiteSpace($SubnetName)) {
        $SubnetName = $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.internal_subnet_id
    }

    if (!$MigrateToSoleTenantNode) {
        $NodeGroupName = $null
    }

    $CustomPrimaryInternalIPType = $false
    if ("Ephemeral-custom" -ieq $PrimaryInternalIP) {
        $CustomPrimaryInternalIPType = $true
    } else {
        $CustomEphemeralIPAddress = $null
    }

    $AssignPublicIP = $false
    if ("automatic" -ieq $ExternalIP) {
        $AssignPublicIP = $true
    }

    if ([string]::IsNullOrWhiteSpace($NetworkTier)) {
        $NetworkTier = "PREMIUM"
    }

    $LicenseType = ""
    if ("linux" -ieq $Source.os_type) {
        if ($OSBYOL -eq $false) {
            $LicenseType = "byol"
        }
    } 
    if ("windows" -ieq $Source.os_type) {
        if ($OSBYOL -eq $true) {
            $LicenseType = "byol"
        }
    }

    if ("windows" -ieq $Source.os_type -and ![string]::IsNullOrWhiteSpace($SQLLicense)) {
        $SQLLicenses = Get-RMSQLLicenseMapping
        $SQLLicense = $SQLLicenses[$SQLLicense]
    }

    if("windows" -ine $Source.os_type) {
        $DisableAutomaticDNSRegistrationOnTheTarget = $false
    }

    $DefaultNetworkTags = @()
    $DefaultNetworkTags += "rivermeadow-tw"
    if ("linux" -ieq $Source.os_type) {
        if ("agent" -ieq $Source.control_connection_type) {
            $DefaultNetworkTags += "rivermeadow-agent"
        }
    }

    if (!$EnforceTargetNetworkIsolation) {
        $NetworkTag = $DefaultNetworkTags
        if ("linux" -ieq $Source.os_type) {
            $NetworkTag += "rivermeadow-ft-linux"
        } else {
            $NetworkTag += "rivermeadow-ft-windows"
        }
    } else {
        $NetworkTag += $DefaultNetworkTags
    }

    if ($AllowHTTPTraffic) {
        $NetworkTag += "http-server"
    }

    if ($AllowHTTPSTraffic) {
        $NetworkTag += "https-server"
    }
    
    $SQLServerUpgrade = Get-RMUpgradeSQLServer -UpgradeSQLServer $UpgradeSQLServer -Source $Source `
        -SourceSQLServerMapping $SourceSQLServerMapping 

    $MigrationInstructionAsHashTable = $null
    try {
        if (($IgnoreValidationError -and $OverrideExistingMigration) -or `
                (!$IgnoreValidationError -and $OverrideExistingMigration) -or `
                $OverrideExistingMigrationWarning) {
            $MigrationInstruction += "override_source_migration=true"
        }
        $MigrationInstructionAsHashTable = Get-RMStringArrayAsHashtable -InputItems $MigrationInstruction -ParameterName "MigrationInstruction"
    } catch {
        $Errors += $PSItem.Exception.Message
    }

    if ([string]::IsNullOrWhiteSpace($ScheduledAt)) {
        $ScheduledAt = ""
    } else {
        # We are confirming the date time format here again, as we have observed that if the previous parameter is an
        # array and the content of the array is in invalid format and the "ScheduledAt" is getting the value from the
        # elements of the array with bad input and it will cause 'Convert-RMDateTimeToUTC' to throw an exception, catching
        # that exception is of no use as the user may not have used the "ScheduledAt" parameter and the error is very cryptic.
        # e.g. -InstanceLabel t1=v1, t2 = v2 (these excess spaces is causing the array to misbehave)
        if ((Confirm-RMDateFormat -InputDate $ScheduledAt -DateFormat "MM/dd/yyyy HH:mm")) {
            $ScheduledAt = Convert-RMDateTimeToUTC -InputDateTime $ScheduledAt
        }
    }

    Out-RMUserParameterResult -ErrorMessage $Errors -WarningMessage $Warnings
    if ($Errors.Count -gt 0 -or $ShouldExit) {
        Add-RMErrorAndWarning -RMReturnObject $RMMigrationReturn -ErrorMessage $Errors -WarningMessage $Warnings
        return $RMMigrationReturn
    }
   
    $HashArguments = @{
        CloudAccount = $CloudAccount
        Entitlement = $Entitlement
        CloudAttributes = $CloudAttributes
        ScheduledAt = $ScheduledAt
        Source = $Source
        TargetVMName = $TargetVMName
        SelectedMount = $SelectedMountPoint
        ResizeMountPoint = $MountsResize
        TransferMethod = $TransferMethod
        ProjectId = $ProjectId
        Region = $Region
        Zone = $Zone
        NodeGroupName = $NodeGroupName
        MachineType = $MachineType
        DiskType = $DiskType
        InstanceLabel = $InstanceLabelAsHashtable
        DestinationNetworkName = $DestinationNetworkName
        SubnetName = $SubnetName
        CustomPrimaryInternalIPType = $CustomPrimaryInternalIPType
        PrimaryInternalIP = $CustomEphemeralIPAddress
        AssignPublicIP = $AssignPublicIP
        NetworkTier = $NetworkTier
        DisableAutomaticDNSRegistrationOnTheTarget = $DisableAutomaticDNSRegistrationOnTheTarget
        EnableSerialPortAccess = $EnableSerialPortAccess
        AllowHTTPTraffic = $AllowHTTPTraffic
        AllowHTTPSTraffic = $AllowHTTPSTraffic
        EnforceTargetNetworkIsolation = $EnforceTargetNetworkIsolation
        NetworkTag = $NetworkTag
        UpgradeOSVersion = $UserInput['UpgradeOSVersion']
        InPlaceUpgrade = $UserInput['InPlaceUpgrade']
        SQLServerUpgrade = $SQLServerUpgrade
        ShutdownSource = $ShutdownSource
        ShutdownTarget = $ShutdownTarget
        RemoveRMSAgent = $RemoveRMSAgent
        OSBYOL = $LicenseType
        SQLLicense = $SQLLicense
        MigrationInstruction = $MigrationInstructionAsHashTable
        IgnoreValidationError = $IgnoreValidationError
        OverrideExistingMigration = $OverrideExistingMigration
    }

    $Response = New-RMGCPMigrationProfile @HashArguments
    $ShouldExit = Start-RMMigrationPreflight -MigrationProfileId $Response.id `
        -IgnoreValidationErrors $IgnoreValidationError -RMMigrationReturn $RMMigrationReturn
    if ($ShouldExit) {
        return $RMMigrationReturn
    }

    $IsScheduled = ![string]::IsNullOrWhiteSpace($ScheduledAt)
    $MigrationResponse = Invoke-RMMigrationPost -MigrationProfileResponse $Response
    return Update-RMMigrationReturnAsSuccess -MigrationResponse $MigrationResponse `
        -RMMigrationReturn $RMMigrationReturn -IsScheduledMigration $IsScheduled `
        -ReturnMessage "Migration started successfully, migration ID"  
        
}

function Start-RMInteractiveGCPVMBasedMigration {
    param ()
    
    $CloudAccount = Read-RMCloudAccount -AccountType "gcp" `
        -UserMessage "Enter target cloud that supports VM-based migrations" -VMBasedAppliancesOnly $true
    $Source = Read-RMVMBasedSource -UserMessage "Enter the VM name of the source machine to be migrated" `
        -ParameterName "Source VM name" -IsRequired $true -CloudAccount $CloudAccount

    $Entitlement = Get-RMEntitlement

    [RMMigrationReturn] $RMMigrationReturn = [RMMigrationReturn]::new()
    if ($null -eq $CloudAccount) {
        return Update-RMMigrationReturnAsError `
            -Message "The migration appliance associated with the given source is not ready for use, cannot start the migration" `
            -RMMigrationReturn $RMMigrationReturn
    }

    $CloudAttributes = Get-RMCloudAttribute -CloudAccount $CloudAccount
    $IgnoreValidationError = Read-RMBoolean -UserMessage "Ignore validation errors" -DefaultValue "false"

    $SourceAttributeResult = Get-RMSourceWithAttribute -Source $Source -CloudAccount $CloudAccount `
        -IgnoreValidationErrors $IgnoreValidationError -RMMigrationReturn $RMMigrationReturn `
        -AccountType "gcp"
    $Source, $ShouldExit, $OverrideExistingMigrationWarning, $OverrideExistingMigrationError = $SourceAttributeResult

    if ($ShouldExit) {
        return $RMMigrationReturn
    }

    if ($OverrideExistingMigrationError) {
        $OverrideExistingMigrationError = Read-RMBoolean -UserMessage "Would you like to override the previous migration attempt" -DefaultValue $false
        if (!$IgnoreValidationError -and !$OverrideExistingMigrationError) {
            return $RMMigrationReturn
        }
    }

    $TargetVMName = Read-RMString -UserMessage "Enter target VM Name" -DefaultValue $Source.host `
    -ParameterName "Target VM Name" -IsRequired $false

    $CloudSummary = Get-CloudAttributesSummary -CloudAccount $CloudAccount

    $DefaultProject = Get-RMDefaultProjectByIdentifier -ProjectObject  $CloudSummary.properties.projects `
        -Identifier $CloudAttributes.properties.projects[0].name
    $ProjectName = Read-RMString -UserMessage "Enter project name" -ParameterName "Project Name" `
        -Options $CloudSummary.properties.projects.name -DefaultValue $DefaultProject.name -IsRequired $false
 
    if (0 -eq $Source.attributes.storage.vm_disks.Count) {
        return Update-RMMigrationReturnAsError `
            -Message "Source has no disks, cannot be migrated" -RMMigrationReturn $RMMigrationReturn
    }
    
    $SourceDisksAsString = "Disks to be migrated [" + ((Get-RMVMDiskProperty -Source $Source) -join ", ") + "]"
    Write-Output $SourceDisksAsString | Out-Host
    
    $SelectedDisk = @()
    $ReturnedValue = Get-RMSelectedDisk -Source $Source
    if ($SelectedDisk -is [hashtable]) {
        $SelectedDisk += $ReturnedValue
    } else {
        $SelectedDisk = $ReturnedValue
    }

    Write-Output "Getting cloud attributes for project $ProjectName ..." | Out-Host
    $ProjectIdentifier = Get-RMProjectIdentifierByProjectName -ProjectObject $CloudSummary.properties.projects -ProjectName $ProjectName
    $CloudAttributesProject = Get-RMCloudAttributesByProjectId -CloudAccount $CloudAccount -ProjectId $ProjectIdentifier.identifier

    $HasProjectChanged = $false
    if ($CloudAttributes.properties.projects[0].identifier -ieq $ProjectIdentifier.identifier) {
        $DefaultRegion = $CloudAttributes.properties.projects[0].regions[0].name
        $DefaultZone = $CloudAttributes.properties.projects[0].regions[0].zones[0].name
    } else {
        $HasProjectChanged = $true
    }

    if ($HasProjectChanged) {
        $RegionName = Read-RMString -UserMessage "Enter region name" -ParameterName "Region Name" `
            -Options $CloudAttributesProject.properties.projects.regions.name -IsRequired $true

        $RegionObject = Get-RMRegionByRegionName -RegionName $RegionName -Region $CloudAttributesProject.properties.projects.regions
        $Zone = Read-RMString -UserMessage "Enter zone name" -ParameterName "Zone Name" -Options $RegionObject.zones.name -IsRequired $true
    } else {
        $RegionName = Read-RMString -UserMessage "Enter region name" -ParameterName "Region Name" `
            -Options $CloudAttributesProject.properties.projects.regions.name -DefaultValue $DefaultRegion -IsRequired $false

        $RegionObject = Get-RMRegionByRegionName -RegionName $RegionName -Region $CloudAttributesProject.properties.projects.regions
        if ($DefaultRegion -ieq  $RegionName) {
            $Zone = Read-RMString -UserMessage "Enter zone name" -ParameterName "Zone Name" -Options $RegionObject.zones.name `
            -DefaultValue $DefaultZone -IsRequired $false
        } else {
            $Zone = Read-RMString -UserMessage "Enter zone name" -ParameterName "Zone Name" -Options $RegionObject.zones.name  -IsRequired $true
        }
    }

    $CloudAttributesProject = Get-RMCloudAttributesByProjectIdAndRegionAndZone -CloudAccount $CloudAccount -ProjectId $ProjectIdentifier.identifier `
        -Region $RegionName -Zone $Zone

    $NodeGroupName = Read-RMNodeGroupName -CloudAttribute $CloudAttributes

    $MachineType = Read-RMMachineType -CloudAttribute $CloudAttributesProject
 
    $DiskType = Get-RMDiskTypeBySource -Source $Source -IsInteractive $true -IsVM $true

    $InstanceLabel = Read-RMPair -UserMessage "Enter one or more instance labels in the format 'key=value' and separated by commas" `
        -Separator "=" -DefaultValue "None"
    $InstanceLabelAsHashtable = Get-RMStringAsHashtable -InputString $InstanceLabel
  
    $NetworkObject = $CloudAttributesProject.properties.projects.regions.networks
    if ($HasProjectChanged -or $RegionName -ine $CloudAccount.appliance.cloud_properties.region -or `
            $Zone -ine $CloudAccount.appliance.cloud_properties.az) {
        $Network = Read-RMString -UserMessage "Enter destination network name" -Parameter "Destination Network Name" -Options $NetworkObject.name `
            -IsRequired $true
    } else {
        $Network = Read-RMString -UserMessage "Enter destination network name" -Parameter "Destination Network Name" -Options $NetworkObject.name `
            -DefaultValue $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.network_name -IsRequired $false
    }
    
    $SubnetObject = Get-RMSubnetByNetworkName -NetworkName $Network -NetworkObject $NetworkObject
   
    if ($Network -ieq $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.network_name) {
        $Subnet = Read-RMString -UserMessage "Enter subnet name" -Parameter "Subnet Name" -Options $SubnetObject.name -IsRequired $false `
            -DefaultValue $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.internal_subnet_id
    } else {
        $Subnet = Read-RMString -UserMessage "Enter subnet name" -Parameter "Subnet Name" -Options $SubnetObject.name -IsRequired $true
    }
    
    $PrimaryInternalIPType = $false
    $PrimaryInternalIP = Read-RMString -UserMessage "Enter the primary internal ip" -Options "Ephemeral_Automatic", "Ephemeral_Custom" `
        -DefaultValue "Ephemeral_Automatic" -IsRequired $false -ParameterName "Primary Internal IP Type"
    if ("Ephemeral_Custom" -ieq $PrimaryInternalIP) {
        $PrimaryInternalIPType = $true
        $CustomEphemeralIPAddress = Read-RMIPAddress -UserMessage "Enter custom Ephemeral IP address" -IsRequired $true `
            -ParameterName "Custom Ephemeral IP Address"
    }

    $ExternalIP = Read-RMString -UserMessage "Do you want to assign external IP address to the target machine" -Options "None", "Automatic" `
        -DefaultValue "None" -IsRequired $false -ParameterName "External IP Address"

    $NetworkTier = "PREMIUM"
    $AssignPublicIP = $false
    if ("Automatic" -ieq $ExternalIP) {
        $AssignPublicIP = $true
        $NetworkTier = Read-RMString -UserMessage "Enter network tier" -Options "Premium", "Standard" -DefaultValue "Premium" -IsRequired $false `
            -ParameterName "Network Tier"
        $NetworkTier = Get-RMNetworkTier -NetworkTierName $NetworkTier
    }

    $EnableSerialPortAccess = Read-RMBoolean -UserMessage "Enable remote access through serial port" -DefaultValue $false

    $FirewallsHTTP = Read-RMBoolean -UserMessage "Allow HTTP traffic through firewalls" -DefaultValue $false

    $FirewallsHTTPS = Read-RMBoolean -UserMessage "Allow HTTPS traffic through firewalls" -DefaultValue $false

    $EnforceTargetNetworkIsolation = Read-RMBoolean -UserMessage "Enforce target network isolation" -DefaultValue $true

    $NetworkTag = @()
    if (!$EnforceTargetNetworkIsolation) {
        $NetworkTagObject = Get-RMNetworkByNertworkName -NetworkObject $NetworkObject -NetworkName $Network
        $Options = $NetworkTagObject.target_tags
        $Options += "rivermeadow-tw"
        $DefaultValue = "rivermeadow-tw"
        if ("linux" -ieq $Source.os_type) {
            $Options += "rivermeadow-ft-linux"
            $DefaultValue = $DefaultValue + ", rivermeadow-ft-linux"
            if ("agent" -ieq $Source.control_connection_type) {
                $Options += "rivermeadow-agent"
                $DefaultValue = $DefaultValue + ", rivermeadow-agent"
            }
        } else {
            $Options += "rivermeadow-ft-windows"
            $DefaultValue = $DefaultValue + ", rivermeadow-ft-windows"
        }
     
        $NetworkTag = Read-RMToken -UserMessage "Enter network tags, separated by commas" -ParameterName "Network Tag" `
            -DefaultValue $DefaultValue -Options $Options -IsRequired $false -Separator ","
    } else {
        $NetworkTag += "rivermeadow-tw"
        if ("linux" -ieq $Source.os_type -and "agent" -ieq $Source.control_connection_type) {
            $NetworkTag += "rivermeadow-agent"
        }
    }

    if ($FirewallsHTTP) {
        $NetworkTag += "http-server"
    }

    if ($FirewallsHTTPS) {
        $NetworkTag += "https-server"
    }

    $ShutdownSource = Read-RMBoolean -UserMessage "Shutdown source after data is fully migrated" -DefaultValue "false"

    $ShutdownTarget = Read-RMBoolean -UserMessage "Shutdown target after data is fully migrated" -DefaultValue "false"

    $FinalizeMigration = Read-RMBoolean -UserMessage "Removes the snapshot(s) from the target vm in preparation for a cutover" `
        -DefaultValue $false
    if ("windows" -ieq $Source.os_type) {
        $OSLicensing = Read-RMBoolean -UserMessage "BYOL" -DefaultValue $false
        $SQLLicenseOptions = Get-RMSQLLicenseMapping
        $SQLLicense = Read-RMString -UserMessage "Enter sql license" -Options $SQLLicenseOptions.Keys -DefaultValue "None" `
            -ParameterName "SQL Licensing" -IsRequired $false
        $SQLLicense = $SQLLicenseOptions[$SQLLicense]
    } else {
        $OSLicensing = Read-RMBoolean -UserMessage "Premium license" -DefaultValue $false
        $OSLicensing = !$OSLicensing
    }

    $ReadValue = Read-RMPair -UserMessage "Enter migration instructions in the format 'key=value' and separated by commas" `
        -Separator "=" -DefaultValue "None"
    if (($IgnoreValidationError -and $OverrideExistingMigrationError) -or (!$IgnoreValidationError -and $OverrideExistingMigrationError) -or $OverrideExistingMigrationWarning) {
        if ("" -eq $ReadValue) {
            $ReadValue = "override_source_migration=true"
        } else {
            $ReadValue += ",override_source_migration=true"
        }
    }
    $MigrationInstruction = Get-RMStringAsHashtable -InputString $ReadValue

    $HashArguments = @{
        CloudAccount = $CloudAccount
        Entitlement = $Entitlement
        ScheduledAt = $ScheduledAt
        Source = $Source
        TargetVMName = $TargetVMName
        SelectedDisk = $SelectedDisk
        ProjectId = $ProjectIdentifier.identifier
        Region = $RegionName
        Zone = $Zone
        NodeGroupName = $NodeGroupName
        MachineType = $MachineType
        DiskType = $DiskType
        InstanceLabel = $InstanceLabelAsHashtable
        DestinationNetworkName = $Network
        SubnetName = $Subnet
        PrimaryInternalIPType = $PrimaryInternalIPType
        PrivateIPAddress = $CustomEphemeralIPAddress
        AssignPublicIP = $AssignPublicIP
        NetworkTier = $NetworkTier
        EnableSerialPortAccess = $EnableSerialPortAccess
        EnforceTargetNetworkIsolation = $EnforceTargetNetworkIsolation
        NetworkTag = $NetworkTag
        FinalizeMigration = $FinalizeMigration
        ShutdownSource = $ShutdownSource
        ShutdownTarget = $ShutdownTarget
        OSBYOL = $OSLicensing
        SQLLicense = $SQLLicense
        MigrationInstruction = $MigrationInstruction
        IgnoreValidationError = $IgnoreValidationError
    }

    $Response = New-RMGCPVMBasedMigrationProfile @HashArguments
    $ShouldExit = Start-RMMigrationPreflight -MigrationProfileId $Response.id `
        -IgnoreValidationErrors $IgnoreValidationError -RMMigrationReturn $RMMigrationReturn
    if ($ShouldExit) {
        return $RMMigrationReturn
    }

    $IsScheduled = ![string]::IsNullOrWhiteSpace($ScheduledAt)
    $MigrationResponse = Invoke-RMMigrationPost -MigrationProfileResponse $Response
    return Update-RMMigrationReturnAsSuccess -MigrationResponse $MigrationResponse `
        -RMMigrationReturn $RMMigrationReturn -IsScheduledMigration $IsScheduled `
        -ReturnMessage "Migration started successfully, migration ID"   
}

function Start-RMNonInteractiveGCPVMBasedMigration {
    param(
        [string] $SourceVMName,
        [string] $SourceVMFolderPath,
        [string] $TargetCloud,
        [string] $ScheduledAt,
        [string] $TargetVMName,
        [string[]] $SelectedDiskLabel,
        [string] $Project,
        [string] $Region,
        [string] $Zone,
        [bool] $MigrateToSoleTenantNode,
        [string] $NodeGroupName,
        [string] $MachineType,
        [string[]] $DiskType,
        [string[]] $InstanceLabel,
        [string] $DestinationNetworkName,
        [string] $SubnetName,
        [string] $PrimaryInternalIP,
        [string] $CustomEphemeralIPAddress,
        [string] $ExternalIP,
        [bool] $EnableSerialPortAccess,
        [bool] $AllowHTTPTraffic,
        [bool] $AllowHTTPSTraffic,
        [bool] $EnforceTargetNetworkIsolation,
        [string[]] $NetworkTag,
        [bool] $ShutdownSource,
        [bool] $ShutdownTarget,
        [bool] $OSBYOL,
        [string] $SQLLicense,
        [string] $OSModernization,
        [bool] $RemoveRMSAgent,
        [bool] $FinalizeMigration,
        [string[]] $MigrationInstruction,
        [bool] $IgnoreValidationError,
        [bool] $OverrideExistingMigration
    )

    $CloudAccount = $null
    $Source = $null
    $Errors = @()
    $ShouldExit = $false
    [RMMigrationReturn] $RMMigrationReturn = [RMMigrationReturn]::new()


    $Errors, $Warnings, $IsSourceAndTargetCloudPresent = Confirm-RMGCPVMBasedFullMigrationParameter $PSBoundParameters

    try {
        if (![string]::IsNullOrWhiteSpace($TargetCloud)) {
            $CloudAccount, $ErrorString = Get-RMCloudAccountByName -CloudAccountName $TargetCloud -AccountType "gcp"  -VMBasedAppliancesOnly $true
            if (![string]::IsNullOrWhiteSpace($ErrorString)) {
                $Errors += $ErrorString
                $IsSourceAndTargetCloudPresent = $false
            } else {    
                $CloudAttributes = Get-RMCloudAttribute -CloudAccount $CloudAccount
            }
        }
    } catch [System.Management.Automation.ItemNotFoundException] {
        $Errors += $PSItem.Exception.Message
        $IsSourceAndTargetCloudPresent = $false
    }

    try {
        if (![string]::IsNullOrWhiteSpace($SourceVMName) -and $null -ne $CloudAccount) {
            $Source = Get-RMSourceByVMName -VMName $SourceVMName -SourceVMFolderPath $SourceVMFolderPath `
                        -CloudAccount  $CloudAccount
            if ($Source.collection_type -ine "VM") {
                $Errors += "Source '$SourceVMName' is not a VM-Based source, to migrate this source, please use the cmdlet 'Start-RMGCPOSBasedMigration'."
                Add-RMErrorAndWarning -RMReturnObject $RMMigrationReturn -ErrorMessage $Errors -WarningMessage $Warnings
                Out-RMUserParameterResult -ErrorMessage $Errors -WarningMessage $Warnings
                return $RMMigrationReturn        
            }
        }
    } catch [System.Management.Automation.ItemNotFoundException], [System.Data.DuplicateNameException] {
        $Errors += $PSItem.Exception.Message
        $IsSourceAndTargetCloudPresent = $false
    }

    if (!$IsSourceAndTargetCloudPresent) {
        Add-RMErrorAndWarning -RMReturnObject $RMMigrationReturn -ErrorMessage $Errors -WarningMessage $Warnings
        Out-RMUserParameterResult -ErrorMessage $Errors -WarningMessage $Warnings
        return $RMMigrationReturn
    }

    $Entitlement = Get-RMEntitlement
    try {
        $Source, $ShouldExit, $OverrideExistingMigrationWarning, $OverrideExistingMigrationError = `
            Get-RMSourceWithAttribute -Source $Source -CloudAccount $CloudAccount `
                -IgnoreValidationErrors $IgnoreValidationError -RMMigrationReturn $RMMigrationReturn `
                -AccountType "gcp"
        if (!$IgnoreValidationError -and $OverrideExistingMigrationError -and !$OverrideExistingMigration) {
            $Errors += "Please set 'OverrideExistingMigration' to true and try again."
        }
    } catch [System.InvalidOperationException], [System.Management.Automation.RuntimeException] {
        $Errors += $PSItem.Exception.Message
    }

    if ($null -ne $Source) {
        if ($null -eq $SelectedDiskLabel -or $SelectedDiskLabel.Count -eq 0) {
            # When the user has not selected any disk labels to migrate,
            # select all for the source.
            $Warnings += "No disk labels have been given, all the disks on the source will be migrated."
            $SelectedDiskLabel = Get-RMVMBasedSourceDiskLabel -Source $Source
            $PSBoundParameters["SelectedDiskLabel"] = $SelectedDiskLabel
        }
    
        $SourceErrors = Confirm-RMGCPVMBasedFullMigrationParameterWithSource -UserParameter $PSBoundParameters -Source $Source
        if ($SourceErrors.Count -gt 0) {
            $Errors += $SourceErrors
        }

        $CloudAttributesSummary = Get-CloudAttributesSummary -CloudAccount $CloudAccount
        $CloudAttributesErrors += Confirm-RMCloudAttributes -UserParameter $PSBoundParameters -CloudAttribute $CloudAttributesSummary
        
        if ($CloudAttributesErrors.Count -eq 0) {
            if ([string]::IsNullOrWhiteSpace($Project) ) {
                $ProjectIdentifier = $CloudAttributes.properties.projects
            } else {
                $ProjectIdentifier = Get-RMProjectIdentifierByProjectName -ProjectObject  $CloudAttributesSummary.properties.projects `
                -ProjectName $Project
            }
            
            $CloudAttributesProject = Get-RMCloudAttributesByProjectId -CloudAccount $CloudAccount -ProjectId $ProjectIdentifier.identifier
            $CloudAttributesProjectErrors +=  Confirm-RMCloudAttributesProject -UserParameter $PSBoundParameters `
                -CloudAttribute $CloudAttributesSummary -CloudAttributesProject $CloudAttributesProject
            if ($CloudAttributesProjectErrors.Count -gt 0) {
                $Errors += $CloudAttributesProjectErrors
            }
        } else {
            $Errors += $CloudAttributesErrors
        }
    }

    if ([string]::IsNullOrWhiteSpace($ScheduledAt)) {
        $ScheduledAt = ""
    } else {
        $ScheduledAt = Convert-RMDateTimeToUTC -InputDateTime $ScheduledAt
    }

    $SelectedDisk = Get-RMSelectedDiskByDiskLabel -DiskLabel $SelectedDiskLabel -Source $Source

    if  ($CloudAttributes.properties.projects[0].identifier -ieq $ProjectIdentifier.identifier) {
        if ([string]::IsNullOrWhiteSpace($Region)) {
            $Region = $CloudAttributes.properties.projects[0].regions[0].name
        }

        if ([string]::IsNullOrWhiteSpace($Zone)) {
            $Zone = $CloudAttributes.properties.projects[0].regions[0].zones[0].name
        } 
    }
   
    if ($CloudAttributesProjectErrors.Count -eq 0) {
        $CloudAttributesProject = Get-RMCloudAttributesByProjectIdAndRegionAndZone -CloudAccount $CloudAccount -ProjectId $ProjectIdentifier.identifier `
            -Region $Region -Zone $Zone
        $CloudAttributesProjectAndRegionAndZoneErrors += Confirm-RMCloudAttributesProjectAndRegionAndZone -UserParameter $PSBoundParameters `
            -CloudAttributesProject $CloudAttributesProject 
        if ($CloudAttributesProjectAndRegionAndZoneErrors.Count -gt 0) {
            $Errors += $CloudAttributesProjectAndRegionAndZoneErrors
        }
    }
    
    $DiskType = Get-RMDiskTypeBySource -Source $Source -IsInteractive $false -DiskTypeName $DiskType -IsVM $true -SelectedDiskLabel $SelectedDiskLabel 

    if ([string]::IsNullOrWhiteSpace($DestinationNetworkName)) {
        $DestinationNetworkName = $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.network_name
    }
    
    if ([string]::IsNullOrWhiteSpace($SubnetName)) {
        $SubnetName = $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.internal_subnet_id
    }

    $PrimaryInternalIPType = $false
    if ("Ephemeral-custom" -ieq $PrimaryInternalIP) {
        $PrimaryInternalIPType = $true
    }

    if (!$MigrateToSoleTenantNode) {
        $NodeGroupName = $null
    }

    if ("windows" -ieq $Source.os_type -and ![string]::IsNullOrWhiteSpace($SQLLicense)) {
        $SQLLicenses = Get-RMSQLLicenseMapping
        $SQLLicense = $SQLLicenses[$SQLLicense]
    }

    $AssignPublicIP = $false
    if ("Automatic" -ieq $ExternalIP) {
        $AssignPublicIP = $true
    }

    if ([string]::IsNullOrWhiteSpace($NetworkTier)) {
        $NetworkTier = "PREMIUM"
    }

    if ("linux" -ieq $Source.os_type) {
        $OSBYOL = !$OSBYOL
    }

    $NetworkTag += "rivermeadow-tw"
    if ("linux" -ieq $Source.os_type -and 
        "agent" -ieq $Source.control_connection_type) {
        $NetworkTag += "rivermeadow-agent"
    }
    if (!$EnforceTargetNetworkIsolation) {
        if ("linux" -ieq $Source.os_type) {
            $NetworkTag += "rivermeadow-ft-linux"
        } else {
            $NetworkTag += "rivermeadow-ft-windows"
        }
    }

    if ($AllowHTTPTraffic) {
        $NetworkTag += "http-server"
    }

    if ($AllowHTTPSTraffic) {
        $NetworkTag += "https-server"
    }
    
    $MigrationInstructionAsHashTable = $null
    try {
        if (($IgnoreValidationError -and $OverrideExistingMigration) -or `
                (!$IgnoreValidationError -and $OverrideExistingMigration) -or `
                $OverrideExistingMigrationWarning) {
            $MigrationInstruction += "override_source_migration=true"
        }
        $MigrationInstructionAsHashTable = Get-RMStringArrayAsHashtable -InputItems $MigrationInstruction -ParameterName "MigrationInstruction"
    } catch {
        $Errors += $PSItem.Exception.Message
    }

    Out-RMUserParameterResult -ErrorMessage $Errors -WarningMessage $Warnings
    if ($Errors.Count -gt 0 -or $ShouldExit) {
        Add-RMErrorAndWarning -RMReturnObject $RMMigrationReturn -ErrorMessage $Errors -WarningMessage $Warnings
        return $RMMigrationReturn
    }

    $HashArguments = @{
        CloudAccount = $CloudAccount
        Entitlement = $Entitlement
        ScheduledAt = $ScheduledAt
        Source = $Source
        TargetVMName = $TargetVMName
        SelectedDisk = $SelectedDisk
        ProjectId = $ProjectIdentifier.identifier
        Region = $Region
        Zone = $Zone
        MigrateToSoleTenantNode = $TenantZones
        NodeGroupName = $NodeGroupName
        MachineType = $MachineType
        DiskType = $DiskType
        InstanceLabel = $InstanceLabelAsHashtable
        DestinationNetworkName = $DestinationNetworkName
        SubnetName = $SubnetName
        PrimaryInternalIPType = $PrimaryInternalIPType
        PrivateIPAddress = $CustomEphemeralIPAddress
        AssignPublicIP = $AssignPublicIP
        NetworkTier = $NetworkTier
        EnableSerialPortAccess = $EnableSerialPortAccess
        EnforceTargetNetworkIsolation = $EnforceTargetNetworkIsolation
        NetworkTag = $NetworkTag
        FinalizeMigration = $FinalizeMigration
        ShutdownSource = $ShutdownSource
        ShutdownTarget = $ShutdownTarget
        OSBYOL = $OSBYOL
        SQLLicense = $SQLLicense
        MigrationInstruction = $MigrationInstructionAsHashTable
        IgnoreValidationError = $IgnoreValidationError
    }

    $Response = New-RMGCPVMBasedMigrationProfile @HashArguments
    $ShouldExit = Start-RMMigrationPreflight -MigrationProfileId $Response.id `
        -IgnoreValidationErrors $IgnoreValidationError -RMMigrationReturn $RMMigrationReturn
    if ($ShouldExit) {
        return $RMMigrationReturn
    }

    $IsScheduled = ![string]::IsNullOrEmpty($ScheduledAt)
    $MigrationResponse = Invoke-RMMigrationPost -MigrationProfileResponse $Response
    return Update-RMMigrationReturnAsSuccess -MigrationResponse $MigrationResponse `
        -RMMigrationReturn $RMMigrationReturn -IsScheduledMigration $IsScheduled `
        -ReturnMessage "Migration started successfully, migration ID"
}

function Confirm-RMGCPFullMigrationParameter {
    param(
        [hashtable] $UserParameter
    )

    $Errors = @()
    $Errors, $Warnings, $IsSourceAndTargetCloudPresent =  Confirm-RMCommonParameter -UserParameter $UserParameter
    $GCPOSErrors, $GCPWarnings = Confirm-RMGCPParameter -UserParameter $UserParameter
    $Errors += $GCPOSErrors
    $Warnings += $GCPWarnings
    return $Errors, $Warnings, $IsSourceAndTargetCloudPresent, $IsRequiredParametersPresentAndValid
}


function Confirm-RMGCPFullMigrationParameterWithSource {
    param(
        [hashtable] $UserParameter,
        [System.Object] $Source,
        [System.Object] $SourceSQLServerMapping
    )

    $Errors = @()
    $Errors += Confirm-RMCommonParameterWithSource -UserParameter $UserParameter -Source $Source -SourceSQLServerMapping $SourceSQLServerMapping
    $Errors += Confirm-RMSQLLicense -UserParameter $UserParameter -Source $Source

    if ($Source.os_type -ne 'windows' -and $UserParameter["DisableAutomaticDNSRegistrationOnTheTarget"]) {
        $Errors += "DisableAutomaticDNSRegistrationOnTheTarget can be 'true', only if the source OS type is 'Windows'."
    }

    $DiskTypeCnt = $UserParameter['DiskType'].Count
    $DiskCnt = $Source.attributes.storage.disks.psobject.Properties.Value.device.Count
    if ($DiskTypeCnt -ne $DiskCnt) {
        $Errors += "The source has '$DiskCnt' disks and the number of disk types given by the parameter 'DiskType' are '$DiskTypeCnt'. The number of disk types given should be equal to the number of disks on the source."
    }

    return $Errors
}

function Confirm-RMGCPVMBasedFullMigrationParameter {
    param (
        [hashtable] $UserParameter
    )
    
    $Errors = @()
    $Errors, $Warnings, $IsSourcePresent = Confirm-RMVMBasedCommonParameter -UserParameter $UserParameter
    $GCPErrors, $GCPWarnings = Confirm-RMGCPParameter -UserParameter $UserParameter
    $Errors += $GCPErrors
    $Warnings += $GCPWarnings
    return $Errors, $Warnings, $IsSourcePresent
}


function Confirm-RMGCPVMBasedFullMigrationParameterWithSource {
    param (
        [hashtable] $UserParameter,
        [System.Object] $Source
    )

    $Errors = @()
    $SourceDiskLabels = Get-RMVMBasedSourceDiskLabel -Source $Source
    $DiskLabelsNotFound = @()
    foreach ($DiskLabel in $UserParameter["SelectedDiskLabel"]) {
        if ($SourceDiskLabels -notcontains $DiskLabel) {
            $DiskLabelsNotFound += $DiskLabel
        }
    }

    if ($DiskLabelsNotFound.Count -gt 0) {
        $DiskLabelsNotFoundAsString = $DiskLabelsNotFound -join ", "
        $Errors += "The disk label(s) '$DiskLabelsNotFoundAsString' specified in the parameter 'SelectedDiskLabel' does not exist on the source"
    }
    
    if ($UserParameter.ContainsKey("DiskType") -and ![string]::IsNullOrWhiteSpace($UserParameter["DiskType"])) {
        $ErrorString = Confirm-RMDiskType -SelectedDiskLabel $SelectedDiskLabel -DiskType $UserParameter["DiskType"]
        if (![string]::IsNullOrEmpty($ErrorString)) {
            $Errors += $ErrorString
        }
    }

    $Errors += Confirm-RMSQLLicense -UserParameter $UserParameter -Source $Source
    return $Errors
}

function Confirm-RMSQLLicense {
    param(
        [hashtable] $UserParameter,
        [System.Object] $Source
    )
    $Errors = @()
    if ($Source.os_type -ieq 'windows') {
        if ($UserParameter.ContainsKey("SQLLicense") -and ![string]::IsNullOrWhiteSpace($UserParameter["SQLLicense"])) {
            $SQLLicenses = Get-RMSQLLicenseMapping
            if ($SQLLicenses.Keys -notcontains $UserParameter["SQLLicense"]) {
                $SQLLicensesAsString = $SQLLicenses.Keys -join ", "
                $Errors = "SQLLicense should be one of '$SQLLicensesAsString'."
            }
        }
    } else {
        if ($UserParameter.ContainsKey("SQLLicense") -and ![string]::IsNullOrWhiteSpace($UserParameter["SQLLicense"])) {
            $Errors = "SQLLicense can be used for a Windows source only."
        }
    }

    return $Errors
}

function Confirm-RMGCPParameter {
    param (
        [hashtable] $UserParameter
    )
  
    $Errors = @()
    $Warnings = @()
    if (!$UserParameter.ContainsKey("MachineType") -or [string]::IsNullOrWhiteSpace($UserParameter["MachineType"])) {
        $Errors += "MachineType is required."
    }

    if (!$UserParameter.ContainsKey("DiskType") -or [string]::IsNullOrWhiteSpace($UserParameter["DiskType"])) {
        $Errors += "DiskType is required."
    }

    if ($UserParameter.ContainsKey("PrimaryInternalIP") -and ![string]::IsNullOrWhiteSpace($UserParameter["PrimaryInternalIP"]) -and `
        "Ephemeral-custom" -ieq $UserParameter["PrimaryInternalIP"]) {
        if (!$UserParameter.ContainsKey("CustomEphemeralIPAddress") -or `
            [string]::IsNullOrWhiteSpace($UserParameter["CustomEphemeralIPAddress"])) {
            $Errors += "CustomEphemeralIPAddress is required, when 'PrimaryInternalIP' is 'Ephemeral-custom'."
        } elseif (!(Confirm-RMIPAddress -IPAddress $UserParameter["CustomEphemeralIPAddress"])){
            $Errors += "Invalid custom ephemeral IP address"
        }
    }
    

    if ($UserParameter.ContainsKey("NetworkTier") -and ![string]::IsNullOrWhiteSpace($UserParameter["NetworkTier"]) -and `
        "Premium" -ine $UserParameter["NetworkTier"] -and `
        $UserParameter.ContainsKey("ExternalIP") -and ![string]::IsNullOrWhiteSpace($UserParameter["ExternalIP"]) -and `
        "none" -ieq $UserParameter["ExternalIP"]) {
        $Errors += "'NetworkTier' has to be 'Premium', when 'ExternalIP' is 'none'."
    }

    if ($UserParameter.ContainsKey("EnforceTargetNetworkIsolation") -and `
            $UserParameter["EnforceTargetNetworkIsolation"] -and `
            $UserParameter.ContainsKey("NetworkTag") -and `
            $UserParameter["NetworkTag"].Count -gt 0) {
        $Warnings += "EnforceTargetNetworkIsolation is true, the network tags given in the parameter 'NetworkTag' will be ignored."
    }

    if (!$UserParameter["MigrateToSoleTenantNode"] -and $UserParameter.ContainsKey("NodeGroupName") -and `
        ![string]::IsNullOrWhiteSpace($UserParameter["NodeGroupName"])) {
        $Warnings  +=  "'MigrateToSoleTenantNode' is false, the node group name given in the parameter 'NodeGroupName' will be ignored."  
    }
    return $Errors, $Warnings
}

function Confirm-RMCloudAttributes {
    param(
        [hashtable] $UserParameter,
        [System.Object] $CloudAttribute
    )

    $Errors = @()

    if ($UserParameter.ContainsKey("Project") -or ![string]::IsNullOrWhiteSpace($UserParameter["Project"])) {
        $ValidProject = Confirm-RMProject -ProjectName $UserParameter["Project"] -CloudAttribute $CloudAttribute
        if (!$ValidProject) {
            $Errors += "Invalid Project Name"
        }
    }

    return $Errors
}


function Confirm-RMCloudAttributesProject {
    param (
        [hashtable] $UserParameter,
        [System.Object] $CloudAccount,
        [System.Object] $CloudAttribute,
        [System.Object] $CloudAttributesProject
    )

    $Errors = @()
    $DefaultProject = Get-RMDefaultProjectByIdentifier -ProjectObject  $CloudAttribute.properties.projects `
        -Identifier $CloudAttributes.properties.projects[0].name

    if (!$UserParameter.ContainsKey("Project") -or [string]::IsNullOrWhiteSpace($UserParameter["Project"]) -or `
        $DefaultProject.name -ieq $UserParameter["Project"]) {
            if ($UserParameter.ContainsKey("Region") -and ![string]::IsNullOrWhiteSpace($UserParameter["Region"])) {
                $RegionIsValid = Confirm-RMRegion -RegionName $UserParameter["Region"] -CloudAttributesProject $CloudAttributesProject
                if (!$RegionIsValid) {
                    $Errors += "Invalid Region Name"
                }
            }

            if ($UserParameter.ContainsKey("Zone") -and ![string]::IsNullOrWhiteSpace($UserParameter["Zone"])) {
                $RegionObject = Get-RMRegionByRegionName -RegionName $UserParameter["Region"] `
                    -Region $CloudAttributesProject.properties.projects.regions
                $ZoneIsValid = Confirm-RMZone -ZoneName $UserParameter["Zone"] -Region $RegionObject
                if (!$ZoneIsValid) {
                    $Errors += "Invalid Zone"
                }
            }

            if (($UserParameter.ContainsKey("Region") -and ![string]::IsNullOrWhiteSpace($UserParameter["Region"]) -and `
                $UserParameter["Region"] -ine $CloudAccount.appliance.cloud_properties.region) -or `
                ($UserParameter.ContainsKey("Zone") -and ![string]::IsNullOrWhiteSpace($UserParameter["Zone"]) -and `
                $UserParameter["Zone"] -ine $CloudAccount.appliance.cloud_properties.az) -and `
                (!$UserParameter.ContainsKey("DestinationNetworkName") -or `
                [string]::IsNullOrWhiteSpace($UserParameter["DestinationNetworkName"]))) {
                    $Errors += "DestinationNetworkName is required"
            }

            if ($UserParameter.ContainsKey("DestinationNetworkName") -and `
                ![string]::IsNullOrWhiteSpace($UserParameter["DestinationNetworkName"]) -and `
                $UserParameter["DestinationNetworkName"] -ine $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.network_name -and `
                (!$UserParameter.ContainsKey("SubnetName") -or [string]::IsNullOrWhiteSpace($UserParameter["SubnetName"])) ) {
                $Errors += "SubnetName is required"
            }
    } else {
        if ($DefaultProject.name -ine $UserParameter["Project"]) {
            if ($UserParameter.ContainsKey("Region") -and ![string]::IsNullOrWhiteSpace($UserParameter["Region"])) {
                $RegionIsValid = Confirm-RMRegion -RegionName $UserParameter["Region"] -CloudAttributesProject $CloudAttributesProject
                if (!$RegionIsValid) {
                    $Errors += "Invalid Region Name"
                }
            } else {
                $Errors += "Region is required."
            }

            if ($UserParameter.ContainsKey("Zone") -and ![string]::IsNullOrWhiteSpace($UserParameter["Zone"])) {
                $RegionObject = Get-RMRegionByRegionName -RegionName $UserParameter["Region"] `
                    -Region $CloudAttributesProject.properties.projects.regions
                $ZoneIsValid = Confirm-RMZone -ZoneName $UserParameter["Zone"] -Region $RegionObject
                if (!$ZoneIsValid) {
                    $Errors += "Invalid Zone"
                }
            } else {
                $Errors += "Zone is required."
            }

            if (!$UserParameter.ContainsKey("DestinationNetworkName") -or `
                [string]::IsNullOrWhiteSpace($UserParameter["DestinationNetworkName"])) {
                $Errors += "DestinationNetworkName is required"
            }

            if (!$UserParameter.ContainsKey("SubnetName") -or `
                [string]::IsNullOrWhiteSpace($UserParameter["SubnetName"])) {
                    $Errors += "SubnetName is required"
            }
       }
    }
    return $Errors
}

function Confirm-RMCloudAttributesProjectAndRegionAndZone {
    param(
        [hashtable] $UserParameter,
        [System.Object] $CloudAttributesProject
    )

    $Errors = @()
    if ($UserParameter.ContainsKey("MigrateToSoleTenantNode") -and $UserParameter["MigrateToSoleTenantNode"]) {
        if (!$UserParameter.ContainsKey("NodeGroupName") -or `
            [string]::IsNullOrWhiteSpace($UserParameter["NodeGroupName"])) {
            $Errors += "'NodeGroupName' is required, when 'MigrateToSoleTenantNode' is 'true'"
        } else {
            $NodeGroupNames = Get-RMAvailableNodeGroupName -CloudAttribute $CloudAttributesProject
            if ($null -eq $NodeGroupNames) {
                $RegionName = $CloudAttributesProject.properties.projects[0].regions[0].name
                $ZoneName = $CloudAttributesProject.properties.projects[0].regions[0].zones[0].name
                $Errors += "No node groups were found in region '$RegionName' and zone '$ZoneName', please create a node group and try again"
            } else {
                $NodeGroupNameIsValid = Confirm-RMNodeGroupName -NodeGroupName $UserParameter["NodeGroupName"] `
                    -NodeGroupNameArray $NodeGroupNames
                if (!$NodeGroupNameIsValid) {
                    $Errors += "Invalid NodeGroupName"
                }
            }
        }
    }

    if ($UserParameter.ContainsKey("MachineType") -and ![string]::IsNullOrWhiteSpace($UserParameter["MachineType"])) {
        $MachineTypeIsValid = Confirm-RMMachineType -MachineType $UserParameter["MachineType"] `
            -CloudAttributesProject $CloudAttributesProject
        if (!$MachineTypeIsValid) {
            $Errors += "Invalid MachineType"
        }
    }

    if ($UserParameter.ContainsKey("DestinationNetworkName") -and `
        ![string]::IsNullOrWhiteSpace($UserParameter["DestinationNetworkName"])) {
        $DestinationNetworkNameIsValid =  Confirm-RMDestinationNetworkName  `
            -DestinationNetworkName $UserParameter["DestinationNetworkName"] `
            -CloudAttributesProject $CloudAttributesProject
        if (!$DestinationNetworkNameIsValid) {
            $Errors += "Invalid DestinationNetworkName"
        }
    }
    
    if ($UserParameter.ContainsKey("SubnetName") -and ![string]::IsNullOrWhiteSpace($UserParameter["SubnetName"])) {
        $SubnetObject = Get-RMSubnetByNetworkName -NetworkName $UserParameter["DestinationNetworkName"] `
            -NetworkObject $CloudAttributesProject.properties.projects.regions.networks
        $SubnetNameIsValid = Confirm-RMSubnetName -SubnetName $UserParameter["SubnetName"] -Subnet $SubnetObject
        if (!$SubnetNameIsValid) {
            $Errors += "Invalid SubnetName"
        }
    }

    return $Errors
}

function Confirm-RMProject {
    param(
        [string] $ProjectName,
        [System.Object] $CloudAttribute
    )

    $ValidProject = $true
    if ($CloudAttribute.properties.projects.name -notcontains $ProjectName) {
        $ValidProject = $false
    }
    return $ValidProject
}

function Confirm-RMRegion {
    param (
        [string] $RegionName,
        [System.Object] $CloudAttributesProject
    )

    $RegionIsValid = $true
    if ($CloudAttributesProject.properties.projects.regions.name -notcontains $RegionName) {
        $RegionIsValid = $false
    }
    return $RegionIsValid
}

function Confirm-RMZone {
    param (
        [string] $ZoneName,
        [System.Object] $Region
    )
    
    $ZoneIsValid = $true
    if ($Region.zones.name -notcontains $ZoneName) {
        $ZoneIsValid = $false
    }
    return $ZoneIsValid
}

function Confirm-RMNodeGroupName {
    param (
        [string] $NodeGroupName,
        [array] $NodeGroupNameArray
    )

    $NodeGroupIsValid = $true
    if ($NodeGroupNameArray -notcontains $NodeGroupName) {
        $NodeGroupIsValid = $false
    }
    return $NodeGroupIsValid
}

function Confirm-RMMachineType {
    param(
        [string] $MachineType,
        [System.Object] $CloudAttributesProject
    )

    $MachineTypeIsValid = $true
    if ($CloudAttributesProject.properties.projects[0].regions[0].zones[0].machine_types.name -notcontains $MachineType) {
        $MachineTypeIsValid = $false
    }
    return $MachineTypeIsValid
}

function Confirm-RMDestinationNetworkName {
    param(
        [string] $DestinationNetworkName,
        [System.Object] $CloudAttributesProject
    )

    $DestinationNetworkNameIsValid = $true
    if ($CloudAttributesProject.properties.projects.regions.networks.name -notcontains $DestinationNetworkName ) {
        $DestinationNetworkNameIsValid = $false
    }
    return $DestinationNetworkNameIsValid
}

function Confirm-RMSubnetName {
    param (
        [string] $SubnetName,
        [System.Object] $Subnet
    )

    $SubnetNameIsValid = $true
    if ($Subnet.name -notcontains $SubnetName) {
        $SubnetNameIsValid = $false
    }
    return $SubnetNameIsValid
}

function Confirm-RMDiskType {
    param (
        [string[]] $SelectedDiskLabel,
        [string[]] $DiskType
    )

    $DiskTypeCnt = $DiskType.Count
    $DisksCnt = $SelectedDiskLabel.Count
    if ($DiskTypeCnt -ne $DisksCnt) {
        $ErrorString += "The source has '$DisksCnt' disks and the number of volume types given by the parameter 'DiskType' are '$DiskTypeCnt'. The number of disk types given should be equal to the number of disks on the source."
    }

    return $ErrorString
}

Export-ModuleMember -Function Start-RMGCPOSBasedInteractiveMigration, Start-RMInteractiveGCPVMBasedMigration, Start-RMNonInteractiveGCPVMBasedMigration, `
Start-RMGCPOSBasedNonInteractiveMigration