DifferentialMigration/DifferentialMigration.psm1
using module '../Common/Result' Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath DifferentialProfile | Join-Path -ChildPath DifferentialProfile) Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath Util | Join-Path -ChildPath MigrationUtil) Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath Preflight | Join-Path -ChildPath Preflight) Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath Common | Join-Path -ChildPath Common) Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath CloudAccount | Join-Path -ChildPath CloudAccount) function Start-RMInteractiveDM { param () $OrganizationId = Get-Variable -Name "RMContext-CurrentProjectId" -ValueOnly $Name = "PowerShell - Differential Profile - " + [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() [RMMigrationReturn] $RMMigrationReturn = [RMMigrationReturn]::new() $Migration, $Source = Read-RMFullMigrationId if ((Test-RMSourceHasRunningMigration -OrganizationId $OrganizationId -SourceId $Source.id)) { $UserMessage = "Currently there is a migration running for the source of the given migration ID, this differential migration might fail." Write-Warning $UserMessage $RMMigrationReturn.AddRMWarning([RMWarning]::new($UserMessage)) } $ScheduledAt = Read-RMMigrationSchedule -IsDifferentialMigration $true -CloudType $Migration.target_cloud_type $TransferMode = "run-once" if ("Continuous" -ieq $ScheduledAt) { $TransferMode = "continuous" $ScheduledAt = "" } $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 $RemoveRMSAgent = Read-RMBoolean -UserMessage "Remove RMS agent post migration" -DefaultValue $false $ReadValue = Read-RMPair -UserMessage "Enter one or more migration instructions in the format 'key=value', separated by commas" ` -Separator "=" -DefaultValue "None" $MigrationInstructions = Get-RMStringAsHashtable -InputString $ReadValue $IgnoreValidationErrors = Read-RMBoolean -UserMessage "Ignore Validation Errors" -DefaultValue $false if (!('windows' -eq $Migration.os_type -or $Migration.source.discovered_features.features_list -contains 'linux_block_based')) { $UpdateMountsPoints = Read-RMBoolean -UserMessage "Update mounts points" -DefaultValue $false } else { $UpdateMountsPoints = $true } $SelectedMounts = @() if ($UpdateMountsPoints) { $UpdateIncludesAndExcludes = $true $SelectedMounts = $Migration.target.properties.selected_mounts.PSObject.properties.Name if ('windows' -ieq $Migration.os_type -and ![string]::IsNullOrWhiteSpace($Migration.upgrade_os_version)) { $TransferMethod = 'file-based' } else { $TransferMethod = (Get-RMTransferMethod -Source $Source -SelectedMountPoints $SelectedMounts -IsInteractive $true)[0] } if ('block-based' -eq $TransferMethod) { $UpdateIncludesAndExcludes = $false } $MountPointsAsString = $SelectedMounts -join "," Write-Output "Mount points to be migrated [$MountPointsAsString]" $SelectedMountPoints = @() if ($SelectedMounts.Count -gt 1) { $ExcludedMountPoints = Get-RMExcludedMountPoint -OSType $Migration.os_type -MountPoints $SelectedMounts -IsDifferentialMigration $true if ("" -ne $ExcludedMountPoints) { $ExcludedList = $ExcludedMountPoints.Split(",").Trim() $Results = Compare-Object -ReferenceObject $SelectedMounts -DifferenceObject $ExcludedList -IncludeEqual foreach ($Result in $Results) { if (!($Result.SideIndicator -contains "==")) { $SelectedMountPoints += $Result.InputObject } } } } if ($SelectedMountPoints.Count -eq 0) { $SelectedMountPoints = $SelectedMounts } if (!$UpdateIncludesAndExcludes) { $MountPoints = Update-RMDefaultMountPoints -UpdateSelectedMounts $SelectedMountPoints -Migration $Migration } else { foreach($MountPoint in $Migration.target.properties.selected_mounts.PSObject.properties) { $MountName = $MountPoint.name if ($SelectedMountPoints -contains $MountName) { $Includes = @() if ('linux' -eq $Migration.os_type) { $OverwriteIncludes = Read-RMBoolean -UserMessage "Overwrite Includes for mount point '$MountName'" -DefaultValue $false if ($OverwriteIncludes) { $IncludesAsString = Read-RMString -UserMessage "For mount point '$MountName', update one or more include paths, separated by commas " ` -DefaultValue 'None' -ParameterName "Includes" -IsRequired $false if ("" -ne $IncludesAsString) { $Includes = $IncludesAsString -split "," } } } $Excludes = @() $DefaultExcludes = 'None' if ("linux" -eq $Migration.os_type) { $DefaultExcludes = '**/.snapshot' } $OverwriteExcludes = Read-RMBoolean -UserMessage "Overwrite Excludes for mount point '$MountName'" -DefaultValue $false if ($OverwriteExcludes) { $ExcludesAsString = Read-RMString -UserMessage "For mount point '$MountName', update one or more exclude paths, separated by commas"` -DefaultValue $DefaultExcludes -ParameterName "Excludes" -IsRequired $false if ("" -ne $ExcludesAsString) { $Excludes = $ExcludesAsString -split "," } } elseif ("linux" -eq $Migration.os_type) { $Excludes += '**/.snapshot' } $MountPoints += @{$MountName = @{"includes" = $Includes "excludes" = $Excludes } } } } } } else { $MountPoints = Update-RMDefaultMountPoints -UpdateSelectedMounts $Migration.target.properties.selected_mounts.PSObject.properties.Name -Migration $Migration } $TargetProperties = Edit-RMMountPoints -TargetProperties $Migration.target.properties -MountPoints $MountPoints $UpdateSourceCredentials = Read-RMBoolean -UserMessage "Update Source Cresentials" -DefaultValue $false if ($UpdateSourceCredentials) { $SourceUserName = Read-RMString -UserMessage "Enter the username" -ParameterName "Username" -IsRequired $true $SourceUseSSHPrivateKey = Read-RMBoolean -UserMessage "Use SSH private key" -DefaultValue $false if ($SourceUseSSHPrivateKey) { $SourcePrivateKey = Read-RMString -UserMessage "Enter the private key" -ParameterName "Private Key" -IsRequired $true $SourcePassword = Read-RMSecureString -UserMessage "Enter the passphrase" -ParameterName "Passphrase" ` -ConfirmMessage "Confirm the passphrase" -ConfirmParameterName "Confirm passphrase" -IsRequired $false } else { $SourcePassword = Read-RMSecureString -UserMessage "Enter the password" -ParameterName "Password" ` -ConfirmMessage "Confirm the password" -ConfirmParameterName "Confirm password" -IsRequired $true } $SourceDomain = Read-RMString -UserMessage "Enter the domain" -ParameterName "Domain" -IsRequired $false } $UpdateTargetInstance = Read-RMBoolean -UserMessage "Update target instance/VM credentials" -DefaultValue $false if ($UpdateTargetInstance) { $TargetUserName = Read-RMString -UserMessage "Enter the username" -ParameterName "Username" -IsRequired $true $TargetUseSSHPrivateKey = Read-RMBoolean -UserMessage "Use SSH private key" -DefaultValue $false if ($TargetUseSSHPrivateKey) { $TargetPrivateKey = Read-RMString -UserMessage "Enter the private key" -ParameterName "Private Key" -IsRequired $true $TargetPassword = Read-RMSecureString -UserMessage "Enter the passphrase" -ParameterName "Passphrase" ` -ConfirmMessage "Confirm the passphrase" -ConfirmParameterName "Confirm passphrase" -IsRequired $false } else { $TargetPassword = Read-RMSecureString -UserMessage "Enter the password" -ParameterName "Password" ` -ConfirmMessage "Confirm the password" -ConfirmParameterName "Confirm password" -IsRequired $true } $TargetDomain = Read-RMString -UserMessage "Enter the domain" -ParameterName "Domain" -IsRequired $false } $UpdateTargetIPAddress = Read-RMBoolean -UserMessage "Update target IP address" -DefaultValue $false $TargetIPAddress = $Migration.target.ip if ($UpdateTargetIPAddress) { $TargetIPAddress = Read-RMIPAddress -UserMessage "Enter new target IP address" -ParameterName "Target IP Address" ` -DefaultValue "$TargetIPAddress" -IsRequired $false } $HashArguments = @{ Name = $Name ScheduledAt = $ScheduledAt OrganizationId = $OrganizationId ApplianceId = $Migration.cloud_appliance_id MigrationId = $Migration.id ShutdownSource = $ShutdownSource ShutdownTarget = $ShutdownTarget RemoveTargetAgent = $RemoveRMSAgent MigrationInstructions = $MigrationInstructions TargetProperties = $TargetProperties IgnoreValidationErrors = $IgnoreValidationErrors SourceUsername = $SourceUserName SourcePrivateKey = $SourcePrivateKey SourcePassword = $SourcePassword SourceDomain = $SourceDomain SourceHost = $Migration.source_hostname TargetUsername = $TargetUsername TargetPrivateKey = $TargetPrivateKey TargetPassword = $TargetPassword TargetDomain = $TargetDomain TargetHost = $Migration.target_hostname TargetIPAddress = $TargetIPAddress SourceId = $Migration.source_id TransferMethod = $TransferMethod TransferMode = $TransferMode } $Response = New-RMDifferentialProfile @HashArguments $ShouldExit = Start-RMDifferentialMigrationPreflight -DifferentialProfileId $Response.id -IgnoreValidationErrors $IgnoreValidationErrors -RMMigrationReturn $RMMigrationReturn if ($ShouldExit) { return $RMMigrationReturn } $IsScheduled = ![string]::IsNullOrWhiteSpace($ScheduledAt) $RMLoginResult = Get-Variable -Name "RMContext-UserLogin" $Uri = Get-Variable -Name "RMContext-ReactorURI" $Headers = @{ Accept = "application/json" "X-Auth-Token" = $RMLoginResult.Value.token } $Params = @{ Method = "Post" Uri = $Uri.Value + "/differentialprofiles/" + $Response.id + "/migrations" Headers = $Headers ContentType = "application/json" } $MigrationResponse = Invoke-RMRestMethod -Params $Params return Update-RMMigrationReturnAsSuccess -MigrationResponse $MigrationResponse ` -RMMigrationReturn $RMMigrationReturn -IsScheduledMigration $IsScheduled ` -ReturnMessage "Differential migration started successfully, migration ID" } function Start-RMNonInteractiveDM { param ( [string] $MigrationId, [string] $ScheduledAt, [bool] $RunContinuous, [bool] $ShutdownSource, [bool] $ShutdownTarget, [bool] $RemoveRMSAgent, [string[]] $MigrationInstruction, [bool] $IgnoreValidationError, [bool] $UpdateMountPoint, [hashtable] $UpdateSelectedMount, [bool] $UpdateSourceCredential, [string] $SourceUsername, [bool] $SourceUseSSHPrivateKey, [string] $SourcePrivateKey, [string] $SourcePassphrase, [string] $SourceConfirmPassphrase, [string] $SourcePassword, [string] $SourceConfirmPassword, [string] $SourceDomain, [bool] $UpdateTargetInstance, [string] $TargetUsername, [bool] $TargetUseSSHPrivateKey, [string] $TargetPrivateKey, [string] $TargetPassphrase, [string] $TargetConfirmPassphrase, [string] $TargetPassword, [string] $TargetConfirmPassword, [string] $TargetDomain, [string] $TargetIPAddress, [System.Object] $TransferMethod ) [RMMigrationReturn] $RMMigrationReturn = [RMMigrationReturn]::new() $Errors, $IsValidMigrationId = Confirm-RMDMParameter -UserParameter $PSBoundParameters if (!$IsValidMigrationId) { $RMMigrationReturn.SetReturnCode([RMReturn]::ERROR) $Errors | ForEach-Object -Process {$RMMigrationReturn.AddRMError([RMError]::new($_))} Out-RMUserParameterResult -ErrorMessage $Errors return $RMMigrationReturn } $OrganizationId = Get-Variable -Name "RMContext-CurrentProjectId" -ValueOnly $Migration = Get-RMMigrationByIdAndStatus -MigrationId $MigrationId $Source, $ErrorString = Confirm-RMSource -SourceId $Migration.source_id if ("" -ne $ErrorString) { $Errors += $ErrorString } $MigrationInstructionAsHashTable = @{} try { if (![string]::IsNullOrWhiteSpace($MigrationInstruction)) { $MigrationInstructionAsHashTable = Get-RMStringArrayAsHashtable -InputItems $MigrationInstruction -ParameterName "MigrationInstruction" } } catch { $Errors += $PSItem.Exception.Message } $MigrationType = $Migration.migration_type $HeartbeatStatus = Test-RMApplianceHeartbeating -CloudAccountId $Migration.cloud_account_id -AccountType $Migration.target_cloud_type if (!$HeartbeatStatus) { $Errors += "Migration appliance is not ready, cannot start differential migration" } if ("full" -ne $MigrationType) { $Errors += "Migration type for the given migration ID is '$MigrationType', differential migration can only be started for a full migration." } else { $MigrationList = Get-RMMigrationListBySourceId -OrganizationId $OrganizationId -SourceId $Source.id foreach ($Mig in $MigrationList) { if ("running" -eq $Mig.state) { $UserMessage = "Currently there is a migration running for the source of the given migration ID, this differential migration might fail." Write-Warning $UserMessage $RMMigrationReturn.AddRMWarning([RMWarning]::new($UserMessage)) Break; } } } $TransferMode, $ScheduledAt = Get-RMTransferModeAndSchedule -RunContinuous $RunContinuous -ScheduledAt $ScheduledAt if ($UpdateMountPoint -or 'windows' -eq $Migration.os_type -or $Migration.source.discovered_features.features_list -contains 'linux_block_based') { $UpdateIncludesAndExcludes = $true if ('windows' -ieq $Migration.os_type -and ![string]::IsNullOrWhiteSpace($Migration.upgrade_os_version)) { if (![string]::IsNullOrWhiteSpace($TransferMethod) -and "block-based" -ieq $TransferMethod) { $Errors += "Transfer method for post OS Modernization differential migrations must be 'file-based'." } else { $TransferMethod = "file-based" } } else { $TransferMethod, $ErrorString = Get-RMTransferMethod -Source $Source -SelectedMountPoints $UpdateSelectedMount.Keys` -TransferMethod $TransferMethod } if ("" -ne $ErrorString) { $Errors += $ErrorString } if ('block-based' -eq $TransferMethod) { $UpdateIncludesAndExcludes = $false } $MountPoints, $MountPointsErrors = Update-RMMountPoints -UpdateSelectedMounts $UpdateSelectedMount -UpdateIncludesAndExcludes $UpdateIncludesAndExcludes -Migration $Migration if ($MountPointsErrors -gt 0) { $Errors += $MountPointsErrors } } else { $MountPoints = Update-RMDefaultMountPoints -UpdateSelectedMounts $Migration.target.properties.selected_mounts.PSObject.properties.Name -Migration $Migration } $TargetProperties = Edit-RMMountPoints -TargetProperties $Migration.target.properties -MountPoints $MountPoints if ([string]::IsNullOrWhiteSpace($TargetIPAddress)) { $TargetIPAddress = $Migration.target.ip } if ($Errors.Count -gt 0) { $RMMigrationReturn.SetReturnCode([RMReturn]::ERROR) $Errors | ForEach-Object -Process {$RMMigrationReturn.AddRMError([RMError]::new($_))} Out-RMUserParameterResult -ErrorMessage $Errors return $RMMigrationReturn } $Name = "PowerShell - Differential Profile - " + [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() $HashArguments = @{ Name = $Name ScheduledAt = $ScheduledAt OrganizationId = $OrganizationId ApplianceId = $Migration.cloud_appliance_id MigrationId = $Migration.id ShutdownSource = $ShutdownSource ShutdownTarget = $ShutdownTarget RemoveTargetAgent = $RemoveRMSAgent MigrationInstructions = $MigrationInstructionAsHashTable TargetProperties = $TargetProperties IgnoreValidationErrors = $IgnoreValidationError SourceUsername = $SourceUsername SourcePrivateKey = $SourcePrivateKey SourcePassword = $SourcePassword SourceDomain = $SourceDomain SourceHost = $Migration.source_hostname TargetUsername = $TargetUsername TargetPrivateKey = $TargetPrivateKey TargetPassword = $TargetPassword TargetDomain = $TargetDomain TargetHost = $Migration.target_hostname TargetIPAddress = $TargetIPAddress SourceId = $Migration.source_id TransferMethod = $TransferMethod TransferMode = $TransferMode } $Response = New-RMDifferentialProfile @HashArguments $ShouldExit = Start-RMDifferentialMigrationPreflight -DifferentialProfileId $Response.id ` -IgnoreValidationErrors $IgnoreValidationError -RMMigrationReturn $RMMigrationReturn if ($ShouldExit) { return $RMMigrationReturn } $IsScheduled = ![string]::IsNullOrWhiteSpace($ScheduledAt) $RMLoginResult = Get-Variable -Name "RMContext-UserLogin" $Uri = Get-Variable -Name "RMContext-ReactorURI" $Headers = @{ Accept = "application/json" "X-Auth-Token" = $RMLoginResult.Value.token } $Params = @{ Method = "Post" Uri = $Uri.Value + "/differentialprofiles/" + $Response.id + "/migrations" Headers = $Headers ContentType = "application/json" } $MigrationResponse = Invoke-RMRestMethod -Params $Params return Update-RMMigrationReturnAsSuccess -MigrationResponse $MigrationResponse ` -RMMigrationReturn $RMMigrationReturn -IsScheduledMigration $IsScheduled ` -ReturnMessage "Differential migration started successfully, migration ID" } function Start-RMInteractiveVMDM { param() $UserInput = @{} $OrganizationId = Get-Variable -Name "RMContext-CurrentProjectId" -ValueOnly $Migration, $Source = Read-RMFullMigrationId -VMBasedSourceRequired $true $UserInput.Add("Migration", $Migration) $UserInput.Add("Source", $Source) [RMMigrationReturn] $RMMigrationReturn = [RMMigrationReturn]::new() if ((Test-RMSourceHasRunningMigration -OrganizationId $OrganizationId -SourceId $Source.id)) { $UserMessage = "Currently there is a migration running for the source of the given migration ID, this differential migration might fail." Write-Warning $UserMessage $RMMigrationReturn.AddRMWarning([RMWarning]::new($UserMessage)) } $ScheduledAt = Read-RMMigrationSchedule -IsDifferentialMigration $true -CloudType $Migration.target_cloud_type $TransferMode = "run-once" if ("Continuous" -ieq $ScheduledAt) { $TransferMode = "continuous" $ScheduledAt = "" } else { $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" $UserInput.Add("ShutdownSource", $ShutdownSource) $UserInput.Add("ShutdownTarget", $ShutdownTarget) } $UpgradeTools = $false if ("guestToolsUnmanaged" -ne $Source.attributes.vm_config.toolsVersionStatus -and ` "toolsNotInstalled" -ne $Source.attributes.vm_config.toolsStatus) { $UpgradeTools = Read-RMBoolean -UserMessage "Do you want to upgrade the VM tools on the target VM" -DefaultValue "false" } $UserInput.Add("UpgradeTools", $UpgradeTools) $UserInput.Add("ScheduledAt", $ScheduledAt) $UserInput.Add("TransferMode", $TransferMode) $FinalizeMigration = Read-RMBoolean -UserMessage "Remove snapshot(s) from the Target VM in preparation for a cutover" -DefaultValue "false" $UserInput.Add("FinalizeMigration", $FinalizeMigration) $DefaultMigrationInstrictions = 'None' if (![string]::IsNullOrWhiteSpace($Migration.migration_instructions) -and "{}" -ne $Migration.migration_instructions) { [string] $MigrationInstractionAsString = $Migration.migration_instructions $DefaultMigrationInstrictions = $MigrationInstractionAsString.Replace('{' , '').Replace('}' , '').Replace('@','') } $ReadValue = Read-RMPair -UserMessage "Enter one or more migration instructions in the format 'key=value', separated by commas" ` -Separator "=" -DefaultValue $DefaultMigrationInstrictions $MigrationInstructions = Get-RMStringAsHashtable -InputString $ReadValue $UserInput.Add("MigrationInstruction", $MigrationInstructions) $IgnoreValidationErrors = Read-RMBoolean -UserMessage "Ignore validation errors" -DefaultValue "false" $UserInput.Add("IgnoreValidationErrors", $IgnoreValidationErrors) $Response = New-RMVMBasedDifferentialProfile @UserInput $ShouldExit = Start-RMDifferentialMigrationPreflight -DifferentialProfileId $Response.id ` -IgnoreValidationErrors $IgnoreValidationErrors -RMMigrationReturn $RMMigrationReturn if ($ShouldExit) { return $RMMigrationReturn } $IsScheduled = ![string]::IsNullOrWhiteSpace($ScheduledAt) $MigrationResponse = Invoke-RMDifferentialMigrationPost -DifferentialProfileResponse $Response return Update-RMMigrationReturnAsSuccess -MigrationResponse $MigrationResponse ` -RMMigrationReturn $RMMigrationReturn -IsScheduledMigration $IsScheduled ` -ReturnMessage "Differential migration started successfully, migration ID" } function Start-RMNonInteractiveVMDM { param ( [System.Guid] $MigrationId, [string] $ScheduledAt, [bool] $RunContinuous, [bool] $UpgradeTool, [bool] $ShutdownSource, [bool] $ShutdownTarget, [bool] $FinalizeMigration, [string[]] $MigrationInstruction, [bool] $IgnoreValidationError ) $UserInput = @{} [RMMigrationReturn] $RMMigrationReturn = [RMMigrationReturn]::new() $Errors, $IsValidMigrationId = Confirm-RMVMBasedDMCommonParameter -UserParameter $PSBoundParameters if (!$IsValidMigrationId) { $RMMigrationReturn.SetReturnCode([RMReturn]::ERROR) $Errors | ForEach-Object -Process {$RMMigrationReturn.AddRMError([RMError]::new($_))} Out-RMUserParameterResult -ErrorMessage $Errors return $RMMigrationReturn } $Migration = Get-RMMigrationByIdAndStatus -MigrationId $MigrationId $UserInput.Add("Migration", $Migration) $Source, $ErrorString = Confirm-RMSource -SourceId $Migration.source_id if ("" -ne $ErrorString) { $Errors += $ErrorString } $UserInput.Add("Source", $Source) $ErrorsInMigrationObject, $Warnings = Confirm-RMVMBasedDMParameterWithMigrationAndSource -UserParameter $PSBoundParameters ` -Migration $Migration -Source $Source $Errors += $ErrorsInMigrationObject $TransferMode, $ScheduledAt = Get-RMTransferModeAndSchedule -RunContinuous $RunContinuous -ScheduledAt $ScheduledAt if ("continuous" -ieq $TransferMode) { $ShutdownSource = $false $ShutdownTarget = $false } if ("guestToolsUnmanaged" -ieq $Source.attributes.vm_config.toolsVersionStatus -or ` "toolsNotInstalled" -ieq $Source.attributes.vm_config.toolsStatus) { $UpgradeTool = $false } $MigrationInstructionAsHashTable = @{} try { if (![string]::IsNullOrWhiteSpace($MigrationInstruction)) { $MigrationInstructionAsHashTable = Get-RMStringArrayAsHashtable -InputItems $MigrationInstruction -ParameterName "MigrationInstruction" } } catch { $Errors += $PSItem.Exception.Message } if ($Errors.Count -gt 0 -or $Warnings.Count -gt 0) { $Errors | ForEach-Object -Process {$RMMigrationReturn.AddRMError([RMError]::new($_))} $Warnings | ForEach-Object -Process {$RMMigrationReturn.AddRMWarning([RMWarning]::new($_))} Out-RMUserParameterResult -ErrorMessage $Errors -WarningMessage $Warnings if ($Errors.Count -gt 0) { $RMMigrationReturn.SetReturnCode([RMReturn]::ERROR) return $RMMigrationReturn } } $UserInput.Add("TransferMode", $TransferMode) $UserInput.Add("ScheduledAt", $ScheduledAt) $UserInput.Add("UpgradeTools", $UpgradeTool) $UserInput.Add("ShutdownSource", $ShutdownSource) $UserInput.Add("ShutdownTarget", $ShutdownTarget) $UserInput.Add("FinalizeMigration", $FinalizeMigration) $UserInput.Add("MigrationInstruction", $MigrationInstructionAsHashTable) $UserInput.Add("IgnoreValidationErrors", $IgnoreValidationError) $Response = New-RMVMBasedDifferentialProfile @UserInput $ShouldExit = Start-RMDifferentialMigrationPreflight -DifferentialProfileId $Response.id ` -IgnoreValidationErrors $IgnoreValidationError -RMMigrationReturn $RMMigrationReturn if ($ShouldExit) { return $RMMigrationReturn } $IsScheduled = ![string]::IsNullOrWhiteSpace($ScheduledAt) $MigrationResponse = Invoke-RMDifferentialMigrationPost -DifferentialProfileResponse $Response return Update-RMMigrationReturnAsSuccess -MigrationResponse $MigrationResponse ` -RMMigrationReturn $RMMigrationReturn -IsScheduledMigration $IsScheduled ` -ReturnMessage "Differential migration started successfully, migration ID" } function Update-RMMountPoints { param( [hashtable] $UpdateSelectedMounts, [bool] $UpdateIncludesAndExcludes, [System.Object] $Migration ) $MountPoints = @() $MountPointsErrors = @() $TargetMounts = $Migration.target.properties.selected_mounts.PSObject.properties.Name if ('linux' -eq $Migration.os_type -and ![string]::IsNullOrWhiteSpace($UpdateSelectedMounts)) { $MountPointsErrors += Compare-RMMountPoints -TargetMounts $TargetMounts -SelectedMounts $UpdateSelectedMounts.Keys } # $UpdateSelectedMounts format example # @{"/" = @{"includes" = @("/value1")} ; "/boot" = @{"includes" = @("/value1","/value2") ; "excludes" = @("value1","value2")}} if ([string]::IsNullOrWhiteSpace($UpdateSelectedMounts)) { $MountPoints = Update-RMDefaultMountPoints -UpdateSelectedMounts $Migration.target.properties.selected_mounts.PSObject.properties.Name -Migration $Migration } elseif (!$UpdateIncludesAndExcludes) { $MountPoints = Update-RMDefaultMountPoints -UpdateSelectedMounts $UpdateSelectedMounts.Keys -Migration $Migration } else { foreach ($SelectedMount in $UpdateSelectedMounts.Keys) { $Includes = @() $Excludes = @() if ("windows" -eq $Migration.os_type) { $Excludes = $UpdateSelectedMounts[$SelectedMount]["excludes"] } else { if (![string]::IsNullOrWhiteSpace($UpdateSelectedMounts[$SelectedMount]["includes"])) { $Includes = $UpdateSelectedMounts[$SelectedMount]["includes"] } if ([string]::IsNullOrWhiteSpace($UpdateSelectedMounts[$SelectedMount]["excludes"])) { $Excludes = @('**shnapshot') } else { $Excludes = $UpdateSelectedMounts[$SelectedMount]["excludes"] } } $MountPoints += @{$SelectedMount = @{"includes" = $Includes "excludes" = $Excludes } } } } return $MountPoints, $MountPointsErrors } function Test-RMApplianceHeartbeating { param( [string] $CloudAccountId, [string] $AccountType ) $HeartBeatingCloudAccounts, $NonHeartBeatingCloudAccounts = Get-RMCloudAccountsForCurrentProject -AccountType $AccountType foreach ($CloudAccount in $HeartBeatingCloudAccounts.Values) { if ($CloudAccount.id -eq $CloudAccountId) { return $true } } return $false } function Update-RMDefaultMountPoints { param( [array] $UpdateSelectedMounts, [System.Object] $Migration ) $MountPoints = @{} $Includes = @() $Excludes = @() if ("linux" -eq $Migration.os_type) { $Excludes = @('**/.snapshot') } foreach ($MountPoint in $UpdateSelectedMounts) { $MountPoints += @{$MountPoint = @{"includes" = $Includes "excludes" = $Excludes } } } return $MountPoints } function Compare-RMMountPoints { param( [array] $TargetMounts, [array] $SelectedMounts ) $MountPointsErrors = @() $MountsNotExcludeList = @("/", "/bin", "/boot" , "/dev", "/etc", "/home", "/lib" , "/mnt", "/opt", "/proc", "/root", "/run", "/sbin", "/srv", "/tmp", "/usr", "/var") $Results = Compare-Object -ReferenceObject $TargetMounts -DifferenceObject $SelectedMounts -IncludeEqual if ($Results.SideIndicator -contains "<=" -or $Results.SideIndicator -contains "=>") { foreach ($Result in $Results) { $MountPoint = $Result.InputObject if ($Result.SideIndicator -eq "<=" -and $MountsNotExcludeList -contains $Result.InputObject) { $MountPointsErrors += "Mount points '$MountPoint' is required" } elseif ($Result.SideIndicator -eq "=>") { $MountPointsErrors += "Mount point '$MountPoint' does not exist on the target machine." } } } return $MountPointsErrors } function Confirm-RMDMParameter { param( [hashtable] $UserParameter ) $Errors = @() $ErrorsCommon, $IsValidMigrationId = Confirm-RMVMBasedDMCommonParameter -UserParameter $UserParameter $Errors += $ErrorsCommon if ($UserParameter.ContainsKey("UpdateMountPoint") -and $UserParameter["UpdateMountPoint"]) { if (!$UserParameter.ContainsKey("UpdateSelectedMount") -or [string]::IsNullOrWhiteSpace($UserParameter["UpdateSelectedMount"])) { $Errors += "UpdateSelectedMount is required." } } if ($UserParameter.ContainsKey("UpdateSourceCredential") -and $UserParameter["UpdateSourceCredential"]) { if (!$UserParameter.ContainsKey("SourceUsername") -or [string]::IsNullOrWhiteSpace($UserParameter["SourceUsername"])) { $Errors += "SourceUsername is required." } if ($UserParameter.ContainsKey("SourceUseSSHPrivateKey") -and $UserParameter["SourceUseSSHPrivateKey"]) { if (!$UserParameter.ContainsKey("SourcePrivateKey") -or [string]::IsNullOrWhiteSpace($UserParameter["SourcePrivateKey"])) { $Errors += "SourcePrivateKey is required." } if ($UserParameter.ContainsKey("SourcePassphrase") -and ![string]::IsNullOrWhiteSpace($UserParameter["SourcePassphrase"])) { if (!$UserParameter.ContainsKey("SourceConfirmPassphrase") -or [string]::IsNullOrWhiteSpace($UserParameter["SourceConfirmPassphrase"])) { $Errors += "SourceConfirmPassphrase is required" } elseif ($UserParameter["SourcePassphrase"] -ne $UserParameter["SourceConfirmPassphrase"]) { $Errors += "SourcePassphrase and SourceConfirmPassphrase must match" } } } else { if(!$UserParameter.ContainsKey("SourcePassword") -or [string]::IsNullOrWhiteSpace($UserParameter["SourcePassword"])) { $Errors += "SourcePassword is required" } if (!$UserParameter.ContainsKey("SourceConfirmPassword") -or [string]::IsNullOrWhiteSpace($UserParameter["SourceConfirmPassword"])) { $Errors += "SourceConfirmPassword is required" } elseif ($UserParameter["SourcePassword"] -ne $UserParameter["ConfirmPassword"]) { $Errors += "SourcePassword and ConfirmPassword must match" } } } if ($UserParameter.ContainsKey("UpdateTargetInstance") -and $UserParameter["UpdateTargetInstance"]) { if (!$UserParameter.ContainsKey("TargetUsername") -or [string]::IsNullOrWhiteSpace($UserParameter["TargetUsername"])) { $Errors += "TargetUsername is required." } if ($UserParameter.ContainsKey("TargetUseSSHPrivateKey") -and $UserParameter["TargetUseSSHPrivateKey"]) { if (!$UserParameter.ContainsKey("TargetPrivateKey") -or [string]::IsNullOrWhiteSpace($UserParameter["TargetPrivateKey"])) { $Errors += "TargetPrivateKey is required." } if ($UserParameter.ContainsKey("TargetPassphrase") -and ![string]::IsNullOrWhiteSpace($UserParameter["TargetPassphrase"])) { if (!$UserParameter.ContainsKey("TargetConfirmPassphrase") -or [string]::IsNullOrWhiteSpace($UserParameter["TargetConfirmPassphrase"])) { $Errors += "TargetConfirmPassphrase is required" } elseif ($UserParameter["TargetPassphrase"] -ne $UserParameter["TargetConfirmPassphrase"]) { $Errors += "TargetPassphrase and TargetConfirmPassphrase must match" } } } else { if(!$UserParameter.ContainsKey("TargetPassword") -or [string]::IsNullOrWhiteSpace($UserParameter["TargetPassword"])) { $Errors += "TargetPassword is required" } if (!$UserParameter.ContainsKey("TargetConfirmPassword") -or [string]::IsNullOrWhiteSpace($UserParameter["TargetConfirmPassword"])) { $Errors += "TargetConfirmPassword is required" } elseif ($UserParameter["TargetPassword"] -ne $UserParameter["TargetConfirmPassword"]) { $Errors += "TargetPassword and TargetConfirmPassword must match" } } } if ($UserParameter.ContainsKey("TargetIPAddress") -and ![string]::IsNullOrWhiteSpace($UserParameter["TargetIPAddress"])) { $IsValidIPAddress = Confirm-RMIPAddress -IPAddress $UserParameter.TargetIPAddress if (!$IsValidIPAddress) { $Errors += "Invalid Target IP address." } } $CloudType = $Migration.target_cloud_type if ("rivermeadow_standalone" -ne $CloudType -and $UserParameter.ContainsKey("RunContinuous") -and $UserParameter["RunContinuous"]) { $Errors += "'RunContinuous' can be true, when cloud type for the given migration ID is 'rivermeadow_standalone'." } return $Errors, $IsValidMigrationId } function Read-RMFullMigrationId { param ( [bool] $VMBasedSourceRequired ) while ($true) { $Errors = @() $MigrationId = Read-RMUUID -UserMessage "Enter the full migration id" -ParameterName "Full migration ID" -IsRequired $true $Migration = Get-RMMigrationByIdAndStatus -MigrationId $MigrationId $HeartbeatStatus = Test-RMApplianceHeartbeating -CloudAccountId $Migration.cloud_account_id -AccountType $Migration.target_cloud_type if (!$HeartbeatStatus) { $Errors += "Migration appliance is not ready, cannot start differential migration, please make sure that the migration appliance is ready for use and then try again." } $MigrationType = $Migration.migration_type if ("full" -ne $MigrationType) { $Errors += "Migration type for the given migration ID is '$MigrationType', differential migrations can only be started for a full migration, please try again." } $Source, $ErrorString = Confirm-RMSource -SourceId $Migration.source_id if ("" -ne $ErrorString) { $Errors += $ErrorString } if ($VMBasedSourceRequired) { if ($null -ne $Source -and $Source.collection_type -ine "vm") { $IPAddress = $Source.host $Errors += "The source with IP address '$IPAddress' is not a VM based source, please use the cmdlet 'Start-RMOSBasedDifferentialMigration' to start a differential migration for this source." } } $Errors | ForEach-Object { Write-RMError -Message $_ } if ($Errors.count -gt 0) { continue } return $Migration, $Source } } function Confirm-RMVMBasedDMParameterWithMigrationAndSource { param( [hashtable] $UserParameter, [System.Object] $Migration, [System.Object] $Source ) $Errors = @() $Warnings = @() $HeartbeatStatus = Test-RMApplianceHeartbeating -CloudAccountId $Migration.cloud_account_id -AccountType $Migration.target_cloud_type if (!$HeartbeatStatus) { $Errors += "Migration appliance is not ready, cannot start differential migration; please make sure that the migration appliance is ready for use and then try again." } $OrganizationId = Get-Variable -Name "RMContext-CurrentProjectId" -ValueOnly if ((Test-RMSourceHasRunningMigration -OrganizationId $OrganizationId -SourceId $Source.id)) { $Warnings += "Currently there is a migration running for the source of the given migration ID, this differential migration might fail." } $MigrationType = $Migration.migration_type if ("full" -ne $MigrationType) { $Errors += "Migration type for the given migration ID is '$MigrationType', differential migration can only be started for a full migration." } $CloudType = $Migration.target_cloud_type if ("rivermeadow_standalone" -ne $CloudType -and $UserParameter.ContainsKey("RunContinuous") -and $UserParameter["RunContinuous"]) { $Errors += "'RunContinuous' can be true, when cloud type for the given migration ID is 'rivermeadow_standalone'." } if ($null -ne $Source -and $Source.collection_type -ine "vm") { $IPAddress = $Source.host $Errors += "The source with IP address '$IPAddress' is not a VM based source, please use the cmdlet 'Start-RMOSBasedDifferentialMigration' to start a differential migration for this source." } return $Errors, $Warnings } function Get-RMTransferModeAndSchedule { param( [bool] $RunContinuous, [string] $ScheduledAt ) $TransferMode = "run-once" if ($RunContinuous) { $ScheduledAt = "" $TransferMode = "continuous" } elseif ([string]::IsNullOrWhiteSpace($ScheduledAt)) { $ScheduledAt = "" } else { $ScheduledAt = Convert-RMDateTimeToUTC -InputDateTime $ScheduledAt } return $TransferMode, $ScheduledAt } |