DifferentialMigration/DifferentialMigration.psm1

using module '../Common/Result'
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath DifferentialProfile  | Join-Path -ChildPath DifferentialProfile)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath Util | Join-Path -ChildPath MigrationUtil)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath Preflight | Join-Path -ChildPath Preflight)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath Common | Join-Path -ChildPath Common)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath CloudAccount | Join-Path -ChildPath CloudAccount)

function Start-RMDM {
    param (
        [string] $MigrationId,
        [bool] $IgnoreValidationErrors
    )
    [RMMigrationReturn] $RMMigrationReturn = [RMMigrationReturn]::new()
    $Errors, $IsValidMigrationId = Confirm-RMDMParameter -UserParameter $PSBoundParameters
    if (!$IsValidMigrationId) {
        $RMMigrationReturn.SetReturnCode([RMReturn]::ERROR)
        $Errors | ForEach-Object -Process {$RMMigrationReturn.AddRMError([RMError]::new($_))}
        Out-RMUserParameterResult -ErrorMessage $Errors
        return $RMMigrationReturn
    }

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

    $Migration = Get-RMMigrationByIdAndStatus -MigrationId $MigrationId
    $MigrationType = $Migration.migration_type
    $HeartbeatStatus = Test-RMApplianceHeartbeating -ApplianceId $Migration.cloud_appliance_id -AccountType $Migration.target_cloud_type
    if (!$HeartbeatStatus) {
        $Errors += "Migration appliance is not ready, cannot start differential migration"
    }

    if ("full" -ne $MigrationType) {
        $Errors += "Migration type for the given migration ID is '$MigrationType', differential migration can only be started for a full migration."
    } else {
        $SourceId = $Migration.source_id
        $MigrationList = Get-RMMigrationListBySourceId -OrganizationId $OrganizationId -SourceId $SourceId
        foreach ($Mig in $MigrationList) {
            if ("running" -eq $Mig.state) {
                $UserMessage = "Currently there is a migration running for the source of the given migration ID, this differential migration might fail."
                Write-Warning $UserMessage
                $RMMigrationReturn.AddRMWarning([RMWarning]::new($UserMessage))
                Break;
            }
        }
    }


    if ($Errors.Count -gt 0) {
        $RMMigrationReturn.SetReturnCode([RMReturn]::ERROR)
        $Errors | ForEach-Object -Process {$RMMigrationReturn.AddRMError([RMError]::new($_))}
        Out-RMUserParameterResult -ErrorMessage $Errors
        return $RMMigrationReturn
    }

    $TransferType = $Migration.transfer_type
    $TargetProperties = $Migration.target.properties
    $MigrationInstructions =  $Migration.migration_instructions
    $Name = "PowerShell - Differential Profile - " + [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()

    $HashArguments = @{
        Name = $Name
        OrganizationId = $OrganizationId
        Description = $Description
        ApplianceId = $Migration.cloud_appliance_id
        Schedule = $Schedule
        TransferMode = $TransferMode
        MigrationId = $Migration.id
        SourceId = $Migration.source_id
        Target = $Target
        Source =  @{
            "host" = $Migration.source_hostname
        }
        ShutdownSource = $false
        ShutdownTarget = $false
        RemoveTargetAgent = $false
        PublishMigrationHub = $false
        TransferType = $TransferType
        MigrationInstructions = $MigrationInstructions
        TargetProperties = $TargetProperties
        TargetIp = $Migration.target.ip
        IgnoreValidationErrors = $IgnoreValidationErrors
    }

    $Response = New-RMDifferentialProfile @HashArguments
    $ShouldExit = Start-RMDifferentialMigrationPreflight -DifferentialProfileId $Response.id `
        -IgnoreValidationErrors $IgnoreValidationErrors -RMMigrationReturn $RMMigrationReturn
    if ($ShouldExit) {
        return $RMMigrationReturn
    }

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

    $MigrationResponse = Invoke-RMRestMethod -Params $Params
    $DifferentialMigrationId = Get-RMMigrationIdFromResponse -Response $MigrationResponse
    Write-Output "Differential migration started successfully, migration ID: $DifferentialMigrationId" | Out-Host
    $RMMigrationReturn.SetMigrationId($DifferentialMigrationId)
    $RMMigrationReturn.SetReturnCode([RMReturn]::SUCCESS)
    return $RMMigrationReturn
}

function Test-RMApplianceHeartbeating {
    param(
        [string] $ApplianceId,
        [string] $AccountType
    )
    $HeartBeatingCloudAccounts, $NonHeartBeatingCloudAccounts = Get-RMCloudAccountsForCurrentProject -AccountType $AccountType
    foreach ($CloudAccount in $HeartBeatingCloudAccounts.Values) {
        if ($CloudAccount.appliance.id -eq $ApplianceId) {
            return $true
        }
    }

    return $false
}

function Confirm-RMDMParameter {
    param(
        [hashtable] $UserParameter
    )
    $Errors = @()
    $IsValidMigrationId = $true
    if (!$UserParameter.ContainsKey("MigrationId") -or [string]::IsNullOrEmpty($UserParameter["MigrationId"])) {
        $Errors += "MigrationId is required"
        $IsValidMigrationId = $false
    }
    return $Errors, $IsValidMigrationId
}