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

    $UserInput = @{}

    $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

    #Placement Settings
    $RegionArray = $CloudAttributesSummary.properties.regions.name
    $RegionDefaultValue = $CloudAccount.appliance.cloud_properties.region
    $Region = Read-RMRegion -UserMessage "Enter Region name, e.g. us-east-1" -Options $RegionArray -IsRequired $false -DefaultValue $RegionDefaultValue

    if ($RegionDefaultValue -ne $Region) {
        $CloudAttributesRegion = Get-RMCloudAttributesByRegion -CloudAccount $CloudAccount -RegionName $Region
    } else {
        $CloudAttributesRegion = Get-CachedCloudAttribute -CloudAccount $CloudAccount
    }

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

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

    $InstanceTagAsHashTable = Get-RMStringAsHashtable -InputString $ReadValue

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

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

    if ($RegionDefaultValue -ne $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 {
        $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-RMSubnetBySubnetIDAndVPC -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

    #Optimization Settings
    $MountsResize = @{}
   
    $ResizeMountPointsFlag = Read-RMBoolean -UserMessage "Do you want to resize mount points" -DefaultValue "false"
    if ($ResizeMountPointsFlag) {
        $MountsResize = Get-RMInteractiveMountsResize -SelectedMountPoints $SelectedMountPoint -Source $Source
    }

    $MigrationExtensionResponse = Get-RMMigrationExtension -OrganizationId $CloudAccount.organization_id
    $MigrationExtensionArray, $MigrationExtensionOSMArray = Get-RMMigrationExtensionFromStep -MigrationExtensionArray $MigrationExtensionResponse.content

    Read-RMMigrationExtension -CloudAccount $CloudAccount -MigrationExtension  $MigrationExtensionArray `
        -MigrationExtensionObject $MigrationExtensionResponse.content -UpdatedUserInput $UserInput


    #Modernization Settings
    Read-RMOSUpgradeOption -Source $Source -UpdatedUserInput $UserInput
    if ($null -ne $UserInput['UpgradeOSVersion']) {
        Read-RMMigrationExtensionOSM -MigrationExtensionOSM $MigrationExtensionOSMArray -MigrationExtensionObject $MigrationExtensionResponse.content `
            -UpdatedUserInput $UserInput
    }

    $SQLServerUpgrade =  Read-RMSQLServerUpgradeOption -Source $Source


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

    $KMSKey = ""
    if ($EncryptVolume) {
        $ReadValue = Read-RMString -UserMessage "Enter disk encryption type" -Options "platform-managed-key" , "customer-managed-key" `
            -DefaultValue "platform-managed-key" -ParameterName "Encryption 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
        }
    }

    $EnforceTargetNetworkIsolation = Read-RMBoolean -UserMessage "Enforce Target Network Isolation" -DefaultValue $true
   
    $VPCIDToSecurityGroup = Get-RMVPCAndSecurityGroupMapping -CloudAttributes $CloudAttributes
    [array] $RMRequiredSG = $VPCIDToSecurityGroup[$VPCID].name | Where-Object {$_ -clike ("RM-Migration-TargetWorker-*")}
    if (!$EnforceTargetNetworkIsolation) {
        $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 {
            $SecurityGroupDefaultValue = $RMRequiredSG -join ", "
            $ReadValue = Read-RMToken -UserMessage "Enter one or more security group names, separated by commas" `
                -ParameterName "Security group names" -Options $VPCIDToSecurityGroup[$VPCID].name -DefaultValue $SecurityGroupDefaultValue `
                -IsRequired $false -Separator ","

            if ($ReadValue -is [string]) {
                $UserSecurityGroups += @($ReadValue.split(",").Trim())
            } else {
                $UserSecurityGroups += $ReadValue
            }

            foreach ($SG in $RMRequiredSG) {
                if ($UserSecurityGroups -notcontains $SG) {
                    $UserSecurityGroups += $SG
                }  
            }  
        }
    } else {
        $UserSecurityGroups = $RMRequiredSG
    }

    $SecurityGroupMapping = Get-RMSecurityGroupIDToNameMapping -SelectedSecurityGroupName $UserSecurityGroups `
        -SecurityGroup $VPCIDToSecurityGroup[$VPCID]
   
    $IAMNameToARNMapping = Get-RMIAMRoleToARNMapping -CloudAttributes $CloudAttributes
    $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)) {
        $RoleARN = $IAMNameToARNMapping[$IAMRole]
    }    


    #Advanced Settings
    $TransferMethod = "file-based"
    if (!$ResizeMountPointsFlag) {
        $TransferMethod = (Get-RMTransferMethod -Source $Source -SelectedMountPoints $SelectedMountPoint -IsInteractive $true)[0]
    }

    $CreateAMI = Read-RMBoolean -UserMessage "Create AMI from the target instance" -DefaultValue "false"

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

    $DisableTargetDNSRegistration = $false
    if ("windows" -ieq $Source.os_type) {
        $DisableTargetDNSRegistration =  Read-RMBoolean -UserMessage "Disable target DNS registration" -DefaultValue "false"
    }
  
    $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"

    $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

    $EnableNetAppFileMigration = Read-RMBoolean -UserMessage "Enable NetApp file migration" -DefaultValue "false"

    $MTUSizeResponse = Invoke-RMProjectSettings -OrganizationId $CloudAccount.organization_id
    $MTUSize = $MTUSizeResponse.azure.mtu_size

    $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
        EnforceTargetNetworkIsolation = $EnforceTargetNetworkIsolation
        SecurityGroup = $SecurityGroupMapping
        IAMRole = $RoleARN
        ShouldCreateAMI = $CreateAMI
        InstanceTag = $InstanceTagAsHashTable
        SQLServerUpgrade = $SQLServerUpgrade
        DisableTargetDNSRegistration = $DisableTargetDNSRegistration
        ShutdownSource = $ShutdownSource
        ShutdownTarget = $ShutdownTarget
        RemoveRMSAgent = $RemoveRMSAgent
        MigrationInstruction = $MigrationInstructionAsHashTable
        IgnoreValidationError = $IgnoreValidationError
        KMSKey = $KMSKey
        EnableNetAppFileMigration = $EnableNetAppFileMigration
        MTUSize = $MTUSize
    }

    $HashArguments += $UserInput

    $Response = New-RMAWSMigrationProfile @HashArguments
    $ShouldExit = Start-RMMigrationPreflight -MigrationProfileId $Response.id -IgnoreValidationErrors $IgnoreValidationError -RMMigrationReturn $RMMigrationReturn
    if ($ShouldExit) {
        return $RMMigrationReturn
    }
    $IsScheduled = ![string]::IsNullOrWhiteSpace($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[]] $MountPoint,
        [bool] $EncryptAllVolume,
        [string] $DiskEncryptionType,
        [string] $KMSKeyAliasName,
        [string] $VolumeType,
        [string[]] $ResizeMountPoint,
        [string] $TransferMethod,
        [string] $IOPS,
        [string] $Region,
        [string] $VPCID,
        [string] $SubnetID,
        [string] $AssignPublicIP,
        [string] $StaticPrivateIP,
        [string] $Tenancy,
        [string] $InstanceType,
        [bool] $EnforceTargetNetworkIsolation,
        [string[]] $SecurityGroup,
        [bool] $DisableTargetDNSRegistration,
        [string] $IAMRole,
        [bool] $ShouldCreateAMI,
        [string[]] $InstanceTag,
        [string] $UpgradeOSVersion,
        [string] $UpgradeOSConversion,
        [string] $MigrationExtension,
        [string] $MigrationExtensionOSM,
        [string] $ConvertFileSystem,
        [hashtable[]] $UpgradeSQLServer,
        [bool] $ShutdownSource,
        [bool] $ShutdownTarget,
        [bool] $RemoveRMSAgent,
        [string[]] $MigrationInstruction,
        [bool] $IgnoreValidationError,
        [bool] $OverrideExistingMigration,
        [bool] $EnableNetAppFileMigration
    )


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


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

    try {
        if (![string]::IsNullOrWhiteSpace($TargetCloud)) {
            $CloudAccount, $ErrorString = Get-RMCloudAccountByName -CloudAccountName $TargetCloud -AccountType "aws"
            if (![string]::IsNullOrWhiteSpace($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
    }

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

    if (!$IsSourceAndTargetCloudPresent) {
        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.RuntimeException] {
        $Errors += $PSItem.Exception.Message
        $IsRequiredParametersPresentAndValid = $false
    }

    $MigrationExtensionResponse = Get-RMMigrationExtension -OrganizationId $CloudAccount.organization_id
    $MigrationExtensionArray, $MigrationExtensionOSMArray = Get-RMMigrationExtensionFromStep -MigrationExtensionArray $MigrationExtensionResponse.content

    $SourceSQLServerMapping = Get-RMSQLMMappingBySource -Source $Source
    $Errors += Confirm-RMAWSFullMigrationParameterWithSource -UserParameter $PSBoundParameters -Source $Source `
        -SourceSQLServerMapping $SourceSQLServerMapping -MigrationExtension $MigrationExtensionArray `
        -MigrationExtensionOSM $MigrationExtensionOSMArray

    $CloudAttributesSummary = Get-CloudAttributesSummary -CloudAccount $CloudAccount

    $CloudAttributesErrors = Confirm-RMCloudAttributes -UserParameter $PSBoundParameters -CloudAccount $CloudAccount -Source $Source -CloudAttributes $CloudAttributes `
        -CloudAttributesSummary $CloudAttributesSummary
    $Errors += $CloudAttributesErrors

    $Entitlement = Get-RMEntitlement

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

    if (![string]::IsNullOrWhiteSpace($KMSKeyAliasName)) {
        $KMSKeyAliasName = Get-RMARNByKMSKey -KMSKeyObject $CloudAttributes.properties.regions.kms_keys -KMSName $KMSKeyAliasName
    }

    $SelectedMountPoint = $null
    $SourceMountPoint = Get-MountPoint -Source $Source
    if ($MountPoint.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]::IsNullOrWhiteSpace($VolumeType)) {
        $VolumeType = "ssd2"
    } else  {
        try {
            $ValidVolumeType = Confirm-RMSupportVolumeType -Source $Source
            $VolumeType = Get-RMVolumeType -VolumeType $VolumeType -SupportVolumeType $ValidVolumeType
        }
        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]::IsNullOrWhiteSpace($ErrorString)) {
            $Errors += $ErrorString
        }
    }   

    if ([string]::IsNullOrWhiteSpace($Region) -or $Region -ieq $CloudAccount.appliance.cloud_properties.region) {
        $Region = $CloudAccount.appliance.cloud_properties.region
        if ([string]::IsNullOrWhiteSpace($VPCID)) {
            $VPCID = $CloudAccount.appliance.cloud_properties.vpc
        }

        if ([string]::IsNullOrWhiteSpace($SubnetID)) {
            $SubnetID = $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.network_name
        }
    }


    if ("windows" -ne $Source.os_type) {
        $DisableTargetDNSRegistration = $false
    }

    $VPCIDToSecurityGroup = Get-RMVPCAndSecurityGroupMapping -CloudAttributes $CloudAttributes
    [array] $DefaultSecurityGroup = $VPCIDToSecurityGroup[$VPCID].name | Where-Object {$_ -clike ("RM-Migration-TargetWorker-*")}
    if ($EnforceTargetNetworkIsolation) {
        $SecurityGroup = $DefaultSecurityGroup
    } else {
        foreach ($SG in $DefaultSecurityGroup) {
            if ($SecurityGroup -notcontains $SG) {
                $SecurityGroup +=  $SG
            }
        }
    }

    $SecurityGroupMapping = Get-RMSecurityGroupIDToNameMapping -SelectedSecurityGroupName $SecurityGroup `
        -SecurityGroup $VPCIDToSecurityGroup[$VPCID]

    if (![string]::IsNullOrWhiteSpace($IAMRole)) {
        $RoleARN = Get-RMARN -CloudAttributesSummary $CloudAttributesSummary -IAMRoleName $IAMRole  
    }

    if ([string]::IsNullOrWhiteSpace($AssignPublicIP)) {
        $AssignPublicIP = "Use-subnet-setting"
    }
    $AutoAssignPublicIPBool = Get-RMAutoAssignPublicIP -AutoAssignPublicIPName $AssignPublicIP `
        -SubnetObject $CloudAttributes.properties.regions[0].vpcs.subnets -SubnetId $SubnetID
    

    $UserInput = @{}
    if (![string]::IsNullOrWhiteSpace($UpgradeOSVersion)) {
        Add-RMOSUpgrade -UserParameter $PSBoundParameters -UpdatedParameter $UserInput -Source $Source
    } else {
        Add-RMOSConversionUpgrade -UserParameter $PSBoundParameters -UpdatedParameter $UserInput -Source $Source
    }

    Add-RMMigrationExtension -UpdatedParameter $UserInput -MigrationExtension $MigrationExtension `
        -MigrationExtensionOSM $MigrationExtensionOSM -MigrationExtensionResponse $MigrationExtensionResponse
    
    $SQLServerUpgrade = Get-RMUpgradeSQLServer -UpgradeSQLServer $UpgradeSQLServer -Source $Source `
        -SourceSQLServerMapping $SourceSQLServerMapping    

    $InstanceTagAsHashTable = $null
    try {
        $InstanceTagAsHashTable = Get-RMStringArrayAsHashtable -InputItems $InstanceTag -ParameterName "InstanceTag"
    }
    catch {
        $Errors += $PSItem.Exception.Message
    }
    
    $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]::IsNullOrWhiteSpace($ScheduledAt)) {
        $ScheduledAt = ""
    } else {
        $ScheduledAt = Convert-RMDateTimeToUTC -InputDateTime $ScheduledAt
    }

    $MTUSizeResponse = Invoke-RMProjectSettings -OrganizationId $CloudAccount.organization_id
    $MTUSize = $MTUSizeResponse.azure.mtu_size
    $UserInput.Add("MTUSize", $MTUSize)
    Add-RMConvertFileSystem -Source $Source -UpdatedUserInput $UserInput -ConvertFileSystem $ConvertFileSystem
  
    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 = $EncryptAllVolume
        VolumeType = $VolumeType
        ResizeMountPoint = $MountsResize
        TransferMethod = $TransferMethod
        IOPS = $IOPS
        Region = $Region
        VPCID = $VPCID
        SubnetID = $SubnetID
        AutoAssignPublicIP = $AutoAssignPublicIPBool
        StaticPrivateIP = $StaticPrivateIP
        Tenancy = $Tenancy
        InstanceType = $InstanceType
        EnforceTargetNetworkIsolation = $EnforceTargetNetworkIsolation
        SecurityGroup = $SecurityGroupMapping
        IAMRole = $RoleARN
        ShouldCreateAMI = $ShouldCreateAMI
        InstanceTag = $InstanceTagAsHashTable
        SQLServerUpgrade = $SQLServerUpgrade
        DisableTargetDNSRegistration = $DisableTargetDNSRegistration
        ShutdownSource = $ShutdownSource
        ShutdownTarget = $ShutdownTarget
        RemoveRMSAgent = $RemoveRMSAgent
        MigrationInstruction = $MigrationInstructionAsHashTable
        IgnoreValidationError = $IgnoreValidationError
        KMSKey = $KMSKeyAliasName
        EnableNetAppFileMigration = $EnableNetAppFileMigration
    }

    $HashArguments += $UserInput
    
    $Response = New-RMAWSMigrationProfile @HashArguments
    $ShouldExit = Start-RMMigrationPreflight -MigrationProfileId $Response.id -IgnoreValidationErrors $IgnoreValidationError -RMMigrationReturn $RMMigrationReturn
    if ($ShouldExit) {
        return $RMMigrationReturn
    }
    $IsScheduled = ![string]::IsNullOrWhiteSpace($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
    )

    $Errors = @()
    $IsRequiredParametersPresentAndValid = $true

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

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

    return $Errors, $IsRequiredParametersPresentAndValid 
}

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

    $Errors = @()
    $IsRequiredParametersPresentAndValid = $true

    if ($UserParameter.ContainsKey("Region") -and ![string]::IsNullOrWhiteSpace($UserParameter["Region"]) -and
        $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
        }
    }
    return $Errors, $IsRequiredParametersPresentAndValid 
}

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

    $Errors = Confirm-RMCommonParameterWithSource -UserParameter $UserParameter -Source $Source `
        -SourceSQLServerMapping $SourceSQLServerMapping -MigrationExtension $MigrationExtension `
        -MigrationExtensionOSM $MigrationExtensionOSM

    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-RMAWSVMBasedFullMigrationParameter {
    param (
        [hashtable] $UserParameter
    )

    $Errors, $Warnings, $IsSourcePresent = Confirm-RMVMBasedCommonParameter -UserParameter $UserParameter
    $AWSErrors, $IsRequiredParametersPresentAndValid = Confirm-RMAWSParameter -UserParameter $UserParameter
    $Errors += $AWSErrors
    return $Errors, $Warnings, $IsSourcePresent, $IsRequiredParametersPresentAndValid    
}

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

    $Errors = @()
   
    if ($UserParameter.ContainsKey("MigrationExtension") -and ![string]::IsNullOrWhiteSpace($UserParameter["MigrationExtension"])) {
        $MigrationExtensionValue = $UserParameter["MigrationExtension"]
        if ($MigrationExtension -notcontains $MigrationExtensionValue) {
            $Errors += "MigrationExtension '$MigrationExtensionValue' does not exist."
        }
    }

    $SourceDiskLabels = Get-RMVMBasedSourceDiskLabel -Source $Source
    $DiskLabelsNotFound = @()
    foreach ($DiskLabel in $UserParameter["SelectedDiskLabel"]) {
        if ($SourceDiskLabels -notcontains $DiskLabel) {
            $DiskLabelsNotFound += $DiskLabel
        }
    }

    if ($DiskLabelsNotFound.Count -gt 0) {
        $DiskLabelsNotFoundAsString = $DiskLabelsNotFound -join ", "
        $Errors += "The disk label(s) '$DiskLabelsNotFoundAsString' specified in the parameter 'SelectedDiskLabel' does not exist on the source"
    }
    
    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
            return $ValidSubnet, $Subnet
        }
    }

    return $ValidSubnet, $null
}
 function Confirm-RMSecurityGroup {
    param (
        [string[]] $UserSecurityGroupName,
        [string[]] $SecurityGroupName
    )

    $ValidSecurityGroup = $true
    foreach($SecurityGroup in $UserSecurityGroupName) {
        if ($SecurityGroupName -notcontains $SecurityGroup) {
            $ValidSecurityGroup = $false
            break
        }
    }
    return $ValidSecurityGroup
 }

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

    $ValidInstanceType = $false
    if ($InstanceTypeName -contains $UserInstanceTypeName) {
        $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 Get-RMARN {
    param (
        [System.Object] $CloudAttributesSummary,
        [string] $IAMRoleName
    )
    $InstaceProfileArray = $CloudAttributesSummary.properties.instance_profiles

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

    return $null
    
}

function Confirm-RMMigrationExtension {
    param (
        [System.Object] $MigrationExtensionObject,
        [string] $MigrationExtensionName
    )

    $ValidMigrationExtension = $false
    $MigrationExtensionArray = $MigrationExtensionObject.content
    foreach ($MigrationExtension in $MigrationExtensionArray) {
        if ($MigrationExtensionName -ieq $MigrationExtension.name) {
            $ValidMigrationExtension = $true
            return $ValidMigrationExtension, $MigrationExtension.id
        }
    }
    
    return $ValidMigrationExtension, $null
}

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

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

    #Placement Settings
    $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
    }

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

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

    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)

     #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 "GP3" -IsRequired $false
    $UserInput.Add("VolumeType", (Get-RMVolumeTypeByUserInput -UserInputVolumeType $VolumeType))

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

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


    #Optimization Settings
    $MigrationExtensionResponse = Get-RMMigrationExtension -OrganizationId $CloudAccount.organization_id
    $MigrationExtensionArray, $MigrationExtensionOSMArray = Get-RMMigrationExtensionFromStep -MigrationExtensionArray $MigrationExtensionResponse.content
    
    Read-RMMigrationExtension -CloudAccount $CloudAccount -MigrationExtension  $MigrationExtensionArray `
        -MigrationExtensionObject $MigrationExtensionResponse.content -UpdatedUserInput $UserInput
    
    #Security Settings
    $EnableEBSEncryption = Read-RMBoolean -UserMessage "Enable EBS encryption on all volumes" -DefaultValue "false"
    $UserInput.Add("EncryptAllVolume", $EnableEBSEncryption)

    $KMSKey = ""
    if ($EnableEBSEncryption) {
        $ReadValue = Read-RMString -UserMessage "Enter disk encryption type" -Options "platform-managed-key" , "customer-managed-key" `
            -DefaultValue "platform-managed-key" -ParameterName "Encryption 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
        }
    }
    $UserInput.Add("KMSKey", $KMSKey)

    $EnforceTargetNetworkIsolation = Read-RMBoolean -UserMessage "Enforce target network isolation" -DefaultValue "true"
    $UserInput.Add("EnforceTargetNetworkIsolation", $EnforceTargetNetworkIsolation)
    $VPCIDToSecurityGroup = Get-RMVPCAndSecurityGroupMapping -CloudAttributes $CloudAttributes
    [array] $RMRequiredSG = $VPCIDToSecurityGroup[$VPCID].name | Where-Object {$_ -clike ("RM-Migration-TargetWorker-*")}
    if (!$EnforceTargetNetworkIsolation) {
        $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 {
            $DefaultSecurityGroup = $RMRequiredSG -join ", "
            $ReadValue = Read-RMToken -UserMessage "Enter one or more security group names, separated by commas" `
                -ParameterName "Security group names" -Options $VPCIDToSecurityGroup[$VPCID].name -DefaultValue $DefaultSecurityGroup `
                -IsRequired $false -Separator ","

            if ($ReadValue -is [string]) {
                $UserSecurityGroups += @($ReadValue.split(",").Trim())
            } else {
                $UserSecurityGroups += $ReadValue
            }
            
            foreach ($SG in $RMRequiredSG) {
                if ($UserSecurityGroups -notcontains $SG) {
                    $UserSecurityGroups += $SG
                }  
            }          
        }
    } else {
        $UserSecurityGroups += $RMRequiredSG
    }

    $SecurityGroupMapping = Get-RMSecurityGroupIDToNameMapping -SelectedSecurityGroupName $UserSecurityGroups `
        -SecurityGroup $VPCIDToSecurityGroup[$VPCID]
    $UserInput.Add("SecurityGroup", $SecurityGroupMapping)

    if ($CloudAttributes.properties.instance_profiles.Count -gt 0) {
        $IAMNameToARNMapping = Get-RMIAMRoleToARNMapping -CloudAttributes $CloudAttributes
        $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])
        }    
    }

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

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

    $MTUSizeResponse = Invoke-RMProjectSettings -OrganizationId $CloudAccount.organization_id
    $MTUSize = $MTUSizeResponse.aws.mtu_size

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

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

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

function Start-RMNonInteractiveAWSVMBasedMigration {
    param (
        [string] $SourceVMName,
        [string] $SourceVMFolderPath,
        [string] $TargetCloud,
        [string] $ScheduledAt,
        [string] $TargetVMName,
        [string[]] $SelectedDiskLabel,
        [bool] $EncryptAllVolume,
        [string] $DiskEncryptionType,
        [string] $KMSKeyAliasName,
        [string] $VolumeType,
        [string] $IOPS,
        [string] $Region,
        [string] $VPCID,
        [string] $SubnetID,
        [string] $AssignPublicIP,
        [string] $StaticPrivateIP,
        [string] $InstanceType,
        [bool] $EnforceTargetNetworkIsolation,
        [string[]] $SecurityGroup,
        [string] $IAMRole,
        [bool] $CreateAMI,
        [string] $MigrationExtension,
        [string[]] $InstanceTag,
        [bool] $ShutdownSource,
        [bool] $ShutdownTarget,
        [bool] $FinalizeMigration,
        [string[]] $MigrationInstruction,
        [bool] $IgnoreValidationError,
        [bool] $OverrideExistingMigration
    )
   
    $CloudAccount = $null
    $Source = $null
    $Errors = @()
    $UserInput = @{}
    $ShouldExit = $false
    [RMMigrationReturn] $RMMigrationReturn = [RMMigrationReturn]::new()
    
    $Errors, $Warnings, $IsSourceAndTargetCloudPresent, $IsRequiredParametersPresentAndValid = Confirm-RMAWSVMBasedFullMigrationParameter $PSBoundParameters

    try {
        if (![string]::IsNullOrWhiteSpace($TargetCloud)) {
            $CloudAccount, $ErrorString = Get-RMCloudAccountByName -CloudAccountName $TargetCloud -AccountType "aws"  -VMBasedAppliancesOnly $true
            if (![string]::IsNullOrWhiteSpace($ErrorString)) {
                $Errors += $ErrorString
                $IsSourceAndTargetCloudPresent = $false
            } else {    
                $CloudAttributes = Get-RMCloudAttribute -CloudAccount $CloudAccount
            }
        }
    } catch [System.Management.Automation.ItemNotFoundException] {
        $Errors += $PSItem.Exception.Message
        $IsSourceAndTargetCloudPresent = $false
    }

    try {
        if (![string]::IsNullOrWhiteSpace($SourceVMName) -and $null -ne $CloudAccount) {
            $Source = Get-RMSourceByVMName -VMName $SourceVMName -SourceVMFolderPath $SourceVMFolderPath `
                        -CloudAccount  $CloudAccount
            if ($Source.collection_type -ine "VM") {
                $Errors += "Source '$SourceVMName' is not a VM-Based source, to migrate this source, please use the cmdlet 'Start-RMAWSOSBasedMigration'."
                Add-RMErrorAndWarning -RMReturnObject $RMMigrationReturn -ErrorMessage $Errors -WarningMessage $Warnings
                Out-RMUserParameterResult -ErrorMessage $Errors -WarningMessage $Warnings
                return $RMMigrationReturn        
            }
        }
    } catch [System.Management.Automation.ItemNotFoundException], [System.Data.DuplicateNameException] {
        $Errors += $PSItem.Exception.Message
        $IsSourceAndTargetCloudPresent = $false
    }   

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

    $Entitlement = Get-RMEntitlement
    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.RuntimeException] {
        $Errors += $PSItem.Exception.Message
    }

    if ($null -ne $Source) {
        if ($null -eq $SelectedDiskLabel -or $SelectedDiskLabel.Count -eq 0) {
            # When the user has not selected any disk labels to migrate,
            # select all for the source.
            $Warnings += "No disk labels have been given, all the disks on the source will be migrated."
            $SelectedDiskLabel = Get-RMVMBasedSourceDiskLabel -Source $Source
            $PSBoundParameters["SelectedDiskLabel"] = $SelectedDiskLabel
        }
    
        $MigrationExtensionResponse = Get-RMMigrationExtension -OrganizationId $CloudAccount.organization_id
        $MigrationExtensionArray, $MigrationExtensionOSMArray = Get-RMMigrationExtensionFromStep -MigrationExtensionArray $MigrationExtensionResponse.content

        $SourceErrors = Confirm-RMAWSVMBasedFullMigrationParameterWithSource -UserParameter $PSBoundParameters -Source $Source `
            -MigrationExtension $MigrationExtensionArray
        $Errors += $SourceErrors

        $CloudAttributesSummary = Get-CloudAttributesSummary -CloudAccount $CloudAccount

        $CloudAttributesErrors = Confirm-RMCloudAttributes -UserParameter $PSBoundParameters -CloudAccount $CloudAccount -Source $Source -CloudAttributes $CloudAttributes `
            -CloudAttributesSummary $CloudAttributesSummary
        $Errors += $CloudAttributesErrors
    }

    Add-RMMigrationExtension -UpdatedParameter $UserInput -MigrationExtension $MigrationExtension `
        -MigrationExtensionOSM $MigrationExtensionOSM -MigrationExtensionResponse $MigrationExtensionResponse

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

    if ([string]::IsNullOrWhiteSpace($TargetVMName)) {
        $TargetVMName = $Source.host
    }

    $SelectedDisk = Get-RMSelectedDiskByDiskLabel -DiskLabel $SelectedDiskLabel -Source $Source

    if (![string]::IsNullOrWhiteSpace($KMSKeyAliasName)) {
        $KMSKeyAliasName = Get-RMARNByKMSKey -KMSKeyObject $CloudAttributes.properties.regions.kms_keys -KMSName $KMSKeyAliasName
    }

    if ([string]::IsNullOrWhiteSpace($VolumeType)) {
        $VolumeType = "ssd2"
    } else  {
        try {
            $ValidVolumeType = Confirm-RMSupportVolumeType -Source $Source
            $VolumeType = Get-RMVolumeType -VolumeType $VolumeType -SupportVolumeType $ValidVolumeType
        }
        catch {
            $Errors += $PSItem.Exception.Message
        }
    }

    if ([string]::IsNullOrWhiteSpace($Region) -or $Region -ieq $CloudAccount.appliance.cloud_properties.region) {
        $Region = $CloudAccount.appliance.cloud_properties.region
        if ([string]::IsNullOrWhiteSpace($VPCID)) {
            $VPCID = $CloudAccount.appliance.cloud_properties.vpc
        }

        if ([string]::IsNullOrWhiteSpace($SubnetID)) {
            $SubnetID = $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.network_name
        }
    }

    $VPCIDToSecurityGroup = Get-RMVPCAndSecurityGroupMapping -CloudAttributes $CloudAttributes

    $DefaultSecurityGroup = $VPCIDToSecurityGroup[$VPCID].name | Where-Object {$_ -clike ("RM-Migration-TargetWorker*")}
    if ($EnforceTargetNetworkIsolation) {
        $SecurityGroup = $DefaultSecurityGroup 
    }  else {
        foreach ($SG in $DefaultSecurityGroup) {
            if ($SecurityGroup -notcontains $SG) {
                $SecurityGroup += $SG
            }
        }
    }
    
    $SecurityGroupMapping = Get-RMSecurityGroupIDToNameMapping -SelectedSecurityGroupName $SecurityGroup `
        -SecurityGroup $VPCIDToSecurityGroup[$VPCID]


    if ([string]::IsNullOrWhiteSpace($AssignPublicIP)) {
        $AssignPublicIP = "Use-subnet-setting"
    }
    $AutoAssignPublicIPBool = Get-RMAutoAssignPublicIP -AutoAssignPublicIPName $AssignPublicIP `
        -SubnetObject $CloudAttributes.properties.regions[0].vpcs.subnets -SubnetId $SubnetID

    if (![string]::IsNullOrWhiteSpace($IAMRole)) {
        $RoleARN = Get-RMARN -CloudAttributesSummary $CloudAttributesSummary -IAMRoleName $IAMRole  
    }
    
    $InstanceTagAsHashTable = $null
    try {
        $InstanceTagAsHashTable = Get-RMStringArrayAsHashtable -InputItems $InstanceTag -ParameterName "InstanceTag"
    }
    catch {
        $Errors += $PSItem.Exception.Message
    }

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

    $MTUSizeResponse = Invoke-RMProjectSettings -OrganizationId $CloudAccount.organization_id
    $MTUSize = $MTUSizeResponse.aws.mtu_size

    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
        CloudAttributes = $CloudAttributes
        Entitlement = $Entitlement
        Source = $Source
        SourceVMName = $SourceVMName
        ScheduledAt = $ScheduledAt
        TargetVMName = $TargetVMName
        SelectedDisk = $SelectedDisk
        EncryptAllVolume = $EncryptAllVolume
        KMSKey = $KMSKey
        VolumeType = $VolumeType
        IOPS = $IOPS
        Region =  $Region
        VPCID = $VPCID
        SubnetID = $SubnetID
        AutoAssignPublicIP = $AutoAssignPublicIPBool
        StaticPrivateIP = $StaticPrivateIP
        InstanceType = $InstanceType
        EnforceTargetNetworkIsolation = $EnforceTargetNetworkIsolation
        SecurityGroup = $SecurityGroupMapping
        AddIAMRole = $RoleARN
        CreateAMI = $CreateAMI
        InstanceTag = $InstanceTagAsHashTable
        ShutdownSource = $ShutdownSource
        ShutdownTarget = $ShutdownTarget
        FinalizeMigration = $FinalizeMigration
        MigrationInstruction = $MigrationInstructionAsHashTable
        IgnoreValidationError = $IgnoreValidationError
        OverrideExistingMigration = $OverrideExistingMigration
        MTUSize = $MTUSize
    }

    $HashArguments += $UserInput

    $Response = New-RMAWSVMBasedMigrationProfile @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-RMCloudAttributes {
    param (
        [hashtable] $UserParameter,
        [System.Object] $CloudAccount,
        [System.Object] $Source,
        [System.Object] $CloudAttributes,
        [System.Object] $CloudAttributesSummary
    )

    $Errors = @()
    $RegionParameterPresentAndValid = $true

    if ($UserParameter.ContainsKey("DiskEncryptionType") -and ![string]::IsNullOrWhiteSpace($UserParameter["DiskEncryptionType"]) -and `
        $UserParameter.ContainsKey("KMSKeyAliasName") -and ![string]::IsNullOrWhiteSpace($UserParameter["KMSKeyAliasName"]) -and `
         (!$UserParameter.ContainsKey("EncryptAllVolume") -or !$UserParameter["EncryptAllVolume"])) {
                $Errors +=  "EncryptAllVolume is required, when parameter 'DiskEncryptionType' and 'KMSKeyAliasName' parameters is specified."
    } elseif (($UserParameter.ContainsKey("EncryptAllVolume") -or $UserParameter["EncryptAllVolume"]) -and `
            $UserParameter.ContainsKey("KMSKeyAliasName") -and ![string]::IsNullOrWhiteSpace($UserParameter["KMSKeyAliasName"]) -and `
            (!$UserParameter.ContainsKey("DiskEncryptionType") -or [string]::IsNullOrWhiteSpace($UserParameter["DiskEncryptionType"]))) {
            $Errors +=  "DiskEncryptionType is required, when parameter 'EncryptAllVolume' is 'true' and 'KMSKeyAliasName' parameter is specified."
    } elseif ((!$UserParameter.ContainsKey("DiskEncryptionType") -or [string]::IsNullOrWhiteSpace($UserParameter["DiskEncryptionType"])) -and `
            (!$UserParameter.ContainsKey("EncryptAllVolume") -or !$UserParameter["EncryptAllVolume"]) -and `
            $UserParameter.ContainsKey("KMSKeyAliasName") -and ![string]::IsNullOrWhiteSpace($UserParameter["KMSKeyAliasName"])) {
            $Errors += "'EncryptAllVolume' and 'DiskEncryptionType' are required, when the 'KMSKeyAliasName' perimeter is specified."
    } elseif ($UserParameter.ContainsKey("KMSKeyAliasName") -and ![string]::IsNullOrWhiteSpace($UserParameter["KMSKeyAliasName"]) -and `
             $UserParameter.ContainsKey("EncryptAllVolume") -and $UserParameter["EncryptAllVolume"] -and `
             $UserParameter.ContainsKey("DiskEncryptionType") -and ![string]::IsNullOrWhiteSpace($UserParameter["DiskEncryptionType"])) {
            if ( "customer-managed-key" -ieq $UserParameter["DiskEncryptionType"]) {
                $KMSKeyArray =  $CloudAttributes.properties.regions.kms_keys.alias_name
                if ($KMSKeyArray -notcontains $KMSKeyAliasName) { 
                    $Errors += "KMSKeyAliasName '$KMSKeyAliasName' does not exist."
                }
            } else {
                $Errors += "'DiskEncryptionType' can be 'customer-managed-key', when when parameter 'EncryptAllVolume' is 'true' and 'KMSKeyAliasName' parameter is specified."
            }
    }

    
    if ($UserParameter.ContainsKey("VolumeType") -and ![string]::IsNullOrWhiteSpace($UserParameter["VolumeType"])) {
        if (![string]::IsNullOrWhiteSpace($UserParameter["IOPS"])) {
            try {
                $IOPS = [int]$UserParameter["IOPS"]
                if ("io1" -ieq $UserParameter["VolumeType"] -and  ($IOPS  -lt 100 -or $IOPS  -gt 700)) {
                    $Errors += "IOPS should be in the range of 100 to 700"
                } elseif ("io2" -ieq $UserParameter["VolumeType"]  -and ($IOPS -lt 100 -or $IOPS -gt 7000)) {
                    $Errors += "IOPS should be in the range of 100 to 7000"
                }
            } catch {
                $Errors += "'IOPS' must be an integer value only."
            } 
        }
    }

    if ($UserParameter.ContainsKey("Region") -and ![string]::IsNullOrWhiteSpace($UserParameter["Region"])) {
        $RegionErrors, $RegionParameterPresentAndValid = Confirm-RMAWSRegionParameter -UserParameter $UserParameter -CloudAccount $CloudAccount
        $Errors += $RegionErrors
        if ($RegionParameterPresentAndValid) {
            
            $ValidRegion =  Confirm-RMRegion -CloudAttributesSummary $CloudAttributesSummary -RegionName $UserParameter["Region"]
            if (!$ValidRegion) {
               $Errors += "Invalid Region Name"
            } else {
                $RegionArray = Get-RMCloudAttributesByRegion -CloudAccount $CloudAccount -RegionName $UserParameter["Region"]
            }
        }
    } else {
        $Region = $CloudAccount.appliance.cloud_properties.region
        $RegionArray = Get-CachedCloudAttribute -CloudAccount $CloudAccount
    }

    if ($RegionParameterPresentAndValid) {
        if ($UserParameter.ContainsKey("VPCID") -and ![string]::IsNullOrWhiteSpace($UserParameter["VPCID"])) {
            $ValidVPCID, $VPC =  Confirm-RMVPCID -VPCList $RegionArray.properties.regions.vpcs -VPCID $VPCID
            if (!$ValidVPCID) {
                $Errors += "VPCID '$VPCID' does not exist in region '$Region'"
            } else {
                if ($UserParameter.ContainsKey("SubnetID") -and ![string]::IsNullOrWhiteSpace($UserParameter["SubnetID"])) {
                    $ValidSubnet, $Subnet = Confirm-RMSubnet -VPC $VPC -SubnetId $SubnetID
                    if (!$ValidSubnet) {
                        $Errors += "SubnetID '$SubnetID' does not exist in VPC with ID '$VPCID'"
                    }
                }
            }
        } elseif ($UserParameter.ContainsKey("SubnetID") -and ![string]::IsNullOrWhiteSpace($UserParameter["SubnetID"])) {
            $Errors +=  "VPCID is required, when the 'SubnetID' perimeter is specified."
        }
    }

    if ($UserParameter.ContainsKey("IAMRole") -and ![string]::IsNullOrWhiteSpace($UserParameter["IAMRole"])) {
        if ($RegionArray.properties.instance_profiles.name -notcontains $UserParameter["IAMRole"]) {
            $Errors += "IAMRole does not exist"
        }
    }
    
    $ValidInstanceType = Confirm-RMInstanceType -InstanceTypeName  $RegionArray.properties.regions.instance_types `
        -UserInstanceTypeName $UserParameter["InstanceType"]
    if (!$ValidInstanceType) {
        $Errors += "Invalid InstanceType"
    }

    if ($UserParameter.ContainsKey("SecurityGroup") -and ![string]::IsNullOrWhiteSpace($UserParameter["SecurityGroup"])) {
        $VPCIDToSecurityGroup = Get-RMVPCAndSecurityGroupMapping -CloudAttributes $CloudAttributes
        if ([string]::IsNullOrWhiteSpace($UserParameter["VPCID"])) {
            $UserParameter["VPCID"] = $CloudAccount.appliance.cloud_properties.vpc
        }
        $ValidSecurityGroup = Confirm-RMSecurityGroup -UserSecurityGroupName $UserParameter["SecurityGroup"] -SecurityGroupName $VPCIDToSecurityGroup[$UserParameter["VPCID"]].name
        if (!$ValidSecurityGroup) {
            $Errors += "Invalid SecurityGroup"
        }
    }
 
    
    return $Errors
}

function Get-RMIAMRoleToARNMapping {
    param(
        [System.Object] $CloudAttributes
    )

    $IAMNameToARNMapping = @{}
    foreach ($InstanceProfile in $CloudAttributes.properties.instance_profiles) {
        $IAMNameToARNMapping[$InstanceProfile.name] = $InstanceProfile.arn
    }

    return $IAMNameToARNMapping
}

function Get-VPCIDAndSubnetIDValue {
    param (
       [system.Object] $RegionArray,
       [System.Object] $CloudAccount,
       [string] $VPCID,
       [string] $SubnetID
    )

    $Errors = @() 
    $ValidVPCID = $true

    if ([string]::IsNullOrWhiteSpace($VPCID)) {
        $VPCID = $CloudAccount.appliance.cloud_properties.vpc
    }
    $ValidVPCID, $VPC =  Confirm-RMVPCID -VPCList $RegionArray.properties.regions.vpcs -VPCID $VPCID
    if (!$ValidVPCID) {
        $Errors += "VPCID '$VPCID' does not exist in region '$Region'"
    } else {
        if ([string]::IsNullOrWhiteSpace($SubnetID)) {
            $SubnetID = $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.network_name
        }
        $ValidSubnet, $Subnet = Confirm-RMSubnet -VPC $VPC -SubnetId $SubnetID
        if (!$ValidSubnet) {
            $Errors += "SubnetID '$SubnetID' does not exist in VPC with ID '$VPCID"
        }
    }

    return $Errors, $VPC, $VPCID, $Subnet, $SubnetID
}

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