Migration/GCP/GCP.psm1

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"))
    $UserInput.Add("AllowHTTPTraffic", (Read-RMBoolean -UserMessage "Allow HTTP traffic through firewalls" -DefaultValue "false"))
    $UserInput.Add("AllowHTTPSTraffic", (Read-RMBoolean -UserMessage "Allow HTTPS traffic through firewalls" -DefaultValue "false"))
    $EnforceTargetNetworkIsolation = Read-RMBoolean -UserMessage "Enforce target network isolation" -DefaultValue "true"
    if (!$EnforceTargetNetworkIsolation) {
        $NetworkNameToNetworkTagsMapping = Get-RMNetworkNameToNetworkTagsMapping -CloudAttribute $CloudAttributes
        $NetworkTagOptions = $NetworkNameToNetworkTagsMapping[$DestinationNetworkName]
        $NetworkTagOptions += "rivermeadow-tw"
        $NetworkTags = @()
        $DefaultValue = ""
        if ("windows" -ieq $Source.os_type) {
            $NetworkTagOptions += "rivermeadow-ft-windows"
            $DefaultValue = "rivermeadow-tw, rivermeadow-ft-windows"
        } else {
            $NetworkTagOptions += "rivermeadow-agent", "rivermeadow-ft-linux"
            $DefaultValue = "rivermeadow-tw, rivermeadow-agent, rivermeadow-ft-linux"
        }

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

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

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

        if ($NetworkTags -notcontains "rivermeadow-tw") {
            $NetworkTags += "rivermeadow-tw"
        }
        $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"])
    $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"
}