Migration/AZURE/AZURE.psm1

Import-Module -Name $PSScriptRoot\..\..\MigrationProfile\AzureMigrationProfile
Import-Module -Name $PSScriptRoot\..\..\Preflight\Preflight -Force
Import-Module -Name $PSScriptRoot\..\..\Util\Util
Import-Module -Name $PSScriptRoot\..\..\CloudAccount\CloudAccount
Import-Module -Name $PSScriptRoot\..\..\RiverMeadow.Source\SourceUtil\SourceUtil
Import-Module -Name $PSScriptRoot\..\..\Common\Wrappers\Wrappers

function Start-RMAzureOSBasedInteractiveMigration {
    param()
    $UserInput = @{}
    $CloudAccountName = Read-Host "Enter cloud account name"
    if ("" -eq $CloudAccountName) {
        throw "Cloud account name is required to start a migration."
    }

    $CloudAccount = Get-RMCloudAccountByName -CloudAccountName $CloudAccountName

    $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

    $Entitlement = Get-RMEntitlement
    $UserInput.Add("CloudAccount", $CloudAccount)
    $UserInput.Add("CloudAttributes", $CloudAttributes)
    $UserInput.Add("Entitlement", $Entitlement)

    $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
    $UserInput.Add("Source", $Source)

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

    $TransferMethod = "file-based"
    if ("windows" -ieq $Source.os_type) {
        $ReadValue = Read-Host "Enter transfer method (file-based/block-based)[file-based]"
        if ("" -ne $ReadValue) {
            $TransferMethod = $ReadValue
        }
    }
    $UserInput.Add("TransferMethod", $TransferMethod)

    $ResourceGroupRegion = ""
    $ReadValue = Read-Host "Create a new resource group (true/false)[false]"
    if ("" -ne $ReadValue) {
        $ReadValue = [System.Convert]::ToBoolean($ReadValue)
        if ($ReadValue) {
            $ResourceGroup = Read-Host "Enter the name of the resource group to be created"
            if ("" -eq $ResourceGroup) {
                throw "Resource group name is required, please try again."
            }
            $ResourceGroupRegion = Read-Host "Enter the region name where the resource group will be created"
            if ("" -eq $ResourceGroupRegion) {
                throw "Resource group region name is required, please try again."
            }
        } else {
            $ResourceGroup = Get-ExistingResourceGroupNameFromUser -CloudAccount $CloudAccount
        }
    } else {
        $ResourceGroup = Get-ExistingResourceGroupNameFromUser -CloudAccount $CloudAccount
    }

    $UserInput.Add("ResourceGroup", $ResourceGroup)
    $UserInput.Add("ResourceGroupRegion", $ResourceGroupRegion)

    $VMSizeName = Read-Host "Enter VM Size name"
    if ("" -eq $VMSizeName) {
        throw "VM Size is required, please try again"
    }
    $VMSize = Get-VMSizeByName -VMSizeName $VMSizeName -CloudAttributes $CloudAttributes
    if ($null -eq $VMSize) {
        throw "VM size by name '$VMSizeName' does not exists."
    }
    $UserInput.Add("VMSize", $VMSize)

    $DiskTypeMappings = @()
    $ReturnedValue = Get-DiskTypeMappingBySource -Source $Source -VMSize $VMSize -IsInteractive $true
    if ($ReturnedValue -is [hashtable]) {
        # The method "Get-DiskTypeMappingBySource" returns an array of hashtables, but it
        # has been observed that if the source contains a single disk, the returned value
        # is of type HashTable; hence we are checking the type and re-adding it to an array
        $DiskTypeMappings += $ReturnedValue
    } else {
        $DiskTypeMappings = $ReturnedValue
    }

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

    $MountPoints = Get-MountPoint -Source $Source
    if (0 -eq $MountPoints.count) {
        throw "Source has no mount points, cannot be migrated"
    }

    $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
    }
    $UserInput.Add("MountPoints", $SelectedMountPoints)

    $ReadValue = Read-Host "Resize mount points (true/false) [false]"
    
    $MountsResize = @{}
    if ("" -ne $ReadValue) {
        $ReadValue = [System.Convert]::ToBoolean($ReadValue)
        if ($ReadValue) {
            $ResizeMounts = @{}
            $MountPointList = @()
            $SelectedMountPoints | ForEach-Object {
                $MountPointList += $_.values
            }
            $SourceMountPointObjects = Get-RMMountPointObject -Source $Source
            foreach ($Mount in $MountPointList) {
                foreach ($MountPoint in $SourceMountPointObjects) {
                    if ($MountPoint.path -ne $Mount) {
                        continue
                    }
                    $TotalSpace = [math]::round($MountPoint.size_kb/(1024 * 1024), 2)
                    $UsedSpace = [math]::round($MountPoint.used_kb/(1024 * 1024), 2)
                    $ReadValue = Read-Host "Enter new size in GiB for mount point $Mount (used space $UsedSpace of $TotalSpace)[$TotalSpace]"
                    if ("" -ne $ReadValue) {
                        $ResizeMounts.Add($Mount, $ReadValue)
                    }
                    break
                }
            }
            if ($ResizeMounts.Count -gt 0) {
                $MountsResize = Add-RMResizeMount -SelectedMounts $MountPointList -ResizeMounts $ResizeMounts -Source $Source
                if ($null -eq $MountsResize) {
                    return
                }
            }
        }
    }
    $UserInput.Add("MountsResize", $MountsResize)

    $VirtualNetwork = $CloudAccount.appliance.cloud_properties.virtual_network.name
    $ReadValue = Read-Host "Enter virtual network name [$VirtualNetwork]"
    if ("" -ne $ReadValue) {
        $VirtualNetwork = $ReadValue
    }
    $UserInput.Add("VirtualNetwork", $VirtualNetwork)

    $Subnet = $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.network_name
    $ReadValue = Read-Host "Enter subnet name [$Subnet]"
    if ("" -ne $ReadValue) {
        $Subnet = $ReadValue
    }
    $UserInput.Add("Subnet", $Subnet)

    $AssignPublicIP = $false
    $ReadValue = Read-Host "Auto assign public IP (true/false)[false]"
    if ("" -ne $ReadValue) {
        $AssignPublicIP = [System.Convert]::ToBoolean($ReadValue)
    }
    $UserInput.Add("AssignPublicIP", $AssignPublicIP)

    $StaticPrivateIP = Read-Host "Enter static private IP to be assigned to target [None]"
    $UserInput.Add("StaticPrivateIP", $StaticPrivateIP)

    $EnforceTargetNetworkIsolation = $true
    $ReadValue = Read-Host "Enforce target network isolation (true/false)[true]"
    if ("" -ne $ReadValue) {
        $EnforceTargetNetworkIsolation = [System.Convert]::ToBoolean($ReadValue)
    }

    $SecurityGroup = $null
    if (!$EnforceTargetNetworkIsolation) {
        $ReadValue = Read-Host "Enter security group name"
        if ("" -ne $ReadValue) {
            $SecurityGroup = $ReadValue
        }
    }
    $UserInput.Add("SecurityGroup", $SecurityGroup)

    $DisableTargetDNSRegistration = $false
    if("windows" -ieq $Source.os_type) {
        $ReadValue = Read-Host "Disable automatic DNS registration (true/false)[false]"
        if ("" -ne $ReadValue) {
            $DisableTargetDNSRegistration = [System.Convert]::ToBoolean($ReadValue)
        }
    }
    $UserInput.Add("DisableTargetDNSRegistration", $DisableTargetDNSRegistration)

    $ReadValue = Read-Host "Enter one or more instance tags in the format 'key=value' and separated by commas [None]"
    $InstanceTags = Get-RMStringAsHashtable -InputString $ReadValue
    $UserInput.Add("InstanceTags", $InstanceTags)

    $AvailabilitySet = $null
    $AvailabilityZone = $null
    $FaultDomainCount = $null
    $UpdateDomainCount = $null
    $ReadValue = Read-Host "Enter availability option (None, AvailabilityZone, AvailabilitySet)[None]"
    if ("" -ne $ReadValue -and "none" -ine $ReadValue) {
        if (-not ("None" -ieq $ReadValue -or "AvailabilityZone" -ieq $ReadValue -or "AvailabilitySet" -ieq $ReadValue)) {
            throw "Invalid availability option entered, please try again"
        }
        
        if ("AvailabilityZone" -ieq $ReadValue) {
            $AvailabilityZones = $VMSize.availability_zones -join ", "
            $AvailabilityZone = Read-Host "Enter the availability zone ($AvailabilityZones)"
            if ("" -eq "AvailabilityZone") {
                throw "Availability zone is required when 'AvailabilityZone' is the selected availability option"
            }
        } else {
            $ReadValue = Read-Host "Create a new availability set (true/false)[false]"
            if ("" -ne $ReadValue) {
                $ReadValue = [System.Convert]::ToBoolean($ReadValue)
                if ($ReadValue) {
                    $AvailabilitySet = Read-Host "Enter the name of the availability set to be created"
                    if ("" -eq $AvailabilitySet) {
                        throw "Availability set name is required, please try again."
                    }
                    $FaultDomainCount = "2"
                    $ReadValue = Read-Host "Enter fault domains (1-3)[2]"
                    if ("" -ne $ReadValue) {
                        $FaultDomainCount = $ReadValue
                    }
                    $UpdateDomainCount = "5"
                    $ReadValue = Read-Host "Enter update domains (1-20)[5]"
                    if ("" -ne $ReadValue) {
                        $UpdateDomainCount = $ReadValue
                    }
                } else {
                    $AvailabilitySet = Get-ExistingAvailabilitySetNameFromUser -CloudAttributes $CloudAttributes -ResourceGroup $ResourceGroup
                }
            } else {
                $AvailabilitySet = Get-ExistingAvailabilitySetNameFromUser -CloudAttributes $CloudAttributes -ResourceGroup $ResourceGroup
            }
        }
    }

    $UserInput.Add("AvailabilityZone", $AvailabilityZone)
    $UserInput.Add("AvailabilitySet", $AvailabilitySet)
    $UserInput.Add("FaultDomainCount", $FaultDomainCount)
    $UserInput.Add("UpdateDomainCount", $UpdateDomainCount)

    $EnableBootDiagnostics = $false
    $StorageAccount = $null
    $ReadValue = Read-Host "Enable boot diagnostics (true/false)[false]"
    if ("" -ne $ReadValue) {
        $EnableBootDiagnostics = [System.Convert]::ToBoolean($ReadValue)
    }
    if ($EnableBootDiagnostics) {
        $ReadValue = Read-Host "Enter azure storage account name for boot diagnostics"
        if ("" -eq $ReadValue) {
            throw "Storage account name is required for enabling boot diagnostics"
        } else {
            $StorageAccount = $ReadValue
        }
    }
    $UserInput.Add("EnableBootDiagnostics", $EnableBootDiagnostics)
    $UserInput.Add("StorageAccount", $StorageAccount)

    $EncryptionSet = $null
    $ReadValue = Read-Host "Enter disk encryption type (platform-managed/customer-managed)[platform-managed]"
    if ("" -ne $ReadValue) {
        if (-not ("platform-managed" -ieq $ReadValue -or "customer-managed" -ieq $ReadValue)) {
            throw "Invalid disk encryption type entered, please try again"
        }
        if ("customer-managed" -ieq $ReadValue) {
            $ReadValue = Read-Host "Enter encryption set name"
            if ("" -eq $ReadValue) {
                throw "Encryption set name is required for the selected encryption type"
            }
            $EncryptionSet = $ReadValue
        }
    }
    $UserInput.Add("EncryptionSet", $EncryptionSet)

    $EnableHybridUseBenefit = $false
    if("windows" -ieq $Source.os_type) {
        $ReadValue = Read-Host "Enable Azure hybrid use benefit (true/false)[false]"
        if ("" -ne $ReadValue) {
            $EnableHybridUseBenefit = [System.Convert]::ToBoolean($ReadValue)
        }
    }
    $UserInput.Add("EnableHybridUseBenefit", $EnableHybridUseBenefit)

    $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"
        }
        $UserInput.Add("UpgradeOSVersion", $SourceOSMMapping[$ReadValue])
        $UserInput.Add("InPlaceUpgrade", $true)
    } else {
        $UserInput.Add("UpgradeOSVersion", $null)
        $UserInput.Add("InPlaceUpgrade", $false)
    }

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

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

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

    $ReadValue = Read-Host "Enter migration instructions in the format 'key=value' and separated by commas [None]"
    $MigrationInstructions = Get-RMStringAsHashtable -InputString $ReadValue
    $UserInput.Add("MigrationInstructions", $MigrationInstructions)

    $Response = New-RMAzureMigrationProfile @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"
    }

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

function Start-RMAzureOSBasedNonInteractiveMigration {
    param(
        [string] $CloudAccountName,
        [string] $SourceIP,
        [bool] $RefreshSourceAttributes,
        [string] $TargetVMName,
        [string] $TransferMethod,
        [bool] $CreateResourceGroup,
        [string] $ResourceGroup,
        [string] $ResourceGroupRegion,
        [string] $VMSizeName,
        [string] $VolumeType,
        [string[]] $MountPoints,
        [string[]] $ResizeMountPoints,
        [string] $VirtualNetwork,
        [string] $Subnet,
        [bool] $AssignPublicIP,
        [string] $StaticPrivateIP,
        [bool] $EnforceTargetNetworkIsolation,
        [string] $SecurityGroup,
        [bool] $DisableTargetDNSRegistration,
        [string[]] $InstanceTags,
        [string] $AvailabilityOptions,
        [int] $AvailabilityZone,
        [bool] $CreateAvailabilitySet,
        [string] $AvailabilitySet,
        [int] $FaultDomains,
        [int] $UpdateDomains,
        [bool] $EnableBootDiagnostics,
        [string] $StorageAccount,
        [string] $DiskEncryptionType,
        [string] $EncryptionSet,
        [bool] $EnableHybridUseBenefit,
        [string] $UpgradeOSVersion,
        [bool] $ShutdownSource,
        [bool] $ShutdownTarget,
        [bool] $RemoveRMSAgent,
        [string[]] $MigrationInstructions
    )
    $ConfirmationResult = Confirm-RMUserParameter $PSBoundParameters
    if ($ConfirmationResult.Count -gt 0) {
        foreach ($Warning in $ConfirmationResult["warnings"]) {
            Write-Warning -Message $Warning
        }
        Write-RMError -Message "The following required parameters were not provided:"
        if ($ConfirmationResult["errors"].Count -gt 0) {
            $ErrorMessage = $ConfirmationResult["errors"] -join "`n"
            Write-RMError -Message $ErrorMessage
            return
        }
    }

    $Entitlement = Get-RMEntitlement

    $UserInput = @{}
    $CloudAccount = Get-RMCloudAccountByName -CloudAccountName $CloudAccountName
    $CloudAttributes = Get-RMCloudAttribute -CloudAccount $CloudAccount -RefreshCloudAttributes $false

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

    $Source = Get-RMSourceWithAttribute -IPAddress $SourceIP -RefreshSourceAttributes $RefreshSourceAttributes -CloudAccount $CloudAccount
    $UserInput.Add("Source", $Source)
    if ([string]::IsNullOrEmpty($TargetVMName)) {
        $TargetVMName = $Source.hostname
    }
    $UserInput.Add("TargetVMName", $TargetVMName)
    $UserInput.Add("TransferMethod", $TransferMethod)
    $UserInput.Add("ResourceGroup", $ResourceGroup)
    $UserInput.Add("ResourceGroupRegion", $ResourceGroupRegion)

    $VMSize = Get-VMSizeByName -VMSizeName $VMSizeName -CloudAttributes $CloudAttributes
    if ($null -eq $VMSize) {
        throw "VM size by name '$VMSizeName' does not exists."
    }
    $UserInput.Add("VMSize", $VMSize)

    if ("" -eq $VolumeType) {
        $VolumeType = "Standard_SSD"
    }
    $UserInput.Add("VolumeType", $VolumeType)

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

    $MountsResize = @{}
    if ("" -ne $ResizeMountPoints) {
        $ResizeMounts = Get-RMStringArrayAsHashtable -InputItems $ResizeMountPoints
        $MountPointList = @()
        foreach ($SelectedMountPoint in $SelectedMountPoints) {
            $MountPointList += $SelectedMountPoint.values
        }
        $MountsResize = Add-RMResizeMount -SelectedMounts $MountPointList -ResizeMounts $ResizeMounts -Source $Source
        if ($null -eq $MountsResize) {
            # validation errors, hence return
            return
        }
    }
    $UserInput.Add("MountsResize", $MountsResize)

    $UserInput.Add("VirtualNetwork", $VirtualNetwork)
    $UserInput.Add("Subnet", $Subnet)
    $UserInput.Add("AssignPublicIP", $AssignPublicIP)
    $UserInput.Add("StaticPrivateIP", $StaticPrivateIP)

    if (!$EnforceTargetNetworkIsolation) {
        $UserInput.Add("SecurityGroup", $SecurityGroup)
    }
    if("windows" -ieq $Source.os_type) {
        $UserInput.Add("DisableTargetDNSRegistration", $DisableTargetDNSRegistration)
    } else {
        $UserInput.Add("DisableTargetDNSRegistration", $false)
    }

    $InstanceTagsAsHashTable = Get-RMStringArrayAsHashtable -InputItems $InstanceTags
    $UserInput.Add("InstanceTags", $InstanceTagsAsHashTable)
    if ("AvailabilitySet" -ieq $AvailabilityOptions) {
        $UserInput.Add("AvailabilitySet", $AvailabilitySet)
        $UserInput.Add("AvailabilityZone", "")

        if ($CreateAvailabilitySet) {
            $UserInput.Add("FaultDomainCount", $FaultDomains.ToString())
            $UserInput.Add("UpdateDomainCount", $UpdateDomains.ToString())
        }else {
            $UserInput.Add("FaultDomainCount", $null)
            $UserInput.Add("UpdateDomainCount", $null)
        }
    } elseif("AvailabilityZone" -ieq $AvailabilityOptions) {
        $UserInput.Add("AvailabilityZone", $AvailabilityZone.ToString())
        $UserInput.Add("AvailabilitySet", "")
        $UserInput.Add("FaultDomainCount", $null)
        $UserInput.Add("UpdateDomainCount", $null)
    }

    $UserInput.Add("EnableBootDiagnostics", $EnableBootDiagnostics)
    $UserInput.Add("StorageAccount", $StorageAccount)
    $UserInput.Add("EncryptionSet", $EncryptionSet)

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

    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)
    }

    $UserInput.Add("ShutdownSource", $ShutdownSource)
    $UserInput.Add("ShutdownTarget", $ShutdownTarget)
    $UserInput.Add("RemoveRMSAgent", $RemoveRMSAgent)
    $MigrationInstructionsAsHashTable = Get-RMStringArrayAsHashtable -InputItems $MigrationInstructions
    $UserInput.Add("MigrationInstructions", $MigrationInstructionsAsHashTable)

    $Response = New-RMAzureMigrationProfile @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"
    }

    $MigrationResponse = Invoke-RMRestMethod -Params $Params
    Out-MigrationIdFromResponse -Response $MigrationResponse
}
function Get-RMAvailabilitySet {
    param(
        [System.Object] $CloudAttributes,
        [string] $ResourceGroup
    )
    if ($null -eq $CloudAttributes -or 0 -eq $CloudAttributes.properties.subscriptions.Count -or 0 -eq $CloudAttributes.properties.subscriptions[0].regions) {
        throw "Internal error, cloud attributes doesn't exist"
    }

    $AvailabilitySets = @()
    foreach ($AvailabilitySet in $CloudAttributes.properties.subscriptions[0].regions[0].availability_sets) {
        if ($AvailabilitySet.resource_group -eq $ResourceGroup) {
            $AvailabilitySets += $AvailabilitySet.name
        }
    }

    return $AvailabilitySets
}

function Get-ExistingResourceGroupNameFromUser {
    param(
        [System.Object] $CloudAccount
    )
    $ResourceGroup = $CloudAccount.appliance.cloud_properties.resource_group
    $ReadValue = Read-Host "Enter existing resource group name [$ResourceGroup]"
    if ("" -ne $ReadValue) {
        $ResourceGroup = $ReadValue
    }

    return $ResourceGroup
}

function Get-ExistingAvailabilitySetNameFromUser {
    param(
        [System.Object] $CloudAttributes,
        [string] $ResourceGroup
    )

    $AvailabilitySets = Get-RMAvailabilitySet -CloudAttributes $CloudAttributes -ResourceGroup $ResourceGroup
    $AvailabilitySetsAsString = $AvailabilitySets -join ", "
    $AvailabilitySet = Read-Host "Enter the existing availability set name ($AvailabilitySetsAsString)"
    if ("" -eq $AvailabilitySet) {
        throw "Availability set name is required based on the selected availability option."
    }

    return $AvailabilitySet
}

function Confirm-RMUserParameter {
    param(
        [hashtable] $UserParameter
    )
    # 'RequiredParamNotFound' hashtable is of the form:
    # "errors" -> array of error strings
    # "warnings" -> array of warning strings
    $RequiredParamNotFound = @{}
    $Errors = @()
    $Warnings = @()
    if (!$UserParameter.ContainsKey("CloudAccountName") -or [string]::IsNullOrEmpty($UserParameter["CloudAccountName"])) {
        $Errors += "CloudAccountName is required"
    }
    if (!$UserParameter.ContainsKey("SourceIP") -or [string]::IsNullOrEmpty($UserParameter["SourceIP"])) {
        $Errors += "SourceIP is required"
    }
    if (!$UserParameter.ContainsKey("TargetVMName") -or [string]::IsNullOrEmpty($UserParameter["TargetVMName"])) {
        $Warnings += "TargetVMName has not been provided, source hostname will be used as the target VM name"
    }
    if ($UserParameter.ContainsKey($CreateResourceGroup) -and $UserParameter["CreateResourceGroup"] -eq $true) {
        if (!$UserParameter.ContainsKey("ResourceGroupRegion") -or [string]::IsNullOrEmpty($UserParameter["ResourceGroupRegion"])) {
            $Errors += "ResourceGroupRegion is required, when parameter 'CreateResourceGroup' is true."
        }
    }
    if (!$UserParameter.ContainsKey("ResourceGroup") -or [string]::IsNullOrEmpty($UserParameter["ResourceGroup"])) {
        $Errors += "ResourceGroup is required."
    }

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

    if (!$UserParameter.ContainsKey("MountPoints") -or $UserParameter["MountPoints"].Count -eq 0) {
        $Errors += "MountPoints is required."
    }
    if (!$UserParameter.ContainsKey("VirtualNetwork") -or [string]::IsNullOrEmpty($UserParameter["VirtualNetwork"])) {
        $Errors += "VirtualNetwork is required."
    }
    if (!$UserParameter.ContainsKey("Subnet") -or [string]::IsNullOrEmpty($UserParameter["Subnet"])) {
        $Errors += "Subnet is required."
    }
    if($UserParameter.ContainsKey("EnforceTargetNetworkIsolation") -and !$UserParameter["EnforceTargetNetworkIsolation"]) {
        if (!$UserParameter.ContainsKey("SecurityGroup") -or [string]::IsNullOrEmpty($UserParameter["SecurityGroup"])) {
            $Errors += "SecurityGroup is required, when parameter 'EnforceTargetNetworkIsolation' is false."
        }
    }
    if ($UserParameter.ContainsKey("AvailabilityOptions")) {
        if ($UserParameter.AvailabilityOptions -ieq "AvailabilitySet") {
            if (!$UserParameter.ContainsKey("AvailabilitySet") -or [string]::IsNullOrEmpty($UserParameter["AvailabilitySet"])) {
                $Errors += "AvailabilitySet is required."
            }
            if ($UserParameter.ContainsKey("CreateAvailabilitySet") -and $UserParameter["CreateAvailabilitySet"] -eq $true) {
                if (!$UserParameter.ContainsKey("FaultDomains")) {
                    $Errors += "FaultDomains is required, when parameter 'CreateAvailabilitySet' is true."
                } else {
                    $FaultDomains = $UserParameter["FaultDomains"]
                    if (-not($FaultDomains -ge 1 -and $FaultDomains -le 3)) {
                        $Errors += "FaultDomains should be in the range 1 and 3."
                    }
                }

                if (!$UserParameter.ContainsKey("UpdateDomains")) {
                    $Errors += "UpdateDomains is required, when parameter 'CreateAvailabilitySet' is true."
                } else {
                    $UpdateDomains = $UserParameter["UpdateDomains"]
                    if (-not($UpdateDomains -ge 1 -and $UpdateDomains -le 20)) {
                        $Errors += "UpdateDomains should be in the range 1 and 20."
                    }
                }
            }
        } elseif ($UserParameter.AvailabilityOptions -ieq "AvailabilityZone") {
            if (!$UserParameter.ContainsKey("AvailabilityZone")) {
                $Errors += "AvailabilityZone is required."
            } else {
                $AvailabilityZone = $UserParameter["AvailabilityZone"]
                if (-not($AvailabilityZone -ge 1 -and $AvailabilityZone -le 3)) {
                    $Errors += "AvailabilityZone should be in the range 1 and 3."
                }
            }
        }
    }

    if ($UserParameter.ContainsKey("DiskEncryptionType") -and $UserParameter["DiskEncryptionType"] -eq "customer-managed") {
        if (!$UserParameter.ContainsKey("EncryptionSet") -or [string]::IsNullOrEmpty($UserParameter["EncryptionSet"])) {
            $Errors += "EncryptionSet is required when parameter 'DiskEncryptionType' is 'customer-managed'"
        }
    }

    if ($UserParameter.ContainsKey("UpgradeOSVersion") -and ![string]::IsNullOrEmpty($UserParameter["UpgradeOSVersion"])) {
        $SourceOSMMapping = Get-RMOSMMappingBySource -Source $Source
        $OSMLabels = $SourceOSMMapping.keys -join ", "
        $UpgradeOSVersion = $UpgradeOSVersion.Trim('"')
        $UpgradeOSVersion = $UpgradeOSVersion.Trim("'")
        if ($SourceOSMMapping.keys -notcontains $UpgradeOSVersion) {
            $Errors += "UpgradeOSVersion should be one of '$OSMLabels'"
        }
    }

    $RequiredParamNotFound.Add("errors", $Errors)
    $RequiredParamNotFound.Add("warnings", $Warnings)
    return $RequiredParamNotFound
}
Export-ModuleMember -Function Start-RMAzureOSBasedInteractiveMigration, Start-RMAzureOSBasedNonInteractiveMigration