Migration/AWS/AWS.psm1
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 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 Common) 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-RMAWSOSBasedMigration { param( [Parameter(Mandatory)] [System.Object] $CloudAccount, [System.Object] $Entitlement ) $Source = $null $SourceIP = Read-Host "Enter the IP address of the source machine to be migrated" if ("" -eq $SourceIP) { throw "Source IP address is required to start a migration." } else { $Source = Get-RMSourceByIP -IPAddress $SourceIP } $TargetVMName = Read-Host "Enter target VM Name" $MountPoints = Get-MountPoint -Source $Source if (0 -eq $MountPoints.count) { throw "Source has no mount points, cannot be migrated" } $MountPointsAsString = $MountPoints.values -join ", " Write-Output "Mount points to be migrated [$MountPointsAsString]" | Out-Host $ExcludedMountPoints = Get-RMExcludedMountPoint -OSType $Source.os_type -MountPoints $MountPoints.Values $SelectedMountPoints = $MountPoints if ("" -ne $ExcludedMountPoints) { $ExcludedList = $ExcludedMountPoints.Split(",").Trim() $SelectedMountPoints = Get-RMSelectedMount -MountPoints $MountPoints -DifferenceList $ExcludedList -IncludeEqual $false } $EncryptVolumes = $false $ReadValue = Read-Host "Enable EBS encryption on all volumes (true/false)[false]" if ("" -ne $ReadValue) { $EncryptVolumes = [System.Convert]::ToBoolean($ReadValue) } $VolumeType = "ssd2" $IOPS = $null $ReadValue = Read-Host "Enter volume type (Magnetic, GP2, GP3, IO1, IO2)[GP2]" if ("" -ne $ReadValue) { $VolumeType = Get-RMVolumeType -VolumeType $ReadValue if ("io1" -ieq $ReadValue) { $IOPS = Read-Host "Enter the IOPS value between 100 and 700 (50:1)" } elseif ("io2" -ieq $ReadValue) { $IOPS = Read-Host "Enter the IOPS value between 100 and 7000 (500:1)" } } $Region = Read-Host "Enter region" $VPCID = $CloudAccount.appliance.cloud_properties.vpc $ReadValue = Read-Host "Enter VPC ID [$VPCID]" if ("" -ne $ReadValue) { $VPCID = $ReadValue } $SubnetID = $CloudAccount.appliance.cloud_properties.network.interfaces.eth0.network_name $ReadValue = Read-Host "Enter subnet ID [$SubnetID]" if ("" -ne $ReadValue) { $SubnetID = $ReadValue } $AutoAssignPublicIP = $false $ReadValue = Read-Host "Auto assign public IP (true/false)[false]" if ("" -ne $ReadValue) { $AutoAssignPublicIP = [System.Convert]::ToBoolean($ReadValue) } $StaticPrivateIP = Read-Host "Enter static private IP to be assigned to target [None]" #TODO: Add tenancy. $InstanceType = Read-Host "Enter instance type" $EnforceTargetNetworkIsolation = $true $ReadValue = Read-Host "Enforce target network isolation (true/false)[true]" if ("" -ne $ReadValue) { $EnforceTargetNetworkIsolation = [System.Convert]::ToBoolean($ReadValue) } $SecurityGroups = @("RM-Migration-TargetWorker-" + $CloudAccount.appliance.cloud_properties.vpc) if (!$EnforceTargetNetworkIsolation) { $ReadValue = Read-Host "Enter one or more security groups separated by commas" if ("" -ne $ReadValue) { $SecurityGroups += $ReadValue.Split(",").Trim() } } $IAMRole = $null $IAMRole = Read-Host "Enter IAM role name to add [None]" #TODO: prepare proper ARN $ShouldCreateAMI = $false $ReadValue = Read-Host "Create an AMI from the target instance (true/false)[false]" if ("" -ne $ReadValue) { $ShouldCreateAMI = [System.Convert]::ToBoolean($ReadValue) } $ReadValue = Read-Host "Enter one or more instance tags in the format 'key=value' and separated by commas [None]" $InstanceTags = Get-RMStringAsHashtable -InputString $ReadValue $ShutdownSource = $false $ReadValue = Read-Host "Shutdown source after data is fully migrated (true/false)[false]" if ("" -ne $ReadValue) { $ShutdownSource = [System.Convert]::ToBoolean($ReadValue) } $ShutdownTarget = $false $ReadValue = Read-Host "Shutdown target after data is fully migrated (true/false)[false]" if ("" -ne $ReadValue) { $ShutdownTarget = [System.Convert]::ToBoolean($ReadValue) } $RemoveRMSAgent = $false $ReadValue = Read-Host "Remove RMS agent post migration (true/false)[false]" if ("" -ne $ReadValue) { $RemoveRMSAgent = [System.Convert]::ToBoolean($ReadValue) } $ReadValue = Read-Host "Enter migration instructions in the format 'key=value' and separated by commas [None]" $MigrationInstructions = Get-RMStringAsHashtable -InputString $ReadValue #TODO: Add Ignore validation errors after we add the preflight cmdlet $HashArguments = @{ CloudAccount = $CloudAccount Entitlement = $Entitlement Source = $Source TargetVMName = $TargetVMName SelectedMounts = $SelectedMountPoints EncryptVolumes = $EncryptVolumes VolumeType = $VolumeType IOPS = $IOPS Region = $Region VPCID = $VPCID SubnetID = $SubnetID AutoAssignPublicIP = $AutoAssignPublicIP StaticPrivateIP = $StaticPrivateIP InstanceType = $InstanceType EnforceTargetNetworkIsolation = $EnforceTargetNetworkIsolation SecurityGroups = $SecurityGroups IAMRole = $IAMRole ShouldCreateAMI = $ShouldCreateAMI InstanceTags = $InstanceTags ShutdownSource = $ShutdownSource ShutdownTarget = $ShutdownTarget RemoveRMSAgent = $RemoveRMSAgent MigrationInstructions = $MigrationInstructions } $Response = New-RMAWSMigrationProfile @HashArguments $RMLoginResult = Get-Variable -Name "RMContext-UserLogin" $Uri = Get-Variable -Name "RMContext-ReactorURI" $Headers = @{ Accept = "application/rm+json" "X-Auth-Token" = $RMLoginResult.Value.token } $Params = @{ Method = "Post" Uri = $Uri.Value + "/migrationprofiles/" + $Response.id + "/migrations" Headers = $Headers ContentType = "application/json" } return Invoke-RMRestMethod -Params $Params } 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 -Region $RegionName -CloudAccount $CloudAccount $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)) $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-RMAWSOSBasedMigration, Start-RMInteractiveAWSVMBasedMigration |