Migration/vSphere/vSphere.psm1

Import-Module -Name $PSScriptRoot\..\..\MigrationProfile\VSphereMigrationProfile
Import-Module -Name $PSScriptRoot\..\..\DifferentialProfile\DifferentialProfile
Import-Module -Name $PSScriptRoot\..\..\Preflight\Preflight
Import-Module -Name $PSScriptRoot\..\..\Util\Util
Import-Module -Name $PSScriptRoot\..\..\Util\MigrationUtil
Import-Module -Name $PSScriptRoot\..\..\CloudAccount\CloudAccount
Import-Module -Name $PSScriptRoot\..\..\RiverMeadow.Source\SourceUtil\SourceUtil
Import-Module -Name $PSScriptRoot\..\..\Validate\Validate
Import-Module -Name $PSScriptRoot\..\..\Common\Common
Import-Module -Name $PSScriptRoot\..\..\Common\Wrappers\Wrappers

function Start-RMNonInteractiveVsphereOsBasedMigration {
    param (
        [string] $CloudAccountName,
        [string] $SourceIP,
        [bool] $RefreshSourceAttributes,
        [string] $TargetVMName,
        [string[]] $MountPoints,
        [string[]] $ResizeMountPoints,
        [string] $TransferMethod,
        [string] $CoresPerSocket,
        [bool] $OverrideMemory,
        [int] $OverrideMemorySize,
        [string] $DatacenterName,
        [bool] $EnableDestinationNetworkIsolation,
        [string] $DestinationNetworkName,
        [string] $IpType,
        [string] $IpAddress,
        [string] $Netmask,
        [string] $DefaultGateway,
        [string] $PrimaryDNS,
        [string] $SecondaryDNS,
        [string] $IsolatedNetworkName,
        [string] $IsolatedIpType,
        [string] $IsolatedIpAddress,
        [string] $IsolatedNetmask,
        [string] $IsolatedDefaultGateway,
        [string] $IsolatedPrimaryDNS,
        [string] $IsolatedSecondaryDNS,
        [bool] $DisableAuthomaticDNSRegistrationOnTheTarget,
        [string] $Cluster,
        [string] $ResourcePool,
        [string] $FolderName,
        [string] $DatastoreCluster,
        [string[]] $DatastoreNames,
        [string[]] $DiskProvisioningTypes,
        [int] $HardwareVersion,
        [bool] $ShutdownSource,
        [bool] $ShutdownTarget,
        [bool] $RemoveRMSAgent,
        [hashtable] $MigrationInstructions,
        [string] $UpgradeOSVersion
    )
    $Errors, $Warnings = Confirm-RMvSphereFullMigrationParameter $PSBoundParameters

    $CloudAccount = $null
    $Source = $null
    try {
        if (![string]::IsNullOrEmpty($CloudAccountName)) {
            $CloudAccount = Get-RMCloudAccountByName -CloudAccountName $CloudAccountName
        }
    } catch [System.Management.Automation.ItemNotFoundException] {
        Write-RMError -Message $PSItem
        Out-RMUserParameterResult -ErrorMessage $Errors -WarningMessage $Warnings
        # We cannot much without a valid cloud account, hence return.
        return
    }

    try {
        if ($null -ne $CloudAccount -and ![string]::IsNullOrEmpty($SourceIP)) {
            $Source = Get-RMSourceWithAttribute -IPAddress $SourceIP -RefreshSourceAttributes $RefreshSourceAttributes -CloudAccount $CloudAccount
        }
    } catch [System.Management.Automation.ItemNotFoundException] {
        Write-RMError -Message $PSItem
        Out-RMUserParameterResult -ErrorMessage $Errors -WarningMessage $Warnings
        # There is no migration without a source, hence return.
        return
    }
    
    if ($null -ne $Source) {
        $Errors += Confirm-RMvSphereFullMigrationParameterWithSource -UserParameter $PSBoundParameters -Source $Source
    }

    Out-RMUserParameterResult -ErrorMessage $Errors -WarningMessage $Warnings
    if ($Errors.Count -gt 0) {
        return
    }

    $UserInput = @{}
    $Entitlement = Get-RMEntitlement
    $UserInput.Add("CloudAccount", $CloudAccount)
    $UserInput.Add("Entitlement", $Entitlement)
    $UserInput.Add("Source", $Source)

    if ([string]::IsNullOrEmpty($TargetVMName)) {
        $TargetVMName = $Source.hostname
    }

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

    $SourceMountPoints = Get-MountPoint -Source $Source
    Compare-RMMountPoint -Source $Source -SourceMountPoints $SourceMountPoints -UserInputMountPoints $MountPoints
    $SelectedMountPoints = Get-RMSelectedMount -MountPoints $SourceMountPoints -DifferenceList $MountPoints -IncludeEqual $true
    $UserInput.Add("SelectedMounts", $SelectedMountPoints)

    $MountsResize = @{}
    if (![string]::IsNullOrEmpty($ResizeMountPoints)) {

        $MountsResize = Get-RMResizeMountsPoint -ResizeMountPoints $ResizeMountPoints -SelectedMountPoints $SelectedMountPoints -Source $Source

        if ("" -ne $TransferMethod -and "block-based" -eq $TransferMethod ) {
            throw "Transfer method needs to be 'file-based' if you want to resize the mount points."
        } else {
            $TransferMethod = "file-based"
        }
    } elseif ("" -eq $TransferMethod) {
        $TransferMethod =  Get-TransferType -Source $Source
    }
    $UserInput.Add("MountsResize", $MountsResize)
    $UserInput.Add("TransferType", $TransferMethod)

    if ("" -eq $CoresPerSocket) {
        $CoresPerSocket = 1
    }
    $UserInput.Add("CoresPerSocket", $CoresPerSocket)
    $UserInput.Add("OverrideMemory", $OverrideMemory)
    $UserInput.Add("OverrideMemorySize", $OverrideMemorySize)

    if ($Source.os_type -eq "windows") {
        $UserInput.Add("DisableAuthomaticDNSRegistrationOnTheTarget", $DisableAuthomaticDNSRegistrationOnTheTarget)
    } else {
        $UserInput.Add("DisableAuthomaticDNSRegistrationOnTheTarget", $false)
    }

    $UserInput.Add("DatacenterName", $DatacenterName)
    $UserInput.Add("EnableDestinationNetworkIsolation", $EnableDestinationNetworkIsolation)
    if ($Source.os_type -eq "windows" -and "" -ne $EnableDestinationNetworkIsolation -and $EnableDestinationNetworkIsolation) {

        if ([string]::IsNullOrEmpty($IpType)) {
            $IpType = "dhcp"
        } else {
            $IpType = $IpType.ToLower()
        }
        $UserInput.Add("IpType", $IpType)

        if ($IpType -ieq "static") {
            $UserInput.Add("IpAddress", $IpAddress)
            $UserInput.Add("Netmask", $Netmask)
            $UserInput.Add("DefaultGateway", $DefaultGateway)
            $UserInput.Add("PrimaryDNS", $PrimaryDNS)
            $UserInput.Add("SecondaryDNS", $SecondaryDNS)
        }

       $DestinationNetworkName, $DvSwitchName = Get-DestinationNetworkAndDvSwitchName -DestinationNetworkName $DestinationNetworkName -IsInteractive $false
       $UserInput.Add("DestinationNetworkName", $DestinationNetworkName)
       $UserInput.Add("DvSwitchName", $DvSwitchName)

       [string[]] $networkNames = $IsolatedNetworkName -split "/"
       if ($networkNames.Count -eq 2) {
           $IsolatedNetworkName = $networkNames[1]
           $IsolatedDvSwitchName = $networkNames[0]
       }
       $UserInput.Add("IsolatedNetworkName", $IsolatedNetworkName)
       $UserInput.Add("IsolatedDvSwitchName", $IsolatedDvSwitchName)

        if ([string]::IsNullOrEmpty($IsolatedIpType)) {
            $IsolatedIpType = "dhcp"
        } else {
            $IsolatedIpType = $IsolatedIpType.ToLower()
        }
        $UserInput.Add("IsolatedIpType", $IsolatedIpType)

        if ($IsolatedIpType -ieq "static") {
            $UserInput.Add("IsolatedIpAddress", $IsolatedIpAddress)
            $UserInput.Add("IsolatedNetmask", $IsolatedNetmask)
            $UserInput.Add("IsolatedDefaultGateway", $IsolatedDefaultGateway)
            $UserInput.Add("IsolatedPrimaryDNS", $IsolatedPrimaryDNS)
            $UserInput.Add("IsolatedSecondaryDNS", $IsolatedSecondaryDNS)
        }
    } else {
        $DestinationNetworkName, $DvSwitchName = Get-DestinationNetworkAndDvSwitchName -DestinationNetworkName $DestinationNetworkName -IsInteractive $false
        $UserInput.Add("DestinationNetworkName", $DestinationNetworkName)
        $UserInput.Add("DvSwitchName", $DvSwitchName)

        if ([string]::IsNullOrEmpty($IpType)) {
            $IpType = "dhcp"
        } else {
            $IpType = $IpType.ToLower()
        }
        $UserInput.Add("IpType", $IpType)
        if ($IpType -ieq "static") {
            $UserInput.Add("IpAddress", $IpAddress)
            $UserInput.Add("Netmask", $Netmask)
            $UserInput.Add("DefaultGateway", $DefaultGateway)
            $UserInput.Add("PrimaryDNS", $PrimaryDNS)
            $UserInput.Add("SecondaryDNS", $SecondaryDNS)
        }
    }

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

    $UserInput.Add("ResourcePool", $ResourcePool)
    $UserInput.Add("FolderName", $FolderName)
    $UserInput.Add("DatastoreCluster", $DatastoreCluster)

    # CHECK IF VALIDATION IS ALREADY CATCHING IT
    if ($DatastoreNames.Count -ne $Source.attributes.storage.disks.psobject.Properties.Value.Count) {
        Throw "The number of datastore names given are '" + $DatastoreNames.Count + "' but source has '" + $Source.attributes.storage.disks.psobject.Properties.Value.Count +"' disks"
    }

    $DiskProvisioningTypes = Get-DiskProvisioningTypeBySource -Source $Source -DiskProvisioningTypes $DiskProvisioningTypes -IsInteractive $false

    $UserInput.Add("DatastoreLocations", $DatastoreNames)
    $UserInput.Add("DisksProvisioningType", $DiskProvisioningTypes)

    $HardwareVersions, $DefaultHardwareVersion, $ToolsPackage = Get-RMHardwareVersionsAndToolsVersion -CloudAccount $CloudAccount
    if (0 -eq $HardwareVersion) {
        $HardwareVersion = $DefaultHardwareVersion
    } else {
        if ($HardwareVersions -notcontains $HardwareVersion) {
            Throw "Invalid hardware version: $HardwareVersion"
        }
    }

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

    $UserInput.Add("ToolsPackage", $ToolsPackage)
    if ("" -eq $MigrationInstructions) {
        $MigrationInstructions = @{}
    }
    $UserInput.Add("MigrationInstructions", $MigrationInstructions)

    $UserInput.Add("ShutdownSource", $ShutdownSource)
    $UserInput.Add("ShutdownTarget", $ShutdownTarget)
    $UserInput.Add("RemoveRMSAgent", $RemoveRMSAgent)

    if ("" -ne $UpgradeOSVersion) {
        $SourceOSMMapping = Get-RMOSMMappingBySource -Source $Source
        $UpgradeOSVersion = $UpgradeOSVersion.Trim('"')
        $UpgradeOSVersion = $UpgradeOSVersion.Trim("'")
        $UserInput.Add("UpgradeOSVersion", $SourceOSMMapping[$UpgradeOSVersion])
        $UserInput.Add("InPlaceUpgrade", $true)
    } else {
        $UserInput.Add("UpgradeOSVersion", $null)
        $UserInput.Add("InPlaceUpgrade", $false)
    }

    $Valid = Confirm-RMVSphereValidateOSBasedMigrationProfile @UserInput
    if (!$Valid) {
        return
    }

    $UserInput["DisksProvisioningType"] = Update-DiskProvisioningTypeForME -DiskProvisioningTypes $UserInput["DisksProvisioningType"]
    $Response = New-RMVSphereMigrationProfile @UserInput

    $RMLoginResult = Get-Variable -Name "RMContext-UserLogin"
    $Uri = Get-Variable -Name "RMContext-ReactorURI"

    $Headers = @{
        Accept = "application/rm+json"
        "X-Auth-Token" = $RMLoginResult.Value.token
    }

    $Params = @{
        Method = "Post"
        Uri = $Uri.Value + "/migrationprofiles/" + $Response.id + "/migrations"
        Headers = $Headers
        ContentType = "application/json"
    }

    $Response = Invoke-RMRestMethod -Params $Params
    Out-MigrationIdFromResponse -Response $Response
}

function Start-RMInteractiveVSphereOSBasedMigration {
    param( )

    $Entitlement = Get-RMEntitlement

    $CloudAccountName = Read-Host "Enter cloud account name"
    $CloudAccount = Get-RMCloudAccountByName -CloudAccountName $CloudAccountName
    if ($null -eq $CloudAccount) {
        $ProjectName = Get-Variable -Name "RMContext-CurrentProjectName" -ValueOnly
        $OrgName = Get-Variable -Name "RMContext-CurrentOrganizationName" -ValueOnly
        throw "Cloud account '$CloudAccountName' does not exist in current project '$ProjectName' of organization '$OrgName'"
    }

    $Source = $null
    $SourceIP = Read-Host "Enter the IP address of the source machine to be migrated"
    if ("" -eq $SourceIP) {
        throw "Source IP address is required to start a migration."
    }

    $RefreshSourceAttributes = $false
    $ReadValue = Read-Host "Refresh source attributes (true/false)[false]"
    if ("" -ne $ReadValue) {
        $RefreshSourceAttributes = [System.Convert]::ToBoolean($ReadValue)
    }
    $Source = Get-RMSourceWithAttribute -IPAddress $SourceIP -RefreshSourceAttributes $RefreshSourceAttributes -CloudAccount $CloudAccount

    $TargetInventory = Get-RMTargetInventory -CloudAccount $CloudAccount

    $TargetVMName = $Source.hostname
    $ReadValue = Read-Host "Enter target VM Name [$TargetVMName]"
    if ("" -ne $ReadValue) {
        $TargetVMName = $ReadValue
    }

    $MountPoints = Get-MountPoint -Source $Source

    if (0 -eq $MountPoints.count) {
        Write-RMError -Message "Source has no mount points, cannot be migrated"
        return
    }

    $MountPointsAsString = $MountPoints.values -join ", "
    Write-Output "Mount points to be migrated [$MountPointsAsString]" | Out-Host
    $ExcludedMountPoints = Get-RMExcludedMountPoint -Source $Source
    $SelectedMountPoints = $MountPoints
    if ("" -ne $ExcludedMountPoints) {
        $ExcludedList = $ExcludedMountPoints.Split(",").Trim()
        $SelectedMountPoints = Get-RMSelectedMount -MountPoints $MountPoints -DifferenceList $ExcludedList  -IncludeEqual $false
    }

    $ReadValue = Read-Host "Resize mount points (true/false) [false]"
    $MountsResize = @{}
    $ResizeMountsBool = $false
    if ("" -ne $ReadValue) {
        $ReadValue = [System.Convert]::ToBoolean($ReadValue)
        if ($ReadValue) {
            $ResizeMountsBool = $true
            $MountsResize = Get-RMInteractiveMountsResize -SelectedMountPoints $SelectedMountPoints -Source $Source
        }
    }

    if (!$ResizeMountsBool) {
        $TransferType = Get-TransferType -Source $Source

        $ReadValue =  Read-Host "Enter transfer type (file-based/block-based)[$TransferType]"
        if ("" -ne $ReadValue) {
            $TransferType = $ReadValue
        }
    } else {
        $TransferType = "file-based"
    }

    $CoresPerSocket = 1
    $ReadValue = Read-Host "Enter cores per socket (1,2)[1]"
    if ("" -ne $ReadValue) {
        if (!($ReadValue -eq "1" -or $ReadValue -eq "2")) {
            Throw "Cores per socket can be 1 or 2"
        }
        $CoresPerSocket = $ReadValue
    }

    $OverrideMemory = $false
    $ReadValue = Read-Host "Override memory (true/false)[false]"
    if ("" -ne $ReadValue) {
        $OverrideMemory = [System.Convert]::ToBoolean($ReadValue)
        if ($OverrideMemory) {
            $OverrideMemorySize = Read-Host "Enter the memory in GB"
        }
    }

    $DatacenterNames = Get-DatacenterList -TargetInventory $TargetInventory
    $DatecenterNamesAsString = $DatacenterNames -join ", "
    $DatacenterName = Read-Host "Enter datacenter name ($DatecenterNamesAsString)"
    if ("" -eq $DatacenterName) {
        Throw "Datacenter name is required"
    } elseif (!($DatacenterName -in $DatacenterNames)) {
        Throw "Datacenter '$DatacenterName' does not exists."
    }

    $NetworkNames = Get-NetworkName -TargetInventory $TargetInventory -datacenterName $DatacenterName
    $NetworkNamesAsString = $NetworkNames -join ", "

    $EnableDestinationNetworkIsolation = $false

    if ($Source.os_type -eq "windows") {
        $ReadValue = Read-Host "Enable Destination network isolation (true/false)[false]"
        if ("" -ne $ReadValue) {
            $EnableDestinationNetworkIsolation = [System.Convert]::ToBoolean($ReadValue)
            if ($EnableDestinationNetworkIsolation) {
                $IpType, $IpAddress, $Netmask, $DefaultGateway, $PrimaryDNS, $SecondaryDNS = Get-MigrationNetworkIPConfig
                $DestinationNetworkName, $DvSwitchName = Get-DestinationNetworkAndDvSwitchName -NetworkNames $NetworkNames -NetworkNamesAsString $NetworkNamesAsString -IsInteractive $true

                $IsolatedNetworkName = Read-Host "Enter isolated network name ($NetworkNamesAsString)"
                if ("" -eq $IsolatedNetworkName) {
                    Throw "Isolated network name is required"
                } elseif(!($IsolatedNetworkName -in $NetworkNames)) {
                    Throw "Isolated network name '$IsolatedNetworkName' does not exists."
                }

                [string[]] $networkNamesArray = $IsolatedNetworkName -split "/"
                if ($networkNamesArray.Count -eq 2) {
                    $IsolatedNetworkName = $networkNamesArray[1]
                    $IsolatedDvSwitchName = $networkNamesArray[0]
                }
                $IsolatedIpType = "dhcp"
                $ReadValue = Read-Host "Enter isolated IP type (static/DHCP)[DHCP]"
                if ("" -ne $ReadValue) {
                    if (!($ReadValue -ieq "static" -or $ReadValue -ieq "DHCP")) {
                        Throw "IP type can be 'static' or 'DHCP'"
                    }
                    $IpType = $ReadValue.ToLower()
                }

                if ($ReadValue -ieq "static") {
                    $IsolatedIpType = $ReadValue
                    $IsolatedIpAddress = Read-Host "Enter isolated IP address"
                    $IsolatedNetmask = Read-Host "Enter isolated netmask"
                    $IsolatedDefaultGateway = Read-Host "Enter isolated default gateway"
                    $IsolatedPrimaryDNS = Read-Host "Enter isolated primary DNS"
                    $IsolatedSecondaryDNS = Read-Host "Enter isolated Secondary DNS" 
                }
            } else {
                $IpType, $IpAddress, $Netmask, $DefaultGateway, $PrimaryDNS, $SecondaryDNS = Get-MigrationNetworkIPConfig
                $DestinationNetworkName, $DvSwitchName = Get-DestinationNetworkAndDvSwitchName -NetworkNames $NetworkNames -NetworkNamesAsString $NetworkNamesAsString -IsInteractive $true
            }
        } else {
            $IpType, $IpAddress, $Netmask, $DefaultGateway, $PrimaryDNS, $SecondaryDNS = Get-MigrationNetworkIPConfig
            $DestinationNetworkName, $DvSwitchName = Get-DestinationNetworkAndDvSwitchName -NetworkNames $NetworkNames -NetworkNamesAsString $NetworkNamesAsString -IsInteractive $true
        }
    } else {
        $IpType, $IpAddress, $Netmask, $DefaultGateway, $PrimaryDNS, $SecondaryDNS = Get-MigrationNetworkIPConfig
        $DestinationNetworkName, $DvSwitchName = Get-DestinationNetworkAndDvSwitchName -NetworkNames $NetworkNames -NetworkNamesAsString $NetworkNamesAsString -IsInteractive $true
    }


    if ($Source.os_type -eq "windows") {
        $DisableAuthomaticDNSRegistrationOnTheTarget = $false
        $ReadValue = Read-Host "Disable automatic DNS registration on the Target (true/false)[false]"
        if ("" -ne $ReadValue) {
            $DisableAuthomaticDNSRegistrationOnTheTarget =  [System.Convert]::ToBoolean($ReadValue)
        }
    } else {
        $DisableAuthomaticDNSRegistrationOnTheTarget = $false
    }

    $Clusters = Get-Cluster -TargetInventory $TargetInventory -datacenterName $DatacenterName
    $ClustersAsString = $Clusters -join ", "
    $Cluster = Read-Host "Enter cluster name ($ClustersAsString)"
    if ("" -eq $Cluster) {
        Throw "Cluster name is required"
    } elseif(!($Cluster -in $Clusters)) {
        Throw "Cluster '$Cluster' does not exists."
    }

    $ResourcePool = Read-Host "Enter resource pool [None]"

    $FolderName = Read-Host "Enter VM folder name [None]"

    $DatastoreClusters = Get-Datastore-Cluster -TargetInventory $TargetInventory -datacenterName $DatacenterName
    $DatastoreClustersAsString = $DatastoreClusters -join ", "
    $ReadValue = Read-Host "Enter datastore cluster ($DatastoreClustersAsString)[None]"
    if ("" -ne $ReadValue) {
        if (!($ReadValue -in $DatastoreClusters)) {
            Throw "Datastore cluster '$ReadValue' does not exists."
        }
        $DatastoreCluster = $ReadValue
    }

    $Datastores = Get-Datastore -TargetInventory $TargetInventory -datacenterName $DatacenterName
    $SelectedDatastores = Get-DatastoreBySource -Source $Source -TargetDatastores $Datastores -IsInteractive $true

    $SelectedProvisioningTypes = Get-DiskProvisioningTypeBySource -Source $Source -IsInteractive $true

    $HardwareVersions, $DefaultHardwareVersion, $ToolsPackage = Get-RMHardwareVersionsAndToolsVersion -CloudAccount $CloudAccount
    $HardwareVersionString = $HardwareVersions -join ", " 
    $HardwareVersion = Read-Host "Enter VM hardware version ($HardwareVersionString)[$DefaultHardwareVersion]"
    if ("" -eq $HardwareVersion) {
        $HardwareVersion = $DefaultHardwareVersion
    }

    $ShutdownSource = $false
    $ReadValue = Read-Host "Shutdown source after data is fully migrated (true/false)[false]"
    if ("" -ne $ReadValue) {
        $ShutdownSource = [System.Convert]::ToBoolean($ReadValue)
    }

    $ShutdownTarget = $false
    $ReadValue = Read-Host "Shutdown target after data is fully migrated (true/false)[false]"
    if ("" -ne $ReadValue) {
        $ShutdownTarget = [System.Convert]::ToBoolean($ReadValue)
    }

    $SourceOSMMapping = Get-RMOSMMappingBySource -Source $Source
    $OSMLabels = $SourceOSMMapping.keys -join ", "
    $ReadValue = Read-Host "Enter the OS version to upgrade to ($OSMLabels)[None]"
    if ("" -ne $ReadValue) {
        $ReadValue = $ReadValue.Trim('"')
        $ReadValue = $ReadValue.Trim("'")
        if ($SourceOSMMapping.keys -notcontains $ReadValue) {
            throw "Please enter one of $OSMLabels and try again"
        }
        $UpgradeOSVersion = $SourceOSMMapping[$ReadValue]
        $InPlaceUpgrade = $true
    } else {
        $UpgradeOSVersion = $null
        $InPlaceUpgrade =  $false
    }

    $RemoveRMSAgent = $false
    $ReadValue = Read-Host "Remove RMS agent post migration (true/false)[false]"
    if ("" -ne $ReadValue) {
        $RemoveRMSAgent = [System.Convert]::ToBoolean($ReadValue)
    }

    $ReadValue = Read-Host "Enter migration instructions in the format 'key/value' and separated by commas [None]"
    $MigrationInstructions = Get-RMStringAsHashtable -InputString $ReadValue

    $HashArguments = @{
        CloudAccount = $CloudAccount
        Entitlement = $Entitlement
        Source = $Source
        TargetVMName = $TargetVMName
        SelectedMounts = $SelectedMountPoints
        MountsResize = $MountsResize
        ShutdownSource = $ShutdownSource
        ShutdownTarget = $ShutdownTarget
        RemoveRMSAgent = $RemoveRMSAgent
        DatacenterName = $DatacenterName
        EnableDestinationNetworkIsolation = $EnableDestinationNetworkIsolation
        DestinationNetworkName = $DestinationNetworkName
        IpType = $IpType
        IpAddress = $IpAddress
        Netmask = $Netmask
        DefaultGateway = $DefaultGateway
        PrimaryDNS = $PrimaryDNS
        SecondaryDNS = $SecondaryDNS
        IsolatedNetworkName = $IsolatedNetworkName
        IsolatedDvSwitchName = $IsolatedDvSwitchName
        IsolatedIpType = $IsolatedIpType
        IsolatedIpAddress = $IsolatedIpAddress
        IsolatedNetmask = $IsolatedNetmask
        IsolatedDefaultGateway = $IsolatedDefaultGateway
        IsolatedPrimaryDNS = $IsolatedPrimaryDNS
        IsolatedSecondaryDNS = $IsolatedSecondaryDNS
        DvSwitchName = $DvSwitchName
        DisableAuthomaticDNSRegistrationOnTheTarget = $DisableAuthomaticDNSRegistrationOnTheTarget
        Cluster = $Cluster
        DatastoreCluster =  $DatastoreCluster
        DatastoreLocations = $SelectedDatastores
        FolderName =  $FolderName
        DisksProvisioningType = $SelectedProvisioningTypes
        TransferType = $TransferType
        CoresPerSocket = $CoresPerSocket
        OverrideMemory = $OverrideMemory
        OverrideMemorySize = $OverrideMemorySize
        ResourcePool = $ResourcePool
        HardwareVersion = $HardwareVersion
        ToolsPackage = $ToolsPackage
        MigrationInstructions = $MigrationInstructions
        UpgradeOSVersion = $UpgradeOSVersion
        InPlaceUpgrade = $InPlaceUpgrade
    }

    $Response = New-RMVSphereMigrationProfile @HashArguments

    $RMLoginResult = Get-Variable -Name "RMContext-UserLogin"
    $Uri = Get-Variable -Name "RMContext-ReactorURI"

    $Headers = @{
        Accept = "application/rm+json"
        "X-Auth-Token" = $RMLoginResult.Value.token
    }

    $Params = @{
        Method = "Post"
        Uri = $Uri.Value + "/migrationprofiles/" + $Response.id + "/migrations"
        Headers = $Headers
        ContentType = "application/json"
    }

    $MigrationResponse = Invoke-RMRestMethod -Params $Params
    Out-MigrationIdFromResponse -Response $MigrationResponse
}

function Start-RMVSphereOSBasedDifferentialMigration {
    param (
        [string] $MigrationId
    )

    Import-Module -Name $PSScriptRoot\..\..\DifferentialProfile\DifferentialProfile -Force

    $OrganizationId = Get-Variable -Name "RMContext-CurrentProjectId" -ValueOnly

    $Name = "PowerShell - Differential Profile - " + [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()

    $Entitlement = Get-RMEntitlement

    $Migration = Get-RMMigrationByIdAndStatus -MigrationId $MigrationId -StatusOnly $true

    $MigrationProfile = Get-MigrationProfile -MigrationProfileId $Migration.migration_profile

    $Source = @{
        "host" = $Migration.hostname
    }

    $TransferType = $Migration.transfer_type

    $TargetProperties = $Migration.target.properties
    $MigrationInstructions =  $Migration.migration_instructions

    $HashArguments = @{
        Name = $Name
        Entitlement = $Entitlement
        OrganizationId = $OrganizationId
        Description = $Description
        ApplianceId = $MigrationProfile.appliance_id
        Schedule = $Schedule
        TransferMode = $TransferMode
        MigrationId = $Migration.id
        SourceId = $Migration.source_id
        Target = $Target
        Source =  $Source
        ShutdownSource = $false
        ShutdownTarget = $false
        RemoveTargetAgent = $false
        PublishMigrationHub = $false
        TransferType = $TransferType
        MigrationInstructions = $MigrationInstructions
        TargetProperties = $TargetProperties
        TargetIp = $Migration.target.ip 
    }
    
    $Response = New-RMDifferentialProfile @HashArguments

    $RMLoginResult = Get-Variable -Name "RMContext-UserLogin"
    $Uri = Get-Variable -Name "RMContext-ReactorURI"

    $Headers = @{
        Accept = "application/rm+json"
        "X-Auth-Token" = $RMLoginResult.Value.token
    }

    $Params = @{
        Method = "Post"
        Uri = $Uri.Value + "/differentialprofiles/" + $Response.id + "/migrations"
        Headers = $Headers
        ContentType = "application/json"
    }

    return Invoke-RMRestMethod -Params $Params |Out-Null
}

function Get-RMTargetInventory {
    param(
        [System.Object] $CloudAccount
    )

    $RMLoginResult = Get-Variable -Name "RMContext-UserLogin"
    $Uri = Get-Variable -Name "RMContext-ReactorURI"

    $Headers = @{
        Accept = "application/rm+json"
        "X-Auth-Token" = $RMLoginResult.Value.token
    }

    $CollectList = New-Object Collections.Generic.List[string]

    $CollectList.Add("datacenter")
    $CollectList.Add("cluster")
    $CollectList.Add("datastore")
    $CollectList.Add("datastore_cluster")
    $CollectList.Add("resource_pool")
    $CollectList.Add("storage_profile")
    $CollectList.Add("vm_folder")
    $CollectList.Add("network")

    $Body = @{
        "appliance_id" = $CloudAccount.appliance.id
        "organization_id" = $CloudAccount.organization_id
        "objects_to_collect" = $CollectList
    } | ConvertTo-Json

    $Params = @{
        Method = "Post"
        Uri = $Uri.Value + "/targetinventories"
        Headers = $Headers
        Body = $Body
        ContentType = "application/json"
    }

    Write-Output "Starting target inventory..." | Out-Host
    $TargetInventory = Invoke-RMRestMethod -Param $Params

    return Watch-RMTargetInventoryStatus -TargetInventoryId $TargetInventory.id
}

function Get-DatacenterList {
    param(
        [System.Object] $TargetInventory
    )

    $DatacenterNames = @()
    foreach ($datacenterName in $TargetInventory.attributes.vSphere.datacenters) {
        $DatacenterNames += $datacenterName.name
    }
    return $DatacenterNames
}

function  Get-NetworkName {
    param (
        [System.Object] $TargetInventory,
        [string] $datacenterName
    )

    $NetworkNames = @()
    foreach ($datacenter in $TargetInventory.attributes.vSphere.datacenters) {
        if ($datacenterName -eq $datacenter.name) {
            foreach($network in $datacenter.networks) {
                if ([string]::IsNullOrEmpty($network.switch_name)) {
                    $NetworkNames += $network.name
                } else {
                    $NetworkNames += ($network.switch_name + "/" + $network.name)
                }
            }
        }
    }
    return  $NetworkNames
}

function Get-Cluster {
    param (
        [System.Object] $TargetInventory,
        [string] $datacenterName
    )

    $Clusters = @()
    foreach ($datacenter in $TargetInventory.attributes.vSphere.datacenters) {
        if ($datacenterName -eq $datacenter.name) {
            foreach($cluster in $datacenter.clusters) {
                $Clusters += $cluster.name
            }
        }
    }
    return $Clusters
}

function Get-Datastore-Cluster {
    param(
        [System.Object] $TargetInventory,
        [string] $datacenterName
    )

    $DatastoreClusters = @()
    foreach ($datacenter in $TargetInventory.attributes.vSphere.datacenters) {
        if ($datacenterName -eq $datacenter.name) {
            foreach($datastoreCluster in $datacenter.datastore_clusters) {
                $DatastoreClusters += $datastoreCluster.name
            }
        }
    }
    return  $DatastoreClusters
}

function Get-Datastore {
    param (
        [System.Object] $TargetInventory,
        [string] $datacenterName
    )

    $Datastores = @()
    foreach ($datacenter in $TargetInventory.attributes.vSphere.datacenters) {
        if ($datacenterName -eq $datacenter.name) {
            foreach($datastore in $datacenter.datastores) {
                $Datastores += $datastore.name
            }
        }
    }
    return  $Datastores
}

function Get-RMHardwareVersionsAndToolsVersion {
    param(
        [System.Object] $CloudAccount
    )

    $HardwareVersions = @()
    $HardwareVersionList = @(8, 9, 10, 11, 13, 14, 15, 17, 18, 19)

    $DefaultVersion = $CloudAccount.vsphere_version

    $ToolsPackage , $DefaultHardwareVersion = Get-RMVsphereVersionInfo -Version $DefaultVersion

    foreach($HardwareVersion in $HardwareVersionList) {
        if ($DefaultHardwareVersion -gt $HardwareVersion -or
         $DefaultHardwareVersion -eq $HardwareVersion) {
            $HardwareVersions += $HardwareVersion
        }
    }

    return $HardwareVersions, $DefaultHardwareVersion, $ToolsPackage
}
function Get-TransferType {
    param (
        [System.Object] $Source
    )

    $TransferType = "file-based"

    if ($Source.os_type -eq "windows") {
        $TransferType = "block-based"
    } else {
        if($Source.attributes.discovered_features.features_list -contains "linux_block_based") {
            $fs = @("ext2", "ext3", "ext4", "xfs")
            $check = $true
            foreach($Mount in $Source.attributes.storage.mounts.psobject.properties.value){
                if ($Mount.nature -eq "disk") {
                    if ($fs -notcontains $Mount.fs_type) {
                        $check = $false;
                        break
                    }
                }
            }
            if ($check) {
                $TransferType = "block-based"
            }
        }
    }
    return  $TransferType
}


function Get-RMVsphereVersionInfo {
    param (
       [string] $Version
    )
    
    $Versions = Get-RMVsphereVersionHashtable

    $ToolsPackage = ""
    $HardwareVersion = ""

    foreach($VsphereVersion in $Versions.GetEnumerator()) {
        if ($Version  -eq $VsphereVersion.Name) {
            $ToolsPackage = $VsphereVersion.Value.tools_version
            $HardwareVersion = $VsphereVersion.Value.hardware_version
            Break
        }
    }
    return $ToolsPackage, $HardwareVersion
}

function Get-RMVsphereVersionHashtable {
    param ()

    $VersionInfo = @{
        "VMC" = @{
            "tools_version" = "vmwaretools-11.3.0.tar.gz"
            "hardware_version" = 19
        }
        "GCVE" = @{
            "tools_version" = "vmwaretools-11.3.0.tar.gz"
            "hardware_version" = 19
        }
        "AVS" = @{
            "tools_version" = "vmwaretools-11.3.0.tar.gz"
            "hardware_version" = 19
        }
        "ESXi_7_0" = @{
            "tools_version" = "vmwaretools-11.0.5.tar.gz"
            "hardware_version" = 17
        }
        "ESXi_6_7_U3" = @{
            "tools_version" = "vmwaretools-10.3.5.tar.gz"
            "hardware_version" = 15
        }
        "ESXi_6_7_U2" = @{
            "tools_version" = "vmwaretools-10.3.5.tar.gz"
            "hardware_version" = 15
        }
        "ESXi_6_7" = @{
            "tools_version" = "vmwaretools-10.3.2.tar.gz"
            "hardware_version" = 14
        }
        "ESXi_6_5" = @{
            "tools_version" = "vmwaretools-10.1.0.tar.gz"
            "hardware_version" = 13
        }
        "ESXi_6_0" = @{
            "tools_version" = "vmwaretools-9.10.0.tar.gz"
            "hardware_version" = 11
        }
    }

    return $VersionInfo
}

function Get-MigrationNetworkIPConfig {
    $IpType = "dhcp"
    $ReadValue = Read-Host "Enter IP type (static/DHCP)[DHCP]"
    if ("" -ne $ReadValue) {
        if (!($ReadValue -ieq "static" -or $ReadValue -ieq "DHCP")) {
            Throw "IP type can be 'static' or 'DHCP'"
        }
        $IpType = $ReadValue.ToLower()
    }
    $IPAddress = ""
    $Netmask = ""
    $DefaultGateway = ""
    $PrimaryDNS = ""
    $SecondaryDNS = ""
    if ($IpType -ieq "static") {
        $IPAddress = Read-Host "Enter IP address"
        $Netmask = Read-Host "Enter netmask"
        $DefaultGateway = Read-Host "Enter default gateway"
        $PrimaryDNS = Read-Host "Enter primary DNS"
        $SecondaryDNS = Read-Host "Enter Secondary DNS"
    }

    return $IpType, $IPAddress, $Netmask, $DefaultGateway, $PrimaryDNS, $SecondaryDNS
}

function Get-DatastoreBySource {
    param(
        [System.Object] $Source,
        [string[]] $TargetDatastores,
        [bool] $IsInteractive
    )
    if (!$IsInteractive) {
        Throw "The function Get-DatastoreBySource does not support non-interactive case"
    }

    if ($TargetDatastores.Count -eq 0) {
        Throw "No datastores were found, migration cannot be started."
    }

    $TargetDatastoresAsString = $TargetDatastores -join ", "
    $Datastores = @()
    $SortedDisks = $Source.attributes.storage.disks.psobject.Properties.Value | Sort-Object -property device
    foreach ($Disk in $SortedDisks) {
        if ($IsInteractive) {
            $Size = $Disk.size_kb/(1024*1024)
            $Size = [math]::round($Size, 2)
            $DefaultDatastore = $TargetDatastores[0]
            $ReadValue = Read-Host "Enter the datastore name for disk of size $Size GiB ($TargetDatastoresAsString)[$DefaultDatastore]"
            if ("" -ne $ReadValue) {
                if (!($ReadValue -in $TargetDatastores)) {
                    Throw "Datastore '$ReadValue' does not exist in the given cluster"
                }
                $Datastores += $ReadValue.Trim()
            } else {
                $Datastores += $DefaultDatastore
            }
        }
    }
    return $Datastores
}

function Get-DiskProvisioningTypeBySource {
    param(
        [System.Object] $Source,
        [string[]] $DiskProvisioningTypes,
        [bool] $IsInteractive
    )
    
    $ProvisioningTypes = "thick-lazy-zeroed", "thick-eager-zeroed", "thin"
    $SortedDisks = $Source.attributes.storage.disks.psobject.Properties.Value | Sort-Object -property device
    $DiskCnt = 0
    $ResultProvisioningTypes = @()

    foreach ($Disk in $SortedDisks) {
        if ($IsInteractive) {
            $Size = $Disk.size_kb/(1024*1024)
            $Size = [math]::round($Size, 2)
            $ProvisioningTypesAsString = $ProvisioningTypes -join ", "
            $ReadValue = Read-Host "Enter disk provisioning type for disk of size $Size GiB ($ProvisioningTypesAsString)[thin]"
            if ("" -ne $ReadValue) {
                if (!($ReadValue -in $ProvisioningTypes)) {
                    Throw "Disk provisioning type '$ReadValue' is not valid"
                }
                $ResultProvisioningTypes += $ReadValue.Trim()
            } else {
                $ResultProvisioningTypes += "thin"
            }
        } else {
            if ($null -ne $DiskProvisioningTypes -and $DiskCnt -lt $DiskProvisioningTypes.Count) {
                $ResultProvisioningTypes += $DiskProvisioningTypes[$DiskCnt].Trim()
            } else {
                $ResultProvisioningTypes += "thin"
            }
        }
        $DiskCnt ++
    }

    return $ResultProvisioningTypes
}

function Update-DiskProvisioningTypeForME {
    param(
        [string[]] $DiskProvisioningTypes
    )
    $Result = @()
    switch ($DiskProvisioningTypes) {
        "thick-lazy-zeroed" {
            $Result += "thick_lazy_zero"
        }
        "thick-eager-zeroed" {
            $Result += "thick_eager_zero"
        }
        "thin" {
            $Result += "thin"
        }
    }

    return $Result
}

function Get-DestinationNetworkAndDvSwitchName {
    param(
        [string[]] $NetworkNames,
        [string] $NetworkNamesAsString,
        [string] $DestinationNetworkName,
        [bool] $IsInteractive
    )
    if ($IsInteractive) {
        $DestinationNetworkName = Read-Host "Enter destination network name ($NetworkNamesAsString)"
        if ([string]::IsNullOrEmpty($DestinationNetworkName)) {
            Throw "Destination network name is required"
        } elseif (!($DestinationNetworkName -in $NetworkNames)) {
            Throw "Destination network name '$DestinationNetworkName' does not exists."
        }    
    } else {
        if ([string]::IsNullOrEmpty($DestinationNetworkName)) {
            Throw "Destination network name is required"
        }
    }

    $DvSwitchName = ""
    [string[]] $networkNamesArray = $DestinationNetworkName -split "/"
    if ($networkNamesArray.Count -eq 2) {
        $DestinationNetworkName = $networkNamesArray[1]
        $DvSwitchName = $networkNamesArray[0]
    }

    return $DestinationNetworkName, $DvSwitchName
}

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

    $Errors, $Warnings = Confirm-RMCommonParameter -UserParameter $UserParameter

    if ($UserParameter.ContainsKey("OverrideMemory") -and $UserParameter["OverrideMemory"] -eq $true ) {
        if (!$UserParameter.ContainsKey("OverrideMemorySize") -or $UserParameter["OverrideMemorySize"] -eq 0) {
            $Errors += "OverrideMemorySize is required, when the parameter 'OverrideMemory' is true."
        }
    }

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

    if (!$UserParameter.ContainsKey("DestinationNetworkName") -or [string]::IsNullOrEmpty($UserParameter["DestinationNetworkName"])) {
        $Errors += "DestinationNetworkName is required."
    }
    
    if ($UserParameter.ContainsKey("IpType") -and $UserParameter["IpType"] -ieq "static") {
        if (!$UserParameter.ContainsKey("IpAddress") -or [string]::IsNullOrEmpty($UserParameter["IpAddress"])) {
            $Errors += "IpAddress is required."
        }

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

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

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

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

    return $Errors, $Warnings
}

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

    $Errors = @()
    if ($Source.os_type -ieq "windows" -and $UserParameter.ContainsKey("EnableDestinationNetworkIsolation") `
            -and $UserParameter["EnableDestinationNetworkIsolation"] -eq $true) {
        if (!$UserParameter.ContainsKey("IsolatedNetworkName") -or [string]::IsNullOrEmpty($UserParameter["IsolatedNetworkName"])) {
            $Errors += "IsolatedNetworkName is required."
        }
        if ($UserParameter.ContainsKey("IsolatedIpType") -and $UserParameter["IsolatedIpType"] -ieq "static") {
            if (!$UserParameter.ContainsKey("IsolatedIpAddress") -or [string]::IsNullOrEmpty($UserParameter["IsolatedIpAddress"])) {
                $Errors += "IsolatedIpAddress is required."
            }

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

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

            if (!$UserParameter.ContainsKey("IsolatedPrimaryDNS") -or [string]::IsNullOrEmpty($UserParameter["IsolatedPrimaryDNS"])) {
                $Errors += "IsolatedPrimaryDNS is required."
            }    
        }
    }
    $Errors += Confirm-RMCommonParameterWithSource -UserParameter $UserParameter -Source $Source
    return $Errors
}

Export-ModuleMember -Function Start-RMInteractiveVSphereOSBasedMigration, Start-RMNonInteractiveVsphereOsBasedMigration, `
    Start-RMVSphereOSBasedDifferentialMigration