Migration/AWS/AWS.psm1

using module '../../Common/Result'
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath MigrationProfile | Join-Path -ChildPath AWSMigrationProfile)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath CloudAccount | Join-Path -ChildPath CloudAccount)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath MigrationProfile | Join-Path -ChildPath AWSMigrationProfile)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath Preflight | Join-Path -ChildPath Preflight)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath Util | Join-Path -ChildPath Util)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath Common | Join-Path -ChildPath Common)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath CloudAccount | Join-Path -ChildPath CloudAccount)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath RiverMeadow.Development.Source | Join-Path -ChildPath SourceUtil | Join-Path -ChildPath SourceUtil)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath Common | Join-Path -ChildPath Wrappers | Join-Path -ChildPath Wrappers)
Import-Module -Name @(Join-Path $PSScriptRoot AWSUtil)

function Start-RMAWSOSBasedInteractiveMigration {
    param ()

    $CloudAccount = Read-RMCloudAccount -AccountType "aws" -UserMessage "Enter target cloud"
    $CloudAttributes = Get-RMCloudAttribute -CloudAccount $CloudAccount

    $Entitlement = Get-RMEntitlement

    $CloudAttributesSummary = Get-CloudAttributesSummary -CloudAccount $CloudAccount
   
    $Source = Read-RMSource -UserMessage "Enter the IP address of the source machine to be migrated" -ParameterName "Source IP address" -IsRequired $true

    $IgnoreValidationError = Read-RMBoolean -UserMessage "Ignore validation errors" -DefaultValue "false"

    [RMMigrationReturn] $RMMigrationReturn = [RMMigrationReturn]::new()
    $Source, $ShouldExit, $OverrideExistingMigrationWarning, $OverrideExistingMigrationError = Get-RMSourceWithAttribute -Source $Source -CloudAccount $CloudAccount `
        -IgnoreValidationErrors $IgnoreValidationError -RMMigrationReturn $RMMigrationReturn  -AccountType "aws"
    if ($ShouldExit) {
        return $RMMigrationReturn
    }

    if ($OverrideExistingMigrationError) {
        $OverrideExistingMigrationError = Read-RMBoolean -UserMessage "Would you like to override the previous migration attempt" -DefaultValue "false"
        if (!$IgnoreValidationError -and !$OverrideExistingMigrationError) {
            return $RMMigrationReturn
        }
    }

    $ScheduledAt = Read-RMMigrationSchedule

    $TargetVMName = Read-RMString -UserMessage "Enter target VM Name" -DefaultValue $Source.hostname `
    -ParameterName "Target VM Name" -IsRequired $false

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

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

    $EncryptVolume = Read-RMBoolean -UserMessage "Enable EBS encryption on all volumes" -DefaultValue "false"

    if ($EncryptVolume) {
        $ReadValue = Read-RMString -UserMessage "Disk(s) Encryption Type" -Options @("platform-managed-key" , "customer-managed-key") `
            -DefaultValue "platform-managed-key" -ParameterName "Encrypt Type" -IsRequired $false

        if ("customer-managed-key" -ieq $ReadValue) {
            $KMSKeyArray =  $CloudAttributes.properties.regions.kms_keys.alias_name
            $ReadValue = Read-RMString -UserMessage "Enter KMS Key" -DefaultValue "None" -ParameterName "KMS Key" `
                -IsRequired $false  -Options $KMSKeyArray
            $KMSKey = Get-RMARNByKMSKey -KMSKeyObject $CloudAttributes.properties.regions.kms_keys -KMSName $ReadValue
        }
    }

    $SupportVolumeType = Confirm-RMSupportVolumeType -Source $Source
    $VolumeTypeArray = @("GP2", "GP3", "Magnetic")
    if ($SupportVolumeType) {
        $VolumeTypeArray += @("IO1", "IO2")
    }
    $ReadValue = Read-RMString -UserMessage "Enter volume type" -Options $VolumeTypeArray -ParameterName "Volume Type" `
        -IsRequired $false -DefaultValue "GP2"
    
    $VolumeType = Get-RMVolumeType -VolumeType $ReadValue -SupportVolumeType $SupportVolumeType
    if ("io1" -ieq $VolumeType) {
        $IOPS = Read-RMInt -UserMessage "Enter the IOPS value" -OptionsRange @(100,700) -ParameterName "IOPS" -IsRequired $true
    } elseif ("io2" -ieq $VolumeType) {
        $IOPS =Read-RMInt -UserMessage "Enter the IOPS value" -OptionsRange @(100,7000) -ParameterName "IOPS" -IsRequired $true
    }

    $ReadValue = Read-RMBoolean -UserMessage "Resize mount points" -DefaultValue "false"

    $MountsResize = @{}
    $TransferMethod = "file-based"
    if ($ReadValue) {
        $MountsResize = Get-RMInteractiveMountsResize -SelectedMountPoints $SelectedMountPoint -Source $Source
    } else {
        $TransferMethod = (Get-RMTransferMethod -Source $Source -SelectedMountPoints $SelectedMountPoint -IsInteractive $true)[0]
    }

    $RegionArray = $CloudAttributesSummary.properties.regions.name
    $RegionDefaultValue = $CloudAccount.appliance.cloud_properties.region
    $Region = Read-RMRegion -UserMessage "Enter Region" -Options $RegionArray -IsRequired $false -DefaultValue $RegionDefaultValue

    if ($RegionDefaultValue -ne $Region) {
        $CloudAttributesRegion = Get-RMCloudAttributesByRegion -CloudAccount $CloudAccount -RegionName $Region
        $VPCArray = $CloudAttributesRegion.properties.regions.vpcs.id
        
        $VPCID = Read-RMString -UserMessage "Enter VPC ID" -Options  $VPCArray -ParameterName "VPC ID" -IsRequired $true
    
        $VPCArray = $CloudAttributesRegion.properties.regions.vpcs
        $VPCObject = Get-ValidVPCData -VPCObject  $VPCArray  -VPCID $VPCID
    
        $SubnetIDArray = $VPCObject.subnets.id
        $SubnetID = Read-RMString -UserMessage "Enter Subnet ID" -Options $SubnetIDArray -ParameterName "Subnet ID" -IsRequired $true
    } else {
        $CloudAttributesRegion = Get-CachedCloudAttribute -CloudAccount $CloudAccount
        $VPCIDDefaultValue = $CloudAccount.appliance.cloud_properties.vpc
        $VPCArray = $CloudAttributesRegion.properties.regions.vpcs

        $VPCID = Read-RMString -UserMessage "Enter VPC ID" -Options  $VPCArray.id -ParameterName "VPC ID" `
            -DefaultValue  $VPCIDDefaultValue -IsRequired $false 
        $VPCObject = Get-ValidVPCData -VPCObject  $VPCArray  -VPCID $VPCID
        
        $SubnetIDArray = $VPCObject.subnets.id
        if ($VPCIDDefaultValue -ne $VPCID) {
            $SubnetID = Read-RMString -UserMessage "Enter Subnet ID" -Options $SubnetIDArray -ParameterName "Subnet" -IsRequired $true
        } else {
            $SubnetIDDefaultValue = $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.network_name
            $SubnetID = Read-RMString -UserMessage "Enter Subnet ID" -Options $SubnetIDArray -ParameterName "Subnet ID" `
                -IsRequired $false -DefaultValue  $SubnetIDDefaultValue
        }
    }
   
    $Subnet = Get-RMSubnetByVPCIDAndSubnetID -SubnetID $SubnetID -VPC $VPCObject

    $AutoAssignPublicIP = Read-RMBoolean -UserMessage "Auto-assign Public IP" -DefaultValue $Subnet.map_public_ip

    $StaticPrivateIP = Read-RMString -UserMessage "Enter Static Private IP" -ParameterName "Static Private IP" -DefaultValue "None" -IsRequired $false

    $InstanceType = Read-RMInstanceType -CloudAttributes $CloudAttributesRegion  -VMMigration $false

    $EnforeTargetNetworkIsolation = Read-RMBoolean -UserMessage "Enforce Target Network Isolation" -DefaultValue $true

    if (!$EnforeTargetNetworkIsolation) {
        $SecurityGroup = Read-RMString -UserMessage "Enter Security Group"  -Options $VPCObject.security_groups.id `
        -ParameterName "Security Group" -IsRequired $true
    } else {
        $SecurityGroup = Get-RMSecurityGroupArray -VPC $VPCObject 
    }

    $IAMRoleArray = $CloudAttributesSummary.properties.instance_profiles.name
    $IAMRole = Read-RMString -UserMessage "Enter the IAM role name that you want to add to the target VM" -Options $IAMRoleArray -ParameterName "IAM Role" `
        -IsRequired $false -DefaultValue "None"
    if ("" -ne $IAMRole) {
        $ValidIAMRole, $ARN = Confirm-RMIAMRole -CloudAttributesSummary $CloudAttributesSummary -IAMRoleName $IAMRole
    }
   
    $CreateAMI = Read-RMBoolean -UserMessage "Create AMI from the target instance" -DefaultValue "false"

    $ReadValue = Read-RMPair -UserMessage "Enter one or more instance Tags in the format 'key=value' and separated by commas" `
        -Separator "=" -DefaultValue "None"

    if ("" -ne  $ReadValue) {
        $InstanceTagAsHashTable = Get-RMStringAsHashtable -InputString $ReadValue
    }
  
    $ShutdownSource = Read-RMBoolean -UserMessage "Shutdown source after data is fully migrated" -DefaultValue "false"
    $ShutdownTarget = Read-RMBoolean -UserMessage "Shutdown target after data is fully migrated" -DefaultValue "false"

    $OSMUpgradeUserInput = @{}
    Read-RMOSMUpgradeOption -Source $Source -UpdatedUserInput $OSMUpgradeUserInput
    $UpgradeOSVersion = $OSMUpgradeUserInput["UpgradeOSVersion"]

    $DisableTargetDNSRegistration = $false
    if ("windows" -ieq $Source.os_type) {
        $DisableTargetDNSRegistration =  Read-RMBoolean -UserMessage "Disable target DNS registration" -DefaultValue "false"
    }

    $MigrationExtensionResponse = Get-RMMigrationExtension -OrganizationId $CloudAccount.organization_id
    $MigrationExtensionArray = $MigrationExtensionResponse.content.name
    $MigrationExtension = Read-RMString -UserMessage "Enter migration extensions" -Options  $MigrationExtensionArray -ParameterName "Migration Extension" `
            -IsRequired $false -DefaultValue "None"
    
    if ("" -ne  $MigrationExtension) {
        foreach ($Me in $MigrationExtensionResponse.content) {
            if ($MigrationExtension -ieq $Me.name) {
                $MigrationExtensionId = $MigrationExtenScion.id
                break
            }
        }
    }

    $RemoveRMSAgent = Read-RMBoolean -UserMessage "Remove RMS Agent post migration" -DefaultValue "false"

    $ReadValue = Read-RMPair -UserMessage "Enter migration instructions in the format 'key=value' and separated by commas" `
    -Separator "=" -DefaultValue "None"
    if (($IgnoreValidationError -and $OverrideExistingMigrationError) -or (!$IgnoreValidationError -and $OverrideExistingMigrationError) -or $OverrideExistingMigrationWarning) {
        if ("" -eq $ReadValue) {
            $ReadValue = "override_source_migration=true"
        } else {
            $ReadValue += ",override_source_migration=true"
        }
    }
    $MigrationInstructionAsHashTable = Get-RMStringAsHashtable -InputString $ReadValue


    $HashArguments = @{
        CloudAccount = $CloudAccount
        Entitlement = $Entitlement
        CloudAttributes = $CloudAttributes
        ScheduledAt = $ScheduledAt
        Source = $Source
        TargetVMName = $TargetVMName
        SelectedMount = $SelectedMountPoint
        EncryptVolume = $EncryptVolume
        VolumeType = $VolumeType
        ResizeMountPoint = $MountsResize
        TransferMethod = $TransferMethod
        IOPS = $IOPS
        Region = $Region
        VPCID = $VPCID
        SubnetID = $SubnetID
        AutoAssignPublicIP = $AutoAssignPublicIP
        StaticPrivateIP = $StaticPrivateIP
        Tenancy = $Tenancy
        InstanceType = $InstanceType
        SecurityGroup = $SecurityGroup
        IAMRole = $ARN
        ShouldCreateAMI = $CreateAMI
        InstanceTag = $InstanceTagAsHashTable
        UpgradeOSVersion = $UpgradeOSVersion
        DisableTargetDNSRegistration = $DisableTargetDNSRegistration
        MigrationExtension = $MigrationExtension
        MigrationExtensionId =  $MigrationExtensionId
        ShutdownSource = $ShutdownSource
        ShutdownTarget = $ShutdownTarget
        RemoveRMSAgent = $RemoveRMSAgent
        MigrationInstruction = $MigrationInstructionAsHashTable
        IgnoreValidationError = $IgnoreValidationError
        KMSKey = $KMSKey
    }

    $Response = New-RMAWSMigrationProfile @HashArguments
    $ShouldExit = Start-RMMigrationPreflight -MigrationProfileId $Response.id -IgnoreValidationErrors $IgnoreValidationError -RMMigrationReturn $RMMigrationReturn
    if ($ShouldExit) {
        return $RMMigrationReturn
    }
    $IsScheduled = ![string]::IsNullOrEmpty($ScheduledAt)
    $MigrationResponse = Invoke-RMMigrationPost -MigrationProfileResponse $Response
    return Update-RMMigrationReturnAsSuccess -MigrationResponse $MigrationResponse `
        -RMMigrationReturn $RMMigrationReturn -IsScheduledMigration $IsScheduled `
        -ReturnMessage "Migration started successfully, migration ID"
}

function Start-RMAWSOSBasedNonInteractiveMigration {
    param (
        [string] $TargetCloud,
        [string] $SourceIP,
        [string] $ScheduledAt,
        [string] $TargetVMName,
        [System.Object[]] $SelectedMount,
        [bool] $EncryptVolume,
        [string]$EncryptType,
        [string] $VolumeType,
        [string[]] $ResizeMountPoint,
        [string] $TransferMethod,
        [string] $IOPS,
        [string] $Region,
        [string] $VPCID,
        [string] $SubnetID,
        [bool] $AutoAssignPublicIP,
        [string] $StaticPrivateIP,
        [string] $Tenancy,
        [string] $InstanceType,
        [bool] $EnforceTargetNetworkIsolation,
        [string[]] $SecurityGroup,
        [bool] $DisableTargetDNSRegistration,
        [string] $IAMRole,
        [bool] $ShouldCreateAMI,
        [hashtable] $InstanceTag,
        [string] $UpgradeOSVersion,
        [string] $MigrationExtension,
        [bool] $ShutdownSource,
        [bool] $ShutdownTarget,
        [bool] $RemoveRMSAgent,
        [string[]] $MigrationInstruction,
        [bool] $IgnoreValidationError,
        [string] $KMSKey,
        [bool] $OverrideExistingMigration
    )


    $CloudAccount = $null
    $Source = $null
    $IsSourceAndTargetCloudPresent = $true
    [RMMigrationReturn] $RMMigrationReturn = [RMMigrationReturn]::new()

    try {
        if (![string]::IsNullOrEmpty($TargetCloud)) {
            $CloudAccount, $ErrorString = Get-RMCloudAccountByName -CloudAccountName $TargetCloud -AccountType "aws"
            if (![string]::IsNullOrEmpty($ErrorString)) {
                $Errors += $ErrorString
                $IsSourceAndTargetCloudPresent = $false
            } else {    
                #TODO: Move this call after adding validation of user input through FE endpoint, at that time
                # this call should be just before we trigger validation.
                $CloudAttributes = Get-RMCloudAttribute -CloudAccount $CloudAccount
            }
        }
    } catch [System.Management.Automation.ItemNotFoundException] {
        $Errors += $PSItem.Exception.Message
        $IsSourceAndTargetCloudPresent = $false
    }
    

    $FullMigrationErrors, $Warnings, $IsSourceAndTargetCloudPresent, $IsRequiredParametersPresentAndValid = Confirm-RMAWSFullMigrationParameter $PSBoundParameters 
    $Errors += $FullMigrationErrors

    $ShouldExit = $false
    try {
        if (![string]::IsNullOrEmpty($SourceIP)) {
            $Source = Get-RMSourceByIP -IPAddress $SourceIP
        }
    } catch [System.Management.Automation.ItemNotFoundException] {
        $Errors += $PSItem.Exception.Message
        $IsSourceAndTargetCloudPresent = $false
    }

    if (!$IsSourceAndTargetCloudPresent -or  $Errors.Count -gt 0) {
        Add-RMErrorAndWarning -RMReturnObject $RMMigrationReturn -ErrorMessage $Errors -WarningMessage $Warnings
        Out-RMUserParameterResult -ErrorMessage $Errors -WarningMessage $Warnings
        return $RMMigrationReturn
    }

    try {
        $Source, $ShouldExit, $OverrideExistingMigrationWarning, $OverrideExistingMigrationError = `
            Get-RMSourceWithAttribute -Source $Source -CloudAccount $CloudAccount `
            -IgnoreValidationErrors $IgnoreValidationError -RMMigrationReturn $RMMigrationReturn -AccountType "aws"
        if (!$IgnoreValidationError -and $OverrideExistingMigrationError -and !$OverrideExistingMigration) {
            $Errors += "Please set 'OverrideExistingMigration' to true and try again."
        }
    } catch [System.InvalidOperationException], [System.Management.Automation.JobFailedException] {
        $Errors += $PSItem.Exception.Message
    }

    $Errors += Confirm-RMAWSFullMigrationParameterWithSource -UserParameter $PSBoundParameters -Source $Source

    $Entitlement = Get-RMEntitlement

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

    if (![string]::IsNullOrEmpty($EncryptType) -and $EncryptType -ieq "customer-managed-key" -and ![string]::IsNullOrEmpty($KMSKey)) {

        $KMSKeyArray =  $CloudAttributes.properties.regions.kms_keys.alias_name

        if ($KMSKeyArray -contains $KMSKey) {
            $KMSKey = Get-RMARNByKMSKey -KMSKeyObject $CloudAttributes.properties.regions.kms_keys -KMSName $KMSKey
        } else {
            $Errors += "KMS Key '$KMSKey' does not exist."
        }
    }

    $SelectedMountPoint = $null
    $SourceMountPoint = Get-MountPoint -Source $Source
    if ($MountPoints.Count -gt 0) {
        $SelectedMountPoint = Get-RMSelectedMount -MountPoints $SourceMountPoint -DifferenceList $MountPoint -IncludeEqual $true
    } else {
        # If no mount points are given then take all mount points on the source as the selected mount points
        $SelectedMountPoint = $SourceMountPoint
    }
    
    if ([string]::IsNullOrEmpty($VolumeType)) {
        $VolumeType = "ssd2"
    } else  {
        $SupportVolumeType = Confirm-RMSupportVolumeType -Source $Source   
        try {
            $VolumeType = Get-RMVolumeType -VolumeType $VolumeType -SupportVolumeType $SupportVolumeType
            if ("io1" -ieq $VolumeType -and  ($IOPS -lt 100 -or  $IOPS -gt 700)) {
                $Errors += "Unsupported IOPS '$IOPS'"
            } elseif ("io2" -ieq $VolumeType) {
                $Errors += "Unsupported IOPS '$IOPS'"
            }
        }
        catch {
            $Errors += $PSItem.Exception.Message
        }       
    }

    $MountsResize = @{}
    if ($null -ne $ResizeMountPoint -and $ResizeMountPoint.Count -gt 0) {
        $MountsResize, $MountsResizeErrors = Get-RMResizeMountsPoint -ResizeMountPoints $ResizeMountPoint -SelectedMountPoints $SelectedMountPoint -Source $Source
        if ($null -eq $MountsResize) {
            $Errors += $MountsResizeErrors
        }

        if ("" -ne $TransferMethod -and "block-based" -eq $TransferMethod) {
            $Errors += "TransferMethod needs to be 'file-based' if you want to resize the mount points."
        }
        $TransferMethod = "file-based"
    } else {
        $TransferMethod, $ErrorString = Get-RMTransferMethod -Source $Source -SelectedMountPoints $SelectedMountPoint `
            -TransferMethod $TransferMethod
        if (![string]::IsNullOrEmpty($ErrorString)) {
            $Errors += $ErrorString
        }
    }   

  

    if ([string]::IsNullOrEmpty($Region)) {
        $ValidVPC = $true
        $Region = $CloudAccount.appliance.cloud_properties.region
        $RegionArray = Get-CachedCloudAttribute -CloudAccount $CloudAccount
        $VPCArray = $RegionArray.properties.regions.vpcs

        if ([string]::IsNullOrEmpty($VPCID)) {
            $VPCID = $CloudAccount.appliance.cloud_properties.vpc
        } else {
        
            if ($VPCArray.id -notcontains $VPCID) {
                $Errors += "Invalid VPCID"
                $ValidVPC = $false
            } else {
                $VPC = Get-ValidVPCData -VPCObject  $VPCArray  -VPCID $VPCID
            }
        }

        if ($ValidVPC) {
            if ([string]::IsNullOrEmpty($SubnetID)) {
                $SubnetID = $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.network_name
            } else {
                $VPC = Get-ValidVPCData -VPCObject  $VPCArray  -VPCID $VPCID
                $SubnetIDArray = $VPC.subnets.id
                if ($SubnetIDArray -notcontains $SubnetID) {
                    $Errors += "Invalid SubnetID"
                }
            }
        }
    } else {
        $CloudAttributesSummary = Get-CloudAttributesSummary -CloudAccount $CloudAccount
        $ValidRegion =  Confirm-RMRegion -CloudAttributesSummary $CloudAttributesSummary -RegionName $Region
        if (!$ValidRegion) {
           $Errors += "Invalid Region Name"
        } else {
            $RegionArray = Get-RMCloudAttributesByRegion -CloudAccount $CloudAccount -RegionName $Region
            $ValidVPCID, $VPC =  Confirm-RMVPCID -VPCList $RegionArray.properties.regions.vpcs -VPCID $VPCID
            if (!$ValidVPCID) {
                $Errors += "Invalid VPCID"
            } else {
                $ValidSubnet = Confirm-RMSubnet -VPC $VPC -SubnetId $SubnetID
                if (!$ValidSubnet) {
                    $Errors += "Invalid Subnet"
                }
            }
        }
    }

    $ValidInstanceType = Confirm-RMInstanceType -InctanceTypeList  $RegionArray.properties.regions.instance_types `
        -InstanceTypeName $InstanceType
    if (!$ValidInstanceType) {
        $Errors += "Invalid Instance Type"
    }

    if ([string]::IsNullOrEmpty($SecurityGroup) -and $EnforceTargetNetworkIsolation) {
        $SecurityGroup = Get-RMSecurityGroupArray -VPC $VPC
    }

    if (![string]::IsNullOrWhiteSpace($IAMRole)) {
        $ValidIAMRole, $ARN = Confirm-RMIAMRole -CloudAttributesSummary $CloudAttributesSummary -IAMRoleName $IAMRole
        if (!$ValidIAMRole) {
            $Errors += "Invalid IAM Role name."
        }
    }
    
    $MigrationInstructionAsHashTable = $null
    try {
        if (($IgnoreValidationError -and $OverrideExistingMigration) -or `
                (!$IgnoreValidationError -and $OverrideExistingMigration) -or `
                $OverrideExistingMigrationWarning) {
            $MigrationInstruction += "override_source_migration=true"
        }
        $MigrationInstructionAsHashTable = Get-RMStringArrayAsHashtable -InputItems $MigrationInstruction -ParameterName "MigrationInstruction"
    } catch {
        $Errors += $PSItem.Exception.Message
    }
    

    if ([string]::IsNullOrEmpty($ScheduledAt)) {
        $ScheduledAt = ""
    } else {
        $ScheduledAt = Convert-RMDateTimeToUTC -InputDateTime $ScheduledAt
    }

    if (![string]::IsNullOrEmpty($MigrationExtension)) {
       $MigrationExtensionResponse = Get-RMMigrationExtension -OrganizationId $CloudAccount.organization_id
       $ValidMigrationExtension, $MigrationExtensionId = Confirm-MigrationExtencion -MigrationExtencionObject $MigrationExtensionResponse -MigrationExtencionName $MigrationExtension
        if (!$ValidMigrationExtension) {
            $Errors += "Invalid Migration Extension."
        }
    }
  
    Out-RMUserParameterResult -ErrorMessage $Errors -WarningMessage $Warnings
    if ($Errors.Count -gt 0 -or $ShouldExit) {
        Add-RMErrorAndWarning -RMReturnObject $RMMigrationReturn -ErrorMessage $Errors -WarningMessage $Warnings
        return $RMMigrationReturn
    }
   

    $HashArguments = @{
        CloudAccount = $CloudAccount
        Entitlement = $Entitlement
        CloudAttributes = $CloudAttributes
        ScheduledAt = $ScheduledAt
        Source = $Source
        TargetVMName = $TargetVMName
        SelectedMount = $SelectedMountPoint
        EncryptVolume = $EncryptVolume
        VolumeType = $VolumeType
        ResizeMountPoint = $MountsResize
        TransferMethod = $TransferMethod
        IOPS = $IOPS
        Region = $Region
        VPCID = $VPCID
        SubnetID = $SubnetID
        AutoAssignPublicIP = $AutoAssignPublicIP
        StaticPrivateIP = $StaticPrivateIP
        Tenancy = $Tenancy
        InstanceType = $InstanceType
        SecurityGroup = $SecurityGroup
        IAMRole = $ARN
        ShouldCreateAMI = $ShouldCreateAMI
        InstanceTag = $InstanceTag
        UpgradeOSVersion = $UpgradeOSVersion
        DisableTargetDNSRegistration = $DisableTargetDNSRegistration
        MigrationExtension = $MigrationExtension
        MigrationExtensionId =  $MigrationExtensionId
        ShutdownSource = $ShutdownSource
        ShutdownTarget = $ShutdownTarget
        RemoveRMSAgent = $RemoveRMSAgent
        MigrationInstruction = $MigrationInstructionAsHashTable
        IgnoreValidationError = $IgnoreValidationError
        KMSKey = $KMSKey
    }

    $Response = New-RMAWSMigrationProfile @HashArguments
    $ShouldExit = Start-RMMigrationPreflight -MigrationProfileId $Response.id -IgnoreValidationErrors $IgnoreValidationError -RMMigrationReturn $RMMigrationReturn
    if ($ShouldExit) {
        return $RMMigrationReturn
    }
    $IsScheduled = ![string]::IsNullOrEmpty($ScheduledAt)
    $MigrationResponse = Invoke-RMMigrationPost -MigrationProfileResponse $Response
    return Update-RMMigrationReturnAsSuccess -MigrationResponse $MigrationResponse `
        -RMMigrationReturn $RMMigrationReturn -IsScheduledMigration $IsScheduled `
        -ReturnMessage "Migration started successfully, migration ID"
}


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

    $Errors, $Warnings, $IsSourceAndTargetCloudPresent =  Confirm-RMCommonParameter -UserParameter $UserParameter
    $AWSErrors, $IsRequiredParametersPresentAndValid = Confirm-RMAWSParameter -UserParameter $UserParameter
    $Errors += $AWSErrors
    return $Errors, $Warnings, $IsSourceAndTargetCloudPresent, $IsRequiredParametersPresentAndValid
}

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

    $Errors = @()
    $IsRequiredParametersPresentAndValid = $true

    if ($UserParameter.ContainsKey("Region") -or ![string]::IsNullOrWhiteSpace($UserParameter["Region"]) -or 
        $UserParameter["Region"] -ne $CloudAccount.appliance.cloud_properties.region) {
        if (!$UserParameter.ContainsKey("VPCID") -or [string]::IsNullOrWhiteSpace($UserParameter["VPCID"])) {
            $Errors += "VPCID is required."
            $IsRequiredParametersPresentAndValid = $false
        }
    
        if (!$UserParameter.ContainsKey("SubnetID") -or [string]::IsNullOrWhiteSpace($UserParameter["SubnetID"])) {
            $Errors += "SubnetID is required."
            $IsRequiredParametersPresentAndValid = $false
        }
    }

    if (!$UserParameter.ContainsKey("InstanceType") -or [string]::IsNullOrWhiteSpace($UserParameter["InstanceType"])) {
        $Errors += "InstanceType is required."
        $IsRequiredParametersPresentAndValid = $false
    }
    
    if ($UserParameter.ContainsKey("EnforceTargetNetworkIsolation") -and !$UserParameter["EnforceTargetNetworkIsolation"] -and 
    (!$UserParameter.ContainsKey("SecurityGroup") -or [string]::IsNullOrWhiteSpace($UserParameter["SecurityGroup"]))) {
        $Errors += "SecurityGroup is required, when parameter 'EnforceTargetNetworkIsolation' is false."
    }

    if ($UserParameter.ContainsKey("EncryptVolume") -and $UserParameter["EncryptVolume"] -and (
        !$UserParameter.ContainsKey("KMSKey") -or  [string]::IsNullOrWhiteSpace($UserParameter["KMSKey"]))) {
        $Errors += "KMSKey is required, when parameter 'EncryptVolume' is true."
        $IsRequiredParametersPresentAndValid = $false
    }

    return $Errors, $IsRequiredParametersPresentAndValid 
}

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

    $Errors = Confirm-RMCommonParameterWithSource -UserParameter $UserParameter -Source $Source

    if ($Source.os_type -ne 'windows' -and $UserParameter["DisableTargetDNSRegistration"]) {
       $Errors += "DisableTargetDNSRegistration can only be 'true', if the source type is 'windows'."
    }
    return $Errors
}

function Confirm-RMRegion {
    param(
        [System.Object] $CloudAttributesSummary,
        [string] $RegionName
    )

    $ValidRegion = $false
    [array] $RegionArray = $CloudAttributesSummary.properties.regions
    foreach($Region in $RegionArray) {
        if ($RegionName -ieq $Region.name) {
            $ValidRegion = $true
            break
        }
    }

    return $ValidRegion
}

function Confirm-RMVPCID {
    param(
        [System.Object] $VPCList,
        [string] $VPCID
    )

    $ValidVPCID = $false
    foreach($VPC in $VPCList) {
        if ($VPCID -ieq $VPC.id) {
           $ValidVPCID = $true
           return $ValidVPCID , $VPC
        }
    }

    return  $ValidVPCID , $null
}

function Confirm-RMSubnet {
    param(
        [System.Object] $VPC,
        [string] $SubnetId
    )

    $ValidSubnet = $false
    $SubnetArray = $VPC.subnets
    foreach ($Subnet in  $SubnetArray) {
        if ($SubnetId -ieq $Subnet.id) {
            $ValidSubnet = $true
            break
        }
    }

    return $ValidSubnet
}

function Confirm-RMInstanceType {
    param(
        [array] $InctanceTypeList,
        [string] $InstanceTypeName
    )

    $ValidInstanceType = $false
    if ($InctanceTypeList -contains $InstanceTypeName) {
        $ValidInstanceType = $true
    }

    return $ValidInstanceType

}

function Confirm-RMSupportVolumeType {
    param(
        [System.Object] $Source
    )

    $SupportVolumeType = $true
    $SourceMountPointObject = Get-RMMountPointObject -Source $Source

    foreach($MountPoint in $SourceMountPointObject) {
        $TotalSpace = [math]::round($MountPoint.size_kb/(1024 * 1024), 2)
        if ($TotalSpace  -lt 4) {
            $SupportVolumeType = $false
            break
        }
    }
    return  $SupportVolumeType 
}

function Confirm-RMIAMRole {
    param (
        [System.Object] $CloudAttributesSummary,
        [string] $IAMRoleName
    )

    $ValidIAMRole = $false
    $InstaceProfileArray = $CloudAttributesSummary.properties.instance_profiles

    foreach ($Profile in $InstaceProfileArray) {
        if ($IAMRoleName -ieq $Profile.name) {
            $ValidIAMRole = $true
            return $ValidIAMRole, $Profile.arn
        }
    }

    return $ValidIAMRole, $null 
    
}

function Confirm-MigrationExtencion {
    param (
        [System.Object] $MigrationExtencionObject,
        [string] $MigrationExtencionName
    )

    $ValidMigrationExtencion = $false
    $MigrationExtencionArray = $MigrationExtencionObject.content
    foreach ($MigrationExtencion in $MigrationExtencionArray) {
        if ($MigrationExtencionName -ieq $MigrationExtencion.name) {
            $ValidMigrationExtencion = $true
            return $ValidMigrationExtencion, $MigrationExtencion.id
        }
    }
    
    return $ValidMigrationExtencion, $null
}

function Get-RMSecurityGroupArray {
    param(
        [System.Object] $VPC
    )
    
    $GroupArray = @()
    $SecurityGroupArray = $VPC.security_groups

    foreach ($SecurityGroup in $SecurityGroupArray) {
        $Name = $SecurityGroup.name
        if ($Name -clike 'RM-Migration-TargetWorker*') {
            $GroupArray += $Name
        }
    }

    return $GroupArray
}

function Get-ValidVPCData {
    param(
        [array] $VPCObject,
        [string] $VPCID
    )

    foreach ($VPC in $VPCObject) {
        if ($VPCID -ieq $VPC.id) {
            return $VPC
        }
    }
    return $null
}

function Get-RMARNByKMSKey {
    param (
        [System.Object] $KMSKeyObject,
        [string] $KMSName
    )

    foreach($KMSKey in $KMSKeyObject) {
        if ($KMSName -ieq $KMSKey.alias_name) {
            return $KMSKey.arn
        }
    }
    return $null
}

function Get-RMSubnetByVPCIDAndSubnetID {
    param (
        [string] $SubnetID,
        [System.Object] $VPCObject
    )

    $SubnetObject = $VPCObject.subnets
    foreach ($Subnet in $SubnetObject) {
        if ($SubnetID -ieq $Subnet.id) {
            return $Subnet
        }
    }

    return $null
    
}

function Start-RMInteractiveAWSVMBasedMigration {
    param()
    $UserInput = @{}
    $CloudAccount = Read-RMCloudAccount -AccountType "aws" `
        -UserMessage "Enter target cloud that supports VM-based migrations" -VMBasedAppliancesOnly $true
    $Source = Read-RMVMBasedSource -UserMessage "Enter the VM name of the source machine to be migrated" `
        -ParameterName "Source VM name" -IsRequired $true -CloudAccount $CloudAccount

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

    [RMMigrationReturn] $RMMigrationReturn = [RMMigrationReturn]::new()
    if ($null -eq $CloudAccount) {
        return Update-RMMigrationReturnAsError `
            -Message "The migration appliance associated with the given source is not ready for use, cannot start the migration" `
            -RMMigrationReturn $RMMigrationReturn
    }

    $UserInput.Add("CloudAccount", $CloudAccount)
    $CloudAttributes = Get-RMCloudAttribute -CloudAccount $CloudAccount
    $IgnoreValidationErrors = Read-RMBoolean -UserMessage "Ignore validation errors" -DefaultValue "false"
    $UserInput.Add("IgnoreValidationErrors", $IgnoreValidationErrors)

    $SourceAttributeResult = Get-RMSourceWithAttribute -Source $Source -CloudAccount $CloudAccount `
        -IgnoreValidationErrors $IgnoreValidationErrors -RMMigrationReturn $RMMigrationReturn `
        -AccountType "aws"
    $Source, $ShouldExit, $OverrideExistingMigrationWarning, $OverrideExistingMigrationError = $SourceAttributeResult
    $UserInput.Add("Source", $Source)
    if ($ShouldExit) {
        return $RMMigrationReturn
    }
    
    if ($OverrideExistingMigrationError) {
        $OverrideExistingMigrationError = Read-RMBoolean -UserMessage "Would you like to override the previous migration attempt" -DefaultValue $false
        if (!$IgnoreValidationErrors -and !$OverrideExistingMigrationError) {
            return $RMMigrationReturn
        }
    }

    $ScheduledAt = Read-RMMigrationSchedule
    $UserInput.Add("ScheduledAt", $ScheduledAt)
    $TargetVMName = Read-RMString -UserMessage "Enter target VM Name" -DefaultValue $Source.host `
        -ParameterName "Target VM Name" -IsRequired $false
    $UserInput.Add("TargetVMName", $TargetVMName)

    $CloudSummary = Get-CloudAttributesSummary -CloudAccount $CloudAccount
    $RegionName = Read-RMString -UserMessage "Enter the region name where the source VM should be migrated to" `
        -ParameterName "Region name" -Options $CloudSummary.properties.regions.name `
        -DefaultValue $CloudAttributes.properties.regions[0].name -IsRequired $false
    $UserInput.Add("Region", $RegionName)

    $HasRegionChanged = $false
    if ($CloudAttributes.properties.regions[0].name -ine $RegionName) {
        Write-Output "Getting cloud attributes for region $RegionName ..." | Out-Host
        $CloudAttributes = Get-RMCloudAttributesByRegion -CloudAccount $CloudAccount -RegionName $RegionName
        $HasRegionChanged = $true
    }

    $VPC = Get-RMVPCByCloudAttributes -CloudAttributes $CloudAttributes
    $VPCID = ""
    if ($HasRegionChanged) {
        $VPCID = Read-RMString -UserMessage "Enter the VPC ID" -ParameterName "VPC ID" `
        -Options $VPC.Keys -IsRequired $true
    } else {
        $VPCID = Read-RMString -UserMessage "Enter the VPC ID" -ParameterName "VPC ID" `
            -DefaultValue $CloudAccount.appliance.cloud_properties.vpc `
            -Options $VPC.Keys -IsRequired $false
    }
    $UserInput.Add("VPCID", $VPCID)

    if (0 -eq $Source.attributes.storage.vm_disks.Count) {
        return Update-RMMigrationReturnAsError `
            -Message "Source has no disks, cannot be migrated" -RMMigrationReturn $RMMigrationReturn
    }

    $SourceDisksAsString = "Disks to be migrated [" + ((Get-RMVMDiskProperty -Source $Source) -join ", ") + "]"
    Write-Output $SourceDisksAsString | Out-Host

    $SelectedDisks = @()
    $ReturnedValue = Get-RMSelectedDisk -Source $Source
    if ($SelectedDisks -is [hashtable]) {
        $SelectedDisks += $ReturnedValue
    } else {
        $SelectedDisks = $ReturnedValue
    }
    $UserInput.Add("SelectedDisk", $SelectedDisks)

    $EnableEBSEncryption = Read-RMBoolean -UserMessage "Enable EBS encryption on all volumes" -DefaultValue "false"
    $UserInput.Add("EncryptAllVolume", $EnableEBSEncryption)

    #TODO - Add check for source supports IO1 and IO2 (NEED TO FIND WHEN EACH VOL TYPE IS SUPPORTED, INCLUDING GP2 AND 3)
    $VolumeType = Read-RMString -UserMessage "Enter volume type" -ParameterName "Volume type" -Options "GP2", `
        "GP3", "IO1", "IO2", "Magnetic" -DefaultValue "GP2" -IsRequired $false
    $UserInput.Add("VolumeType", (Get-RMVolumeTypeByUserInput -UserInputVolumeType $VolumeType))

    $SubnetIDs = Get-RMSubnetIDBySubnet -Subnet $VPC[$VPCID]
    $SubnetID = ""
    if ($HasRegionChanged) {
        $SubnetID = Read-RMString -UserMessage "Enter the subnet ID" -ParameterName "Subnet ID" `
            -Option $SubnetIDs -IsRequired $true
    } else {
        $SubnetID = Read-RMString -UserMessage "Enter the subnet ID" -ParameterName "Subnet ID" `
            -DefaultValue $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.network_name `
            -Option $SubnetIDs -IsRequired $false
    }
    $UserInput.Add("SubnetID", $SubnetID)

    $Subnet = Get-RMSubnetByVPCIDAndSubnetID -VPCID $VPCID -SubnetID $SubnetID -VPC $VPC
    $AutoAssignPublicIP = Read-RMBoolean -UserMessage "Auto assign public IP address" `
        -DefaultValue ($Subnet.map_public_ip).ToString().ToLower()
    $UserInput.Add("AutoAssignPublicIP", $AutoAssignPublicIP)
    $UserInput.Add("AvailabilityZone", $Subnet.availability_zone)
    
    $UserInput.Add("StaticPrivateIP", (Read-RMString -UserMessage "Enter the static private IP address" `
        -DefaultValue "None" -ParameterName "Static private IP address" -IsRequired $false))

    $UserInput.Add("InstanceType", (Read-RMInstanceType -CloudAttributes $CloudAttributes -VMMigration $true))

    $EnforceTargetNetworkIsolation = Read-RMBoolean -UserMessage "Enforce target network isolation" -DefaultValue "true"
    if (!$EnforceTargetNetworkIsolation) {
        $VPCIDToSecurityGroup = Get-RMVPCAndSecurityGroupMapping -CloudAttributes $CloudAttributes
        $RMRequiredSG = $VPCIDToSecurityGroup[$VPCID].name | Where-Object {$_ -ieq ("RM-Migration-TargetWorker-" + $VPCID)}
        $UserSecurityGroups = @()
        if ($null -eq $RMRequiredSG) {
            $UserSecurityGroups = Read-RMToken -UserMessage "Enter one or more security group names, separated by commas" `
                -ParameterName "Security group names" -Options $VPCIDToSecurityGroup[$VPCID].name -IsRequired $true -Separator ","
        } else {
            $UserSecurityGroups = Read-RMToken -UserMessage "Enter one or more security group names, separated by commas" `
                -ParameterName "Security group names" -Options $VPCIDToSecurityGroup[$VPCID].name -DefaultValue $RMRequiredSG `
                -IsRequired $false -Separator ","
            if ($UserSecurityGroups -notcontains $RMRequiredSG) {
                $UserSecurityGroups += $RMRequiredSG
            }        
        }
        $SecurityGroupMapping = Get-RMSecurityGroupIDToNameMapping -SelectedSecurityGroupName $UserSecurityGroups `
            -SecurityGroup $VPCIDToSecurityGroup[$VPCID]
        $UserInput.Add("SecurityGroup", $SecurityGroupMapping)
    }

    $DisableTargetDNSRegistration = $false
    if("windows" -ieq $Source.os_type) {
        $DisableTargetDNSRegistration = Read-RMBoolean -UserMessage "Disable automatic DNS registration on target" -DefaultValue "false"
    }
    $UserInput.Add("DisableAutomaticDNSRegistrationOnTheTarget", $DisableTargetDNSRegistration)

    if ($CloudAttributes.properties.instance_profiles.Count -gt 0) {
        $IAMNameToARNMapping = @{}
        foreach ($InstanceProfile in $CloudAttributes.properties.instance_profiles) {
            $IAMNameToARNMapping[$InstanceProfile.name] = $InstanceProfile.arn
        }
        $IAMRole = Read-RMString -UserMessage "Enter the IAM role name that you want to add to the target VM" `
            -ParameterName "IAM role name" -Options $IAMNameToARNMapping.Keys -DefaultValue "None" -IsRequired $false
        if (![string]::IsNullOrWhiteSpace($IAMRole)) {
            $UserInput.Add("AddIAMRole", $IAMNameToARNMapping[$IAMRole])
        }    
    }

    $UserInput.Add("CreateAMI", (Read-RMBoolean -UserMessage "Create AMI from the target instance" -DefaultValue "false"))

    $ReadValue = Read-RMPair -UserMessage "Enter one or more instance tags in the format 'key=value' and separated by commas" `
        -Separator "=" -DefaultValue "None"
    $InstanceTags = Get-RMStringAsHashtable -InputString $ReadValue
    $UserInput.Add("InstanceTag", $InstanceTags)

    $UserInput.Add("ShutdownSource", (Read-RMBoolean -UserMessage "Shutdown source after data is fully migrated" -DefaultValue "false"))
    $UserInput.Add("ShutdownTarget", (Read-RMBoolean -UserMessage "Shutdown target after data is fully migrated" -DefaultValue "false"))
    $UserInput.Add("FinalizeMigration", (Read-RMBoolean -UserMessage "Remove snapshot(s) from the Target VM in preparation for a cutover" `
        -DefaultValue "false"))

    $ReadValue = Read-RMPair -UserMessage "Enter migration instructions in the format 'key=value' and separated by commas" `
        -Separator "=" -DefaultValue "None"
    if (($IgnoreValidationErrors -and $OverrideExistingMigrationError) -or (!$IgnoreValidationErrors -and $OverrideExistingMigrationError) -or $OverrideExistingMigrationWarning) {
        if ("" -eq $ReadValue) {
            $ReadValue = "override_source_migration=true"
        } else {
            $ReadValue += ",override_source_migration=true"
        }
    }
    $MigrationInstructions = Get-RMStringAsHashtable -InputString $ReadValue
    $UserInput.Add("MigrationInstruction", $MigrationInstructions)

    $Response = New-RMAWSVMBasedMigrationProfile @UserInput
    $ShouldExit = Start-RMMigrationPreflight -MigrationProfileId $Response.id `
        -IgnoreValidationErrors $IgnoreValidationErrors -RMMigrationReturn $RMMigrationReturn
    if ($ShouldExit) {
        return $RMMigrationReturn
    }

    $IsScheduled = ![string]::IsNullOrEmpty($ScheduledAt)
    $MigrationResponse = Invoke-RMMigrationPost -MigrationProfileResponse $Response
    return Update-RMMigrationReturnAsSuccess -MigrationResponse $MigrationResponse `
        -RMMigrationReturn $RMMigrationReturn -IsScheduledMigration $IsScheduled `
        -ReturnMessage "Migration started successfully, migration ID"    
}

Export-ModuleMember -Function Start-RMAWSOSBasedNonInteractiveMigration, Start-RMAWSOSBasedInteractiveMigration, Start-RMInteractiveAWSVMBasedMigration