RiverMeadow.Development.Migrate.psm1

using module './Common/Result'
using module './Migration/vSphere/RiverMeadow.VSphereNetworkConfiguration'
using module './Migration/vSphere/RiverMeadow.VSphereVMTag'
Import-Module -Name @(Join-Path $PSScriptRoot Util | Join-Path -ChildPath Util)
Import-Module -Name @(Join-Path $PSScriptRoot Util | Join-Path -ChildPath MigrationUtil)
Import-Module -Name @(Join-Path $PSScriptRoot Common | Join-Path -ChildPath Wrappers | Join-Path -ChildPath Wrappers)
Import-Module -Name @(Join-Path $PSScriptRoot Migration | Join-Path -ChildPath AZURE | Join-Path -ChildPath AZURE)
Import-Module -Name @(Join-Path $PSScriptRoot Migration | Join-Path -ChildPath vSphere | Join-Path -ChildPath vSphere)
Import-Module -Name @(Join-Path $PSScriptRoot DifferentialMigration | Join-Path -ChildPath DifferentialMigration)
Import-Module -Name @(Join-Path $PSScriptRoot DifferentialMigration | Join-Path -ChildPath vSphere | Join-Path -ChildPath VMBasedDifferentialMigration)

function Start-RMAzureOSBasedMigration {
    param(
        [Alias("tc")]
        [string] $TargetCloud,

        [Alias("sip")]
        [string] $SourceIP,

        [Alias("sat")]
        [string] $ScheduledAt,

        [Alias("tvmn")]
        [string] $TargetVMName,

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

        [Alias("crg")]
        [bool] $CreateResourceGroup,

        [Alias("rg")]
        [string] $ResourceGroup,

        [Alias("rgr")]
        [string] $ResourceGroupRegion,

        [Alias("vmsn")]
        [string] $VMSizeName,

        [Alias("vt")]
        [ValidateSet("Standard_SSD", "Standard_HDD", "Premium_SSD")]
        [string[]] $VolumeType,

        [Alias("mp")]
        [string[]] $MountPoint,

        [Alias("rmp")]
        [string[]] $ResizeMountPoint,

        [Alias("vn")]
        [string] $VirtualNetwork,

        [Alias("dnn")]
        [string] $DestinationNetworkName,

        [Alias("apip")]
        [bool] $AssignPublicIP,

        [Alias("spip")]
        [string] $StaticPrivateIP,

        [Alias("etni")]
        [bool] $EnforceTargetNetworkIsolation,

        [Alias("sg")]
        [string] $SecurityGroup,

        [Alias("dtdnsr")]
        [bool] $DisableTargetDNSRegistration,

        [Alias("it")]
        [string[]] $InstanceTag,

        #TODO: parameterset
        [Alias("ao")]
        [ValidateSet("None", "AvailabilityZone", "AvailabilitySet")]
        [string] $AvailabilityOption,

        [Alias("az")]
        [ValidateSet(1, 2, 3)]
        [int] $AvailabilityZone,

        [Alias("cas")]
        [bool] $CreateAvailabilitySet,

        [Alias("as")]
        [string] $AvailabilitySet,

        [Alias("fd")]
        [int] $FaultDomain,

        [Alias("ud")]
        [int] $UpdateDomain,

        #TODO: parameterset
        [Alias("ebd")]
        [bool] $EnableBootDiagnostics,

        [Alias("sa")]
        [string] $StorageAccount,

        #TODO: parameterset
        [Alias("det")]
        [ValidateSet("platform-managed", "customer-managed")]
        [string] $DiskEncryptionType,

        [Alias("es")]
        [string] $EncryptionSet,

        [Alias("ehub")]
        [bool] $EnableHybridUseBenefit,

        [Alias("uosv")]
        [string] $UpgradeOSVersion,

        [Alias("ss")]
        [bool] $ShutdownSource,

        [Alias("st")]
        [bool] $ShutdownTarget,

        [Alias("rrmsas")]
        [bool] $RemoveRMSAgentFromSource,

        [Alias("rrmsat")]
        [bool] $RemoveRMSAgentFromTarget,

        [Alias("mi")]
        [string[]] $MigrationInstruction,

        [Alias("ive")]
        [bool] $IgnoreValidationError,

        [Alias("oem")]
        [bool] $OverrideExistingMigration
    )
    try {
        $LoginStatus = Test-UserLoggedIn
        if ($LoginStatus.ReturnCode -eq [RMReturn]::ERROR) {
            return [RMMigrationReturn]::new($LoginStatus)
        }

        if (0 -eq $PSBoundParameters.Count) {
            return Start-RMAzureOSBasedInteractiveMigration
        } else {
            return Start-RMAzureOSBasedNonInteractiveMigration @PSBoundParameters
        }
    } catch {
        Show-RMError -ErrorObj $PSItem
        $RMReturn = Build-RMError -ErrorObj $PSItem
        # We want to return an object of type RMMigrationReturn from migration cmdlets,
        # hence converting it.
        return [RMMigrationReturn]::new($RMReturn)
    }
}

function Start-RMVSphereOSBasedMigration {
    param(
        [Alias('tc')]
        [string] $TargetCloud,

        [Alias("sip")]
        [string] $SourceIP,

        [Alias("sat")]
        [string] $ScheduledAt,

        [Alias("tvmn")]
        [string] $TargetVMName,

        [Alias("mp")]
        [string[]] $MountPoint,

        [Alias("rmp")]
        [string[]] $ResizeMountPoint,

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

        [Alias("tvmvcpuc")]
        [int] $TargetVMVCPUCount,

        [Alias("cps")]
        [int] $CoresPerSocket,

        [Alias("tvmms")]
        [int] $TargetVMMemorySize,

        [Alias("dcn")]
        [string] $DatacenterName,

        [Alias("edni")]
        [bool] $EnableDestinationNetworkIsolation,

        [Alias("mnc")]
        [RMVSphereMigrationNetworkConfiguration] $MigrationNetworkConfig,

        [Alias("inc")]
        [RMVSphereIsolatedNetworkConfiguration] $IsolatedNetworkConfig,

        [Alias("nicc")]
        [RMVSphereDestinationNetworkConfiguration[]] $DestinationNICConfig,

        [Alias("dadnsrot")]
        [bool] $DisableAutomaticDNSRegistrationOnTheTarget,

        [Alias("cln")]
        [string] $ClusterName,

        [Alias("rpn")]
        [string] $ResourcePoolName,

        [Alias("fn")]
        [string] $FolderName,

        [Alias("dcln")]
        [string] $DatastoreClusterName,

        [Alias("dsn")]
        [string[]] $DatastoreName,

        [Alias("dpt")]
        [ValidateSet("thick-lazy-zeroed", "thick-eager-zeroed", "thin")]
        [string[]] $DiskProvisioningType,

        [Alias("hv")]
        [int] $HardwareVersion,

        [Alias("ss")]
        [bool] $ShutdownSource,

        [Alias("st")]
        [bool] $ShutdownTarget,

        [Alias("rrmsas")]
        [bool] $RemoveRMSAgentFromSource,

        [Alias("rrmsat")]
        [bool] $RemoveRMSAgentFromTarget,

        [Alias("mi")]
        [string[]] $MigrationInstruction,

        [Alias("uosv")]
        [string]  $UpgradeOSVersion,

        [Alias("ive")]
        [bool] $IgnoreValidationError,

        [Alias("oem")]
        [bool] $OverrideExistingMigration
    )
    try {
        $LoginStatus = Test-UserLoggedIn
        if ($LoginStatus.ReturnCode -eq [RMReturn]::ERROR) {
            return [RMMigrationReturn]::new($LoginStatus)
        }

        if (0 -eq $PSBoundPArameters.Count) {
            return Start-RMInteractiveVSphereOSBasedMigration
        } else {
            return Start-RMNonInteractiveVsphereOsBasedMigration  @PSBoundParameters
        }
    } catch {
        Show-RMError -ErrorObj $PSItem
        $RMReturn = Build-RMError -ErrorObj $PSItem
        # We want to return an object of type RMMigrationReturn from migration cmdlets,
        # hence converting it.
        return [RMMigrationReturn]::new($RMReturn)
    }
}

#.ExternalHelp RiverMeadow.Development.Migrate.psm1-help.xml
function Start-RMVSphereVMBasedMigration {
    param(
        [Alias("svmn")]
        [string] $SourceVMName,

        [Alias("svmfp")]
        [string]  $SourceVMFolderPath,

        [Alias('tc')]
        [string] $TargetCloud,

        [Alias("sat")]
        [string] $ScheduledAt,

        [Alias("tvmn")]
        [string] $TargetVMName,

        [Alias("sdl")]
        [string[]] $SelectedDiskLabel,

        [Alias("tvmvcpuc")]
        [int] $TargetVMVCPUCount,

        [Alias("cps")]
        [string] $CoresPerSocket,

        [Alias("tvmms")]
        [int] $TargetVMMemorySize,

        [Alias("dcn")]
        [string] $DatacenterName,

        [Alias("pipa")]
        [bool] $PreserveIPAddress,

        [Alias("nicc")]
        [RMVSphereDestinationNetworkConfiguration[]] $DestinationNICConfig,

        [Alias("pmaca")]
        [bool] $PreserveMACAddress,

        [Alias("vmt")]
        [RMVSphereTag[]] $VMTag,

        [Alias("cln")]
        [string] $ClusterName,

        [Alias("rpn")]
        [string] $ResourcePoolName,

        [Alias("fn")]
        [string] $FolderName,

        [Alias("dcln")]
        [string] $DatastoreClusterName,

        [Alias("dsn")]
        [string[]] $DatastoreName,

        [Alias("spn")]
        [string[]] $StoragePolicyName,

        [Alias("dpt")]
        [ValidateSet("", "thick-lazy-zeroed", "thick-eager-zeroed", "thin")]
        [string[]] $DiskProvisioningType,

        [Alias("ut")]
        [bool] $UpgradeTool,

        [Alias("hv")]
        [int] $HardwareVersion,

        [Alias("ss")]
        [bool] $ShutdownSource,

        [Alias("st")]
        [bool] $ShutdownTarget,

        [Alias("fm")]
        [bool] $FinalizeMigration,

        [Alias("mi")]
        [string[]] $MigrationInstruction,

        [Alias("ive")]
        [bool] $IgnoreValidationError,

        [Alias("oem")]
        [bool] $OverrideExistingMigration
    )
    try {
        $LoginStatus = Test-UserLoggedIn
        if ($LoginStatus.ReturnCode -eq [RMReturn]::ERROR) {
            return [RMMigrationReturn]::new($LoginStatus)
        }

        if (0 -eq $PSBoundPArameters.Count) {
            return Start-RMInteractiveVSphereVMBasedMigration
        } else {
            return Start-RMNonInteractiveVsphereVMBasedMigration  @PSBoundParameters
        }
    } catch {
        Show-RMError -ErrorObj $PSItem
        $RMReturn = Build-RMError -ErrorObj $PSItem
        return [RMMigrationReturn]::new($RMReturn)
    }
}

function Start-RMOSBasedDifferentialMigration {
    param(
        [Alias("mid")]
        [System.Guid] $MigrationId,

        [Alias("sat")]
        [string] $ScheduledAt,

        [Alias("rc")]
        [bool] $RunContinuous,

        [Alias("ss")]
        [bool] $ShutdownSource,

        [Alias("st")]
        [bool] $ShutdownTarget,

        [Alias("rrmsa")]    
        [bool] $RemoveRMSAgent,

        [Alias("mi")]
        [string[]] $MigrationInstruction,

        [Alias("ive")]
        [bool] $IgnoreValidationError,

        [Alias("ump")]
        [bool] $UpdateMountPoint,
    
        [Alias("usm")]
        [hashtable]$UpdateSelectedMount,

        #Update Source Credentials body
        [Alias("usc")]
        [bool] $UpdateSourceCredential,
      
        [Alias("sun")]
        [string] $SourceUsername,
      
        [Alias("susshpk")]
        [bool] $SourceUseSSHPrivateKey,
      
        [Alias("spk")]
        [string] $SourcePrivateKey,
      
        [Alias("spp")]
        [string] $SourcePassphrase,
  
        [Alias("scpp")]
        [string] $SourceConfirmPassphrase,
      
        [Alias("spw")]
        [string] $SourcePassword,
  
        [Alias("scpw")]
        [string] $SourceConfirmPassword,
      
        [Alias("sd")]
        [string] $SourceDomain,
      
        #Update Target Instance body
        [Alias("uti")]
        [bool] $UpdateTargetInstance,
      
        [Alias("tun")]
        [string] $TargetUsername,
      
        [Alias("tusshpk")]
        [bool] $TargetUseSSHPrivateKey,
      
        [Alias("tpk")]
        [string] $TargetPrivateKey,
      
        [Alias("tpp")]
        [string] $TargetPassphrase,
  
        [Alias("tcpp")]
        [string] $TargetConfirmPassphrase,
      
        [Alias("tpw")]
        [string] $TargetPassword,
  
        [Alias("tcpw")]
        [string] $TargetConfirmPassword,
      
        [Alias("td")]
        [string] $TargetDomain,
    
        [Alias("tipa")]
        [string] $TargetIPAddress,

        [Alias("tm")]
        [string] $TransferMethod
    )

    try {
        $LoginStatus = Test-UserLoggedIn
        if ($LoginStatus.ReturnCode -eq [RMReturn]::ERROR) {
            return [RMMigrationReturn]::new($LoginStatus)
        }

        if (0 -eq $PSBoundPArameters.Count) {
            return Start-RMInteractiveDM
        } else {
            return Start-RMNonInteractiveDM  @PSBoundParameters
        }   
    } catch {
        Show-RMError -ErrorObj $PSItem
        $RMReturn = Build-RMError -ErrorObj $PSItem
        # We want to return an object of type RMMigrationReturn from migration cmdlets,
        # hence converting it.
        return [RMMigrationReturn]::new($RMReturn)
    }
}

#.ExternalHelp RiverMeadow.Development.Migrate.psm1-help.xml
function Start-RMVSphereVMBasedDifferentialMigration {
    param(
        [Alias("mid")]
        [System.Guid] $MigrationId,

        [Alias("sat")]
        [string] $ScheduledAt,

        [Alias("rc")]
        [bool] $RunContinuous,

        [Alias("ut")]
        [bool] $UpgradeTool,

        [Alias("ss")]
        [bool] $ShutdownSource,

        [Alias("st")]
        [bool] $ShutdownTarget,

        [Alias("fm")]
        [bool] $FinalizeMigration,

        [Alias("mi")]
        [string[]] $MigrationInstruction,

        [Alias("ive")]
        [bool] $IgnoreValidationError
    )
    try {
        $LoginStatus = Test-UserLoggedIn
        if ($LoginStatus.ReturnCode -eq [RMReturn]::ERROR) {
            return [RMMigrationReturn]::new($LoginStatus)
        }

        if (0 -eq $PSBoundPArameters.Count) {
            return Start-RMInteractiveVSphereVMBasedDifferentialMigration
        } else {
            return Start-RMNonInteractiveVsphereVMBasedDifferentialMigration  @PSBoundParameters
        }
    } catch {
        Show-RMError -ErrorObj $PSItem
        $RMReturn = Build-RMError -ErrorObj $PSItem
        return [RMMigrationReturn]::new($RMReturn)
    }
}

function Stop-RMMigration {
    param (
        [Alias("mid")]
        [System.Guid] $MigrationId
    )
    $Migration = $null
    try {
        $LoginStatus = Test-UserLoggedIn
        if ($LoginStatus.ReturnCode -eq [RMReturn]::ERROR) {
            return $LoginStatus
        }

        if (0 -eq $PSBoundParameters.Count) {
            $UserMigrationId = @()
            $UserMigrationId = Read-RMUUID -UserMessage "Enter the ID of the migration that you want to stop" `
                -ParameterName "Migration ID" -IsRequired $true
            $MigrationId = $UserMigrationId[0]
        }

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

        if("error" -ne $Migration.state -and "success" -ne $Migration.state -and "cancelled" -ne $Migration.state) {

            $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 + "/migrations/" + $MigrationId + "/cancel"
                Headers = $Headers
                ContentType = "application/json"
            }

            $Response = Invoke-RMRestMethod -Params $Params
            $UserMessage = "Migration with ID {0} is stopped" -f $Response.id
            Write-Output $UserMessage | Out-Host
            return [RMReturn]::new([RMReturn]::SUCCESS, @{"Result" = $UserMessage})
        }
    } catch [System.Exception] {
        Show-RMError -ErrorObj $PSItem
        return Build-RMError -ErrorObj $PSItem
    }
    $UserMessage = "Migration is in final state '{0}', cannot be stopped." -f $Migration.state
    Write-RMError -Message $UserMessage
    [RMReturn] $RMReturn = [RMReturn]::new()
    $RMReturn.SetReturnCode([RMReturn]::ERROR)
    $RMReturn.AddRMError([RMError]::new($UserMessage))
    return $RMReturn
}

function Get-RMMigrations {
    param (
        # ID's of the migrations that needs to be fetched
        [Alias("mid")]
        [System.Guid[]] $MigrationId
    )
    try {
        [RMReturn] $RMReturn = [RMReturn]::new()        
        $LoginStatus = Test-UserLoggedIn
        if ($LoginStatus.ReturnCode -eq [RMReturn]::ERROR) {
            return $LoginStatus
        }
    
        if (0 -eq $PSBoundParameters.Count) {
            $MigrationId = Read-RMUUID -UserMessage "Enter one or more migration IDs, separated by commas" -ParameterName "Migration ID" -IsRequired $true
        } elseif (0 -eq $MigrationId.Count) {
            $UserMessage = "At least one migration ID is required for parameter 'MigrationId'"
            Write-RMError -Message $UserMessage
            $RMReturn.SetReturnCode([RMReturn]::ERROR)
            $RMReturn.AddRMError([RMError]::new($UserMessage))
            return $RMReturn
        }
    
        $Migrations = Get-RMMigrationsByIds -MigrationIds $MigrationId
        $Result = @()
        $ReceivedMigrationIds = @()
        foreach ($Migration in $Migrations.content) {
            $MigrationStatus = $null
            switch ($Migration.state) {
                "success" {
                    $MigrationStatus = [MigrationStatus]::new($Migration.id, $Migration.state, "100%")
                }
                "running" {
                    $Progress = [string] (Get-RMMigrationProgress -Migration $Migration)
                    $IsStalled = Test-RMMigrationStalled -Migration $Migration
                    if ($IsStalled) {
                        $MigrationStatus = [MigrationStatus]::new($Migration.id, "stalled", "N/A")
                    } else {
                        $MigrationStatus = [MigrationStatus]::new($Migration.id, $Migration.state, $Progress+"%")
                    }
                }
                default {
                    $MigrationStatus = [MigrationStatus]::new($Migration.id, $Migration.state, "N/A")
                }
            }
            $Result += $MigrationStatus
            $ReceivedMigrationIds += $Migration.id
        }
    
        if ($Migrations.content.Count -ne $MigrationId.Count) {
            $MigrationsNotFound = Get-RMMigrationsNotFound -ReceivedMigrationIds $ReceivedMigrationIds -UserInputMigrationIds $MigrationId
            $MigrationsNotFoundAsString = $MigrationsNotFound -join ", "
            $UserMessage = "Could not find migrations with ID(s): {0}" -f $MigrationsNotFoundAsString
            Write-RMError -Message $UserMessage
            $RMReturn.SetReturnCode([RMReturn]::ERROR)
            $RMReturn.AddRMError([RMError]::new($UserMessage))
        } else {
            $RMReturn.SetReturnCode([RMReturn]::SUCCESS)
        }
    
        $Result | Select-Object MigrationId, Status, Progress | Format-Table | Out-Host
        if ($Result.Count -gt 0) {
            $RMReturn.SetOutputData(@{"Result" = $Result;})
        }

        return $RMReturn
    } catch {
        Show-RMError -ErrorObj $PSItem
        return Build-RMError -ErrorObj $PSItem
    }
}

class MigrationStatus {
    [string] $MigrationId
    [string] $Status
    [string] $Progress

    MigrationStatus( [string] $MigrationId, [string] $Status, [string] $Progress) {
        $this.MigrationId = $MigrationId
        $this.Status = $Status
        $this.Progress = $Progress
    }
}
Export-ModuleMember -Function Start-RMVSphereOSBasedMigration, Start-RMVSphereVMBasedMigration, `
Start-RMAzureOSBasedMigration, Get-RMMigrations, Start-RMOSBasedDifferentialMigration, `
Start-RMVSphereVMBasedDifferentialMigration, Stop-RMMigration