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\..\..\CloudAccount\CloudAccount
Import-Module -Name $PSScriptRoot\..\..\RiverMeadow.Source\SourceUtil\SourceUtil

function Start-RMNonInteractiveVsphereOsBasedMigration {
    param (
        [Parameter(Mandatory)]
        [string] $CloudAccountName,

        [Parameter(Mandatory)]
        [string] $SourceIP,

        [bool] $RefreshSourceAttributes,

        [Parameter(Mandatory)]
        [string] $TargetVMName,

        [Parameter(Mandatory)]
        [string[]] $MountPoints,

        [string[]] $ResizeMountPoints,

        [ValidateSet("file-based", "block-based", "")]
        [string] $TransferMethod,

        [Parameter(Mandatory)]
        [string] $CoresPerSocket,

        [bool] $OverrideMemory,

        [int] $OverrideMemorySize,

        [Int64] $DataTransferPort,

        [Parameter(Mandatory)]
        [string] $DatacenterName,

        [bool] $EnableDestinationNetworkIsolation,

        [string] $MigrationNetworkName,

        [ValidateSet("static", "dhcp", "")]
        [string] $IpType,

        [string] $IpAddress,

        [string] $Netmask,

        [string] $DefaultGateway,

        [string] $PrimaryDNS,

        [string] $SecondaryDNS,

        [string] $IsolatedNetworkName,

        [ValidateSet("static", "dhcp", "")]
        [string] $IsolatedIpType,

        [string] $IsolatedIpAddress,

        [string] $IsolatedNetmask,

        [string] $IsolatedDefaultGateway,

        [string] $IsolatedPrimaryDNS,

        [string] $IsolatedSecondaryDNS,

        [bool] $DisableAuthomaticDNSRegistrationOnTheTarget,

        [Parameter(Mandatory)]
        [string] $Cluster,

        [string] $ResourcePool,

        [string] $FolderName,

        [string] $DatastoreCluster,

        [Parameter(Mandatory)]
        [string] $DatastoreLocations,

        [string] $DisksProvisioningType,

        [Parameter(Mandatory)]
        [string] $HardwareVersion,

        [bool] $ShutdownSource,

        [bool] $ShutdownTarget,

        [bool] $RemoveRMSAgent,

        [hashtable] $MigrationInstructions,

        [string] $UpgradeOSVersion
    )

    $Entitlement = Get-RMEntitlement

    $UserInput = @{}

    $CloudAccount = Get-RMCloudAccountByName -CloudAccountName $CloudAccountName
    $CloudAttributes = Get-RMCloudAttribute -CloudAccount $CloudAccount -RefreshCloudAttributes $false
    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'"
    }

    $UserInput.Add("CloudAccount", $CloudAccount)
    $UserInput.Add("CloudAttributes", $CloudAttributes)
    $UserInput.Add("Entitlement", $Entitlement)

    $Source = $null
    if ("" -eq $SourceIP) {
        throw "Source IP address is required to start a migration."
    } else {
        $Source = Get-RMSourceWithAttribute -IPAddress $SourceIP -RefreshSourceAttributes $RefreshSourceAttributes
    }
    $UserInput.Add("Source", $Source)

    if ("" -eq $TargetVMName) {
        throw "Target VM name is required."
    }
    $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 ("" -ne $ResizeMountPoints) {

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

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

    if ("" -eq $CoresPerSocket) {
        throw "Cores per socket is required."
    }
    $UserInput.Add("CoresPerSocket", $CoresPerSocket)

    if ("" -ne $OverrideMemory -and $OverrideMemory) {
        if (0 -eq $OverrideMemorySize ) {
            throw "Override memory size is required"
        }
    }
    $UserInput.Add("OverrideMemory", $OverrideMemory)
    $UserInput.Add("OverrideMemorySize", $OverrideMemorySize)

    if ($Source.os_type -eq "windows") {
        if ($DataTransferPort -eq 0) {
            $DataTransferPort = 5995
        }

        $UserInput.Add("DisableAuthomaticDNSRegistrationOnTheTarget", $DisableAuthomaticDNSRegistrationOnTheTarget)
        $UserInput.Add("DataTransferPort", $DataTransferPort)
    } else {
        $UserInput.Add("DisableAuthomaticDNSRegistrationOnTheTarget", $false)
        $UserInput.Add("DataTransferPort", 5995)
    }

    if ("" -eq $DatacenterName) {
        throw "Datacenter Name is required, please provide the datacenter name."
    }
    $UserInput.Add("DatacenterName", $DatacenterName)

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

    if ($Source.os_type -eq "windows" -and "" -ne $EnableDestinationNetworkIsolation -and $EnableDestinationNetworkIsolation) {

       if ("" -eq $IpType) {
          throw "IP type is required."
       } else {
        $UserInput.Add("IpType", $IpType)

            if ($IpType -eq "static") {
                if ("" -eq $IpAddress) {
                    throw "IP Address is requeired."
                }
                $UserInput.Add("IpAddress", $IpAddress)

                if ("" -eq $Netmask) {
                    throw "Netmask is required."
                }
                $UserInput.Add("Netmask", $Netmask)

                if ("" -eq $DefaultGateway) {
                    throw "Default Gateway is required."
                }
                $UserInput.Add("DefaultGateway", $DefaultGateway)

                if ("" -eq $PrimaryDNS) {
                   throw "Primary DNS is required."
                }
                $UserInput.Add("PrimaryDNS", $PrimaryDNS)
                $UserInput.Add("SecondaryDNS", $SecondaryDNS)
            }
       }
       if ("" -eq $MigrationNetworkName) {
            throw "Migration network name is required."
       }

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

       if ("" -eq $IsolatedNetworkName) {
            throw "Isolated network name is required."
       }

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

       if ("" -eq $IsolatedIpType) {
            throw "Isolated ip type id required."
       } else {
            $UserInput.Add("IsolatedIpType", $IsolatedIpType)
            if ($IsolatedIpType -eq "static") {
                if ("" -eq $IsolatedIpAddress) {
                    throw "Isolated IP Address is requeired."
                }
                $UserInput.Add("IsolatedIpAddress", $IsolatedIpAddress)

                if ("" -eq $IsolatedNetmask) {
                    throw "Isolated Netmask is required."
                }
                $UserInput.Add("IsolatedNetmask", $IsolatedNetmask)

                if ("" -eq $IsolatedDefaultGateway) {
                    throw "Isolated Default Gateway is required."
                }
                $UserInput.Add("IsolatedDefaultGateway", $IsolatedDefaultGateway)

                if ("" -eq $IsolatedPrimaryDNS) {
                    throw "Isolated Primary DNS is required."
                }
                $UserInput.Add("IsolatedPrimaryDNS", $IsolatedPrimaryDNS)
                $UserInput.Add("IsolatedSecondaryDNS", $IsolatedSecondaryDNS)
            }

        }
    } else {
        if ("" -eq $MigrationNetworkName) {
            throw "Destination network name is required"
        }

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

        $UserInput.Add("MigrationNetworkName", $MigrationNetworkName)
        $UserInput.Add("DvSwitchName", $DvSwitchName)

        if ("" -eq $IpType) {
            throw "IP type is required"
        } else {
            $UserInput.Add("IpType", $IpType)
            if ($IpType -eq "static") {
                if ("" -eq $IpAddress) {
                    throw " IP Address is requeired."
                }
                $UserInput.Add("IpAddress", $IpAddress)

                if ("" -eq $Netmask) {
                    throw "Netmask is required."
                }
                $UserInput.Add("Netmask", $Netmask)

                if ("" -eq $DefaultGateway) {
                    throw "Default Gateway is required."
                }
                $UserInput.Add("DefaultGateway", $DefaultGateway)

                if ("" -eq $PrimaryDNS) {
                    throw "Primary DNS is required."
                }
                $UserInput.Add("PrimaryDNS", $PrimaryDNS)
                $UserInput.Add("SecondaryDNS", $SecondaryDNS)
            }
        }
    }

    if ("" -eq $Cluster) {
        throw "Cluster is required"
    }
    $UserInput.Add("Cluster", $Cluster)

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

    if ("" -eq $DatastoreLocations) {
        throw "Datastore is required"
    }

    if ("" -eq $DisksProvisioningType) {
        $DisksProvisioningType = "thin"
    }

    [System.Collections.ArrayList] $DatastoreLocationsList =  @()
    [System.Collections.ArrayList] $DisksProvisioningTypeList =  @()

    $SourceMountPoints | ForEach-Object {
        $DatastoreLocationsList += $DatastoreLocations
        $DisksProvisioningTypeList += $DisksProvisioningType
    }

    $UserInput.Add("DatastoreLocations", $DatastoreLocationsList)
    $UserInput.Add("DisksProvisioningType", $DisksProvisioningTypeList)

    if ("" -eq $HardwareVersion) {
        throw "Hardware VM version is required"
    }

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

    $ToolsPackage = Get-Tools-Package -CloudAccount $CloudAccount -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
        $OSMLabels = $SourceOSMMapping.keys -join ", "
        $UpgradeOSVersion = $UpgradeOSVersion.Trim('"')
        $UpgradeOSVersion = $UpgradeOSVersion.Trim("'")
        if ($SourceOSMMapping.keys -notcontains $UpgradeOSVersion) {
            throw "Please enter one of '$OSMLabels' and try again"
        }
        $UserInput.Add("UpgradeOSVersion", $SourceOSMMapping[$UpgradeOSVersion])
        $UserInput.Add("InPlaceUpgrade", $true)
    } else {
        $UserInput.Add("UpgradeOSVersion", $null)
        $UserInput.Add("InPlaceUpgrade", $false)
    }

    $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"
    }

    return Invoke-RMRestMethod -Params $Params
}



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'"
    }

    $RefreshCloudAttributes = $false
    $ReadValue = Read-Host "Refresh cloud attributes (true/false)[false]"
    if ("" -ne $ReadValue) {
        $RefreshCloudAttributes = [System.Convert]::ToBoolean($ReadValue)
    }
    $CloudAttributes = Get-RMCloudAttribute -CloudAccount $CloudAccount -RefreshCloudAttributes $RefreshCloudAttributes

    $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

    $TargetInventory = Get-RMTargetInventory -CloudAccount $CloudAccount

    $TargetVMName = Read-Host "Enter target VM Name"

    $MountPoints = Get-MountPoint -Source $Source

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

    $MountPointsAsString = $MountPoints.values -join ", "
    Write-Output "Mount points to be migrated [$MountPointsAsString]"
    $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 = Read-Host "Enter cores per socket"

    $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"
        }
    }

    $DataTransferPort = 5995
    if ($Source.os_type -eq "windows") {
        $ReadValue = Read-Host "Enter data transfer port [5995]"
        if ("" -ne $ReadValue) {
            $DataTransferPort = $ReadValue
        }
    }


    $DatacenterNames = Get-DatacenterList -TargetInventory $TargetInventory
    $DatacenterName = Read-Host "Enter datacenter name ($DatacenterNames)"

    $NetworkNames = Get-NetworkName -TargetInventory $TargetInventory -datacenterName $DatacenterName

    $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) {
                $MigrationNetworkName = Read-Host "Enter migration network name ($NetworkNames)"

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

                $IpType = Read-Host "Enter ip type (static/dhcp)"
                if ($IpType -eq "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" 
                }

                $IsolatedNetworkName = Read-Host "Enter isolated network name ($NetworkNames)"

                [string[]] $networkNamesArray = $IsolatedNetworkName -split "/"
                if ($networkNamesArray.Count -eq 2) {
                    $IsolatedNetworkName = $networkNamesArray[1]
                    $IsolatedDvSwitchName = $networkNamesArray[0]
                }
                $IsolatedIpType = Read-Host "Enter isolated ip type (static/dhcp)"
                if ($IsolatedIpType -eq "static") {
                    $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 = Read-Host "Enter ip type (static/dhcp)"
                if ($IpType -eq "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"
                }
                $MigrationNetworkName = Read-Host "Enter destination network name ($NetworkNames)"

                [string[]] $networkNamesArray = $MigrationNetworkName -split "/"
                if ($networkNamesArray.Count -eq 2) {
                    $MigrationNetworkName = $networkNamesArray[1]
                    $DvSwitchName = $networkNamesArray[0]
                }
            }
        } else {
            $IpType = Read-Host "Enter ip type (static/dhcp)"
            if ($IpType -eq "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"
            }
            $MigrationNetworkName = Read-Host "Enter destination network name ($NetworkNames)"

            [string[]] $networkNamesArray = $MigrationNetworkName -split "/"
            if ($networkNamesArray.Count -eq 2) {
                $MigrationNetworkName = $networkNamesArray[1]
                $DvSwitchName = $networkNamesArray[0]
            }
        }
    } else {
        $IpType = Read-Host "Enter ip type (static/dhcp)"
        if ($IpType -eq "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"
        }
        $MigrationNetworkName = Read-Host "Enter destination network name ($NetworkNames)"

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


    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
    $Cluster = Read-Host "Enter cluster name ($Clusters)"

    $ResourcePool = Read-Host "Enter resource pool"

    $FolderName = Read-Host "Enter VM folder name"

    $DatastoreClusters = Get-Datastore-Cluster -TargetInventory $TargetInventory -datacenterName $DatacenterName
    $ReadValue = Read-Host "Enter datastore cluster ($DatastoreClusters)[None]"
    if ("" -ne $ReadValue) {
        $DatastoreCluster = $ReadValue
    }

    $Datastores = Get-Datastore -TargetInventory $TargetInventory -datacenterName $DatacenterName
    $DatastoreLocations = Read-Host "Enter datastore locations ($Datastores)"

    $DisksProvisioningType = "thin"
    $ReadValue = Read-Host "Enter disks provisioning type (Thin_Provision/Thin_Provision_Eager_Zeroed/Thin_Provision_Lazy_Zeroed)[Thin_Provision]"
    if ("" -ne $ReadValue) {
       switch ($ReadValue) {
        "Thin_Provision" {  $DisksProvisioningType = "thin" }
        "Thin_Provision_Eager_Zeroed" { $DisksProvisioningType = "thin_eager_zero" }
        "Thin_Provision_Lazy_Zeroed" { $DisksProvisioningType = "thin_lazy_zero" }
       }
    }

    $HardwareVersions = Get-Hardware-Version -CloudAccount $CloudAccount
    $HardwareVersion = Read-Host "Enter VM hardware version ($HardwareVersions)"

    $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



    $ToolsPackage = Get-Tools-Package -CloudAccount $CloudAccount -HardwareVersion $HardwareVersion

    [System.Collections.ArrayList] $DatastoreLocationsList =  @()
    [System.Collections.ArrayList] $DisksProvisioningTypeList =  @()

    $MountPoints | ForEach-Object {
       $DatastoreLocationsList += $DatastoreLocations
       $DisksProvisioningTypeList += $DisksProvisioningType
    }


    $HashArguments = @{
        CloudAccount = $CloudAccount
        CloudAttributes = $CloudAttributes
        Entitlement = $Entitlement
        Source = $Source
        TargetVMName = $TargetVMName
        SelectedMounts = $SelectedMountPoints
        MountsResize = $MountsResize
        ShutdownSource = $ShutdownSource
        ShutdownTarget = $ShutdownTarget
        RemoveRMSAgent = $RemoveRMSAgent
        DatacenterName = $DatacenterName
        EnableDestinationNetworkIsolation = $EnableDestinationNetworkIsolation
        MigrationNetworkName = $MigrationNetworkName
        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 = $DatastoreLocationsList
        FolderName =  $FolderName
        DisksProvisioningType = $DisksProvisioningTypeList
        TransferType = $TransferType
        CoresPerSocket = $CoresPerSocket
        OverrideMemory = $OverrideMemory
        OverrideMemorySize = $OverrideMemorySize
        DataTransferPort =  $DataTransferPort
        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"
    }

    return Invoke-RMRestMethod -Params $Params
}

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-Migration -MigrationId $MigrationId
    
    $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"
    }

    $TargetInventory = Invoke-RMRestMethod -Param $Params

    $Params2 = @{
        Method = "Get"
        Uri = $Uri.Value + "/targetinventories/" + $TargetInventory.id
        Headers = $Headers
    }

    $Response = Invoke-RMRestMethod -Params $Params2

    while ($Response.state -ne "success") {
        $Response = Invoke-RMRestMethod -Params $Params2
    }

    return $Response
}

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

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

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) {
                $NetworkNames +=$network.name
            }
            return  $NetworkNames -join ", "
        }
    }
}

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 -join ", "
        }
    }
}

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 -join ", "
        }
    }
}

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 -join ", "
        }
    }
}

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

    $HardwareVersions = @()
    $Version = $CloudAccount.vsphere_version

    switch ($Version) {
        "VMC" {  $HardwareVersions += "19" }
        "GCVE" {  $HardwareVersions += "18"  }
        "ESXi_7_0" { $HardwareVersions += "17" }
        "ESXi_6_7_U3" {  $HardwareVersions += "15" }
        "ESXi_6_7_U2" { $HardwareVersions += "15" }
        "ESXi_6_7" { $HardwareVersions += "14" }
        "ESXi_6_5" { $HardwareVersions += "13" }
        "ESXi_6_0" { $HardwareVersions += "11" }
    }

    return $HardwareVersions -join ", "
}
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-Tools-Package {
    param (
       [System.Object] $CloudAccount,
       [string] $HardwareVersion
    )

    $Version = $CloudAccount.vsphere_version
    switch ($Version) {
        "VMC" {
             if($HardwareVersion -eq "19") {
                 return "vmwaretools-10.3.10.tar.gz"
             }
        }
        "GCVE" {
            if ($HardwareVersion -eq "18") {
               return "vmwaretools-10.3.10.tar.gz"
            }
        }
        "ESXi_7_0" {
            if ($HardwareVersion -eq "17") {
                return "vmwaretools-10.3.10.tar.gz"
            }
        }
        "ESXi_6_7_U3" {
            if ($HardwareVersion -eq "15") {
                return "vmwaretools-10.3.5.tar.gz"
            }
        }
        "ESXi_6_7_U2" {
            if ($HardwareVersion -eq "15") {
                return "vmwaretools-10.3.5.tar.gz"
            }
        }
        "ESXi_6_7" {
            if ($HardwareVersion -eq "14") {
                return "vmwaretools-10.3.2.tar.gz"
            }
        }
        "ESXi_6_5" {
            if ($HardwareVersion -eq "13") {
                return "vmwaretools-10.1.0.tar.gz"
            }
        }
        "ESXi_6_0" {
            if ($HardwareVersion -eq "11") {
                return "vmwaretools-9.10.0.tar.gz"
            }
        }
    }

}


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