Migration/nc2/NC2.psm1

using module '../../Common/Result'
using module './RiverMeadow.NC2NetworkConfiguration'

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 NC2MigrationProfile)
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 Common | Join-Path -ChildPath Wrappers | Join-Path -ChildPath Wrappers)
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 Util | Join-Path -ChildPath MigrationUtil)
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 RiverMeadow.Development.Source | Join-Path -ChildPath SourceUtil | Join-Path -ChildPath SourceUtil)

function Start-RMNC2OSBasedNonInteeractiveMigration {
    param(
        [string] $TargetCloud,
        [string] $SourceIP,
        [string] $ScheduledAt,
        [string] $TargetVMName,
        [string[]] $MountPoint,
        [string[]] $ResizeMountPoint,
        [string] $Cluster,
        [string] $StorageContainer,
        [string] $TransferMethod,
        [string] $UpgradeOSVersion,
        [string] $MigrationExtension,
        [string] $MigrationExtensionOSM,
        [int] $Cores,
        [int] $Sockets,
        [int] $RAM,
        [bool] $EnableOSHardening,
        [string] $OSHardeningProfile,
        [bool] $Level1Server,
        [bool] $Level2Server,
        [bool] $Level1Computer,
        [bool] $Level2Computer,
        [bool] $Level1User,
        [bool] $Level2User,
        [string] $AdministratorName,
        [string] $GuestName,
        [string] $LegalNoticeCaption,
        [string] $LegalNoticeText,
        [bool] $ConvertBootModeUEFI,
        [RMNC2DestinationNetworkConfiguration[]] $DestinationNICConfig,
        [bool] $RemoveRMSAgent,
        [bool] $ConnectARCAgent,
        [bool] $InstallAzureARC,
        [string] $NetBIOSName,
        [bool] $IgnoreValidationError,
        [bool] $ShutdownSource,
        [bool] $ShutdownTarget,
        [bool] $Sysprep,
        [bool] $DisableTargetDNSRegistration,
        [string] $AccessMode,
        [string] $VolumeMode,
        [string[]] $MigrationInstruction,
        [bool] $EnableCloudFilesMigration,
        [bool] $InstallAWSSSMAgent,
        [bool] $InstallAWSCloudWatchAgent,
        [bool] $OverrideExistingMigration,
        [string] $ConvertFileSystem
    )

    $CloudAccount = $null
    $CloudAccountErrors = $null
    $Source = $null
    $UserInput = @{}
    $Entitlement = Get-RMEntitlement
    try {
        if (![string]::IsNullOrEmpty($TargetCloud)) {
            $CloudAccount, $ErrorString = Get-RMCloudAccountByName -CloudAccountName $TargetCloud -AccountType "nc2"
            if (![string]::IsNullOrEmpty($ErrorString)) {
                $CloudAccountErrors += $ErrorString
                $IsSourceAndTargetCloudPresent = $false
            } else {
                $TargetInventory = Get-RMTargetInventory -CloudAccount $CloudAccount
            }
        }
    } catch [System.Management.Automation.ItemNotFoundException] {
        $CloudAccountErrors += $PSItem.Exception.Message
        $IsSourceAndTargetCloudPresent = $false
    }

    $ProjectSettingsResponse = Invoke-RMProjectSettings -OrganizationId $CloudAccount.organization_id

    $Errors, $Warnings, $IsSourceAndTargetCloudPresent, $IsRequiredParametersPresentAndValid = Confirm-RMNC2FullMigrationParameter $PSBoundParameters `
        -TargetInventory $TargetInventory -ProjectSettingsResponse $ProjectSettingsResponse

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

    [RMMigrationReturn] $RMMigrationReturn = [RMMigrationReturn]::new()
    if (!$IsSourceAndTargetCloudPresent -or !$IsRequiredParametersPresentAndValid) {
        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 -AccountType "nc2" `
                -RMMigrationReturn $RMMigrationReturn
        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

    if ($null -ne $Source) {
        $SourceErrors = Confirm-RMNC2FullMigrationParameterWithSource -UserParameter $PSBoundParameters `
            -Source $Source -MigrationExtension $MigrationExtensionArray -MigrationExtensionOSM $MigrationExtensionOSMArray

        $Errors +=  $SourceErrors
    }

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

    $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]::IsNullOrEmpty($TargetVMName)) {
        $TargetVMName = $Source.hostname
    }

    $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 (($IgnoreValidationError -and $OverrideExistingMigration) -or `
                (!$IgnoreValidationError -and $OverrideExistingMigration) -or `
                $OverrideExistingMigrationWarning) {
        if ($null -ne $MigrationInstruction -and $MigrationInstruction.Count -gt 0) {
            $MigrationInstruction += ",override_source_migration=true"
        } else {
            $MigrationInstruction += "override_source_migration=true"
        }   
    }

    $MigrationInstructionsAsHashTable = @{}
    if ($null -ne $MigrationInstruction) {
        $MigrationInstructionsAsHashTable = Get-RMStringAsHashtable -InputString $MigrationInstruction
    }

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

    $NetworkInterfaces, $NetworkErrors = Add-RMNetworkConfig -DestinationNICConfig $DestinationNICConfig
    if ($null -ne $NetworkErrors) {
        $Errors += $NetworkErrors
    }

    # Add OS Upgrade functionality
    Add-RMOSUpgrade -UserParameter $PSBoundParameters -UpdatedParameter $UserInput -Source $Source

    # Add Migration Extension functionality
    Add-RMMigrationExtension -UpdatedParameter $UserInput -MigrationExtension $MigrationExtension `
        -MigrationExtensionOSM $MigrationExtensionOSM -MigrationExtensionResponse $MigrationExtensionResponse

    $UserInput.Add("SelectedMount",  $SelectedMountPoint)
    Add-RMConvertFileSystem -Source $Source -UpdatedUserInput $UserInput

    # Add OS Hardening functionality
    $OSHardening = Get-RMOSHardening -OrganizationId $CloudAccount.organization_id 
    $ErrorsOSHardening = Add-RMOSHardening -Source $Source -UpdatedParameter $UserInput -UserParameter $PSBoundParameters -OSHardeningProfile $OSHardening.content
    if ($null -ne $ErrorsOSHardening) {
        $Errors += $ErrorsOSHardening
    }

    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
        Source = $Source
        ScheduledAt = $ScheduledAt
        TransferMethod = $TransferMethod
        TargetVMName = $TargetVMName
        Cores = $Cores
        RAM = $RAM
        Sockets = $Sockets
        StorageContainer = $StorageContainer
        Cluster = $Cluster
        ResizeMountPoint = $MountsResize
        NetworkInterfaces = $NetworkInterfaces
        RemoveRMSAgent = $RemoveRMSAgent
        ShutdownSource = $ShutdownSource
        ShutdownTarget = $ShutdownTarget
        ConnectARCAgent = $ConnectARCAgent
        InstallAzureARC = $InstallAzureARC
        NetBIOSName = $NetBIOSName
        IgnoreValidationError = $IgnoreValidationError
        DisableTargetDNSRegistration = $DisableTargetDNSRegistration
        MigrationInstruction = $MigrationInstructionsAsHashTable
        EnableCloudFilesMigration = $EnableCloudFilesMigration
        InstallAWSSSMAgent = $InstallAWSSSMAgent
        InstallAWSCloudWatchAgent = $InstallAWSCloudWatchAgent
        ConvertBootModeUEFI = $ConvertBootModeUEFI
        GeneralizeSystem = $Sysprep
        ProjectSettingsResponse = $ProjectSettingsResponse
    }

    $HashArguments += $UserInput

    $Response = New-RMNC2MigrationProfile @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-RMNC2VMBasedNonInteractiveMigration {
    param (
        [string] $SourceVMName,
        [string] $SourceVMFolderPath,
        [string] $TargetCloud,
        [string] $ScheduledAt,
        [string] $TargetVMName,
        [string[]] $SelectedDiskLabel,
        [string] $Cluster,
        [string] $StorageContainer,
        [int] $Cores,
        [int] $Sockets,
        [int] $RAM,
        [RMNC2DestinationNetworkConfiguration[]] $DestinationNICConfig,
        [string] $MigrationExtension,
        [bool] $InstallAWSSSMAgent,
        [bool] $InstallAWSCloudWatchAgent,
        [bool] $IgnoreValidationError,
        [bool] $ShutdownSource,
        [bool] $ShutdownTarget,
        [string[]] $MigrationInstruction,
        [bool] $FinalizeMigration,
        [bool] $OverrideExistingMigration 
    )

    $UserInput = @{}
    $CloudAccount = $null
    $CloudAccountErrors = $null
    $Source = $null
    $Errors = @()
    $Entitlement = Get-RMEntitlement
    [RMMigrationReturn] $RMMigrationReturn = [RMMigrationReturn]::new()

    try {
        $TargetInventory = $null
        if (![string]::IsNullOrEmpty($TargetCloud)) {
            $CloudAccount, $ErrorString = Get-RMCloudAccountByName -CloudAccountName $TargetCloud `
                -AccountType "nc2" -VMBasedAppliancesOnly $true
            if (![string]::IsNullOrEmpty($ErrorString)) {
                $Errors += $ErrorString
                Add-RMErrorAndWarning -RMReturnObject $RMMigrationReturn -ErrorMessage $Errors -WarningMessage $Warnings
                Out-RMUserParameterResult -ErrorMessage $Errors -WarningMessage $Warnings
                return $RMMigrationReturn   
            } else {
                $TargetInventory = Get-RMTargetInventory -CloudAccount $CloudAccount
            }
        }
    } catch {
        $CloudAccountErrors += $PSItem.Exception.Message
        $IsSourceAndTargetCloudPresent = $false
    }
    
    $Errors, $Warnings, $IsSourceAndTargetCloudPresent, $IsRequiredParametersPresentAndValid = Confirm-RMNC2VMBasedFullMigrationParameter  $PSBoundParameters `
        -TargetInventory $TargetInventory

    if ($null -ne $CloudAccountErrors) {
        $Errors += $CloudAccountErrors
        $IsSourceAndTargetCloudPresent = $false
    }

    try {
        if (![string]::IsNullOrEmpty($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-RMOpenshiftOSBasedMigration'."
                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 -or !$IsRequiredParametersPresentAndValid) {
        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 -AccountType "nc2" `
                -RMMigrationReturn $RMMigrationReturn
    }
    catch [System.InvalidOperationException], [System.Management.Automation.RuntimeException] {
        $Errors += $PSItem.Exception.Message
    }

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

    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
        }
        $SourceErrors = Confirm-RMNC2VMBasedFullMigrationParameterWithSource  -MigrationExtensionArray $MigrationExtensionArray -Source $Source `
             -UserParameter $PSBoundParameters
       if ($null -ne $SourceErrors) {
            $Errors +=  $SourceErrors
       }
    }

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

    $MigrationInstructionsAsHashTable = $null
    try {
        if (($IgnoreValidationError -and $OverrideExistingMigration) -or (!$IgnoreValidationError -and $OverrideExistingMigration) -or $OverrideExistingMigrationWarning) {
            if ($null -ne $MigrationInstruction) {
                $MigrationInstruction += ",override_source_migration=true"
            } else {
                $MigrationInstruction += "override_source_migration=true"
            }
            
        }
        $MigrationInstructionsAsHashTable = Get-RMStringArrayAsHashtable -InputItems $MigrationInstruction -ParameterName "MigrationInstruction"
    } catch {
        $Errors += $PSItem.Exception.Message
    }

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

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

    $NetworkInterfaces, $NetworkErrors = Add-RMNetworkConfig -DestinationNICConfig $DestinationNICConfig
    if ($null -ne $NetworkErrors) {
        $Errors += $NetworkErrors
    }

    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
        Source = $Source
        SourceVMName = $SourceVMName
        SelectedMount = $SelectedDisk
        SourceVMFolderPath = $SourceVMFolderPath
        TargetCloud = $TargetCloud
        ScheduledAt = $ScheduledAt
        TargetVMName = $TargetVMName
        Cluster = $Cluster
        StorageContainer = $StorageContainer
        Cores = $Cores
        Sockets = $Sockets
        RAM = $RAM
        NetworkInterfaces = $NetworkInterfaces
        InstallAWSSSMAgent = $InstallAWSSSMAgent
        InstallAWSCloudWatchAgent = $InstallAWSCloudWatchAgent
        IgnoreValidationError = $IgnoreValidationError
        ShutdownSource = $ShutdownSource
        ShutdownTarget = $ShutdownTarget
        MigrationInstruction = $MigrationInstructionsAsHashTable
        FinalizeMigration = $FinalizeMigration
        OverrideExistingMigration = $OverrideExistingMigration
    }

    $HashArguments += $UserInput

    $Response = New-RMNutanixNC2VMBasedMigrationProfile @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-RMNC2FullMigrationParameter {
    param (
        [hashtable] $UserParameter,
        [System.Object] $TargetInventory,
        [System.Object] $ProjectSettingsResponse
    )
    
    $Errors, $Warnings, $IsSourceAndTargetCloudPresent = Confirm-RMCommonParameter -UserParameter $UserParameter
    $nc2Errors, $IsRequiredParametersPresentAndValid = Confirm-RMNC2Parameter -UserParameter $UserParameter `
        -TargetInventory $TargetInventory -ProjectSettingsResponse $ProjectSettingsResponse
    $Errors += $nc2Errors
    return $Errors, $Warnings, $IsSourceAndTargetCloudPresent, $IsRequiredParametersPresentAndValid
}

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

    $Errors = @()
    $Errors = Confirm-RMCommonParameterWithSource -UserParameter $UserParameter -Source $Source `
        -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'."
    }

    if ($null -ne $Source.attributes.os.source_migration_state) {
        $MigrationState = $Source.attributes.os.source_migration_state | ConvertFrom-Json
        if ($UserParameter.ContainsKey("ConvertBootModeUEFI") -and $UserParameter["ConvertBootModeUEFI"]) {
            if ("uefi" -ne $MigrationState.boot_mode_conversion_option) {
                $Errors += "'ConvertBootModeUEFI' can not be 'true'"
            } 
        }
    }

    return $Errors
}

function Confirm-RMNC2Parameter {
    param (
        [hashtable] $UserParameter,
        [System.Object] $TargetInventory,
        [System.Object] $ProjectSettingsResponse
    )

    $Errors = @()
    $IsRequiredParametersPresentAndValid = $true

    if (!$UserParameter.ContainsKey("Cluster") -or [string]::IsNullOrWhiteSpace($UserParameter["Cluster"])) {
        $Errors += "Cluster is required."
        $IsRequiredParametersPresentAndValid = $false
    } else {
        $ClusterNames = $TargetInventory.attributes.nc2.clusters.name
        if ($ClusterNames -notcontains $UserParameter["Cluster"]) {
            $Cluster = $UserParameter["Cluster"]
            $Errors += "Cluster '$Cluster' does not exist."
            $IsRequiredParametersPresentAndValid = $false
        }
    }

    if (!$UserParameter.ContainsKey("StorageContainer") -or [string]::IsNullOrWhiteSpace($UserParameter["StorageContainer"])) {
        $Errors += "StorageContainer is required."
        $IsRequiredParametersPresentAndValid = $false
    } else {
        $StorageContainerNames = $TargetInventory.attributes.nc2.clusters.storage_containers.name
        if ($StorageContainerNames -notcontains $UserParameter["StorageContainer"]) {
            $StorageContainer = $UserParameter["StorageContainer"]
            $Errors += "StorageContainer '$StorageContainer' does not exist."
            $IsRequiredParametersPresentAndValid = $false
        }
    }

    # Validate DestinationNICConfig array
    if (!$UserParameter.ContainsKey("DestinationNICConfig") -or $null -eq $UserParameter["DestinationNICConfig"]) {
        $Errors += "DestinationNICConfig is required."
        $IsRequiredParametersPresentAndValid = $false
    } else {
        $AvailableNetworks = $TargetInventory.attributes.nc2.clusters.networks.name
        $DestinationNICConfig = $UserParameter["DestinationNICConfig"]
        
        foreach ($config in $DestinationNICConfig) {
            if ($AvailableNetworks -notcontains $config.DestinationNetworkName) {
                $Errors += "Destination network '$($config.DestinationNetworkName)' does not exist in the target inventory."
                $IsRequiredParametersPresentAndValid = $false
            }
            
            # Check if static assignment has IP address
            if ($config.AssignmentType.ToLower() -eq "static" -and [string]::IsNullOrEmpty($config.IpAddress)) {
                $Errors += "IP Address is required when AssignmentType is 'static' for network '$($config.DestinationNetworkName)'."
                $IsRequiredParametersPresentAndValid = $false
            }
        }
    }

    if (!$UserParameter.ContainsKey("RAM") -or [string]::IsNullOrWhiteSpace($UserParameter["RAM"])) {
        $Errors += "RAM is required."
        $IsRequiredParametersPresentAndValid = $false
    } else {
        $RAM = $UserParameter["RAM"]
        if (!($RAM -match '^\d+$')) {
            $Errors += "RAM must be a positive integer."
            $IsRequiredParametersPresentAndValid = $false
        }
    }

    if (!$UserParameter.ContainsKey("Sockets") -or [string]::IsNullOrWhiteSpace($UserParameter["Sockets"])) {
        $Errors += "Sockets is required."
        $IsRequiredParametersPresentAndValid = $false
    } else {
        $Sockets = $UserParameter["Sockets"]
        if (!($Sockets -match '^\d+$')) {
            $Errors += "Sockets must be a positive integer."
            $IsRequiredParametersPresentAndValid = $false
        }
    }

    if (!$UserParameter.ContainsKey("Cores") -or [string]::IsNullOrWhiteSpace($UserParameter["Cores"])) {
        $Errors += "Cores is required."
        $IsRequiredParametersPresentAndValid = $false
    } else {
        $Cores = $UserParameter["Cores"]
        if (!($Cores -match '^\d+$')) {
            $Errors += "Cores must be a positive integer."
            $IsRequiredParametersPresentAndValid = $false
        }
    }

    return $Errors, $IsRequiredParametersPresentAndValid
}

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

    $Errors, $Warnings, $IsSourcePresent = Confirm-RMVMBasedCommonParameter -UserParameter $UserParameter
    $NC2Errors, $IsRequiredParametersPresentAndValid = Confirm-RMNC2Parameter -UserParameter $UserParameter -TargetInventory $TargetInventory
    $Errors += $NC2Errors
    return $Errors, $Warnings, $IsSourcePresent, $IsRequiredParametersPresentAndValid
}

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

    $Errors = @()
    if (![string]::IsNullOrWhiteSpace($UserParameter["MigrationExtension"])) {
        $MigrationExtension = $UserParameter["MigrationExtension"]
        if ($MigrationExtensionArray -notcontains $MigrationExtension) {
            $Errors += "MigrationExtension '$MigrationExtension' 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
}

Export-ModuleMember -Function Start-RMNC2OSBasedNonInteeractiveMigration, Start-RMNC2VMBasedNonInteractiveMigration