RiverMeadow.Development.Source/RiverMeadow.Development.Source.psm1

using module '../Common/Result'
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath Util | Join-Path -ChildPath Util)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath Common | Join-Path -ChildPath Common)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath Common | Join-Path -ChildPath Wrappers | Join-Path -ChildPath Wrappers)
Import-Module -Name @(Join-Path $PSScriptRoot SourceUtil| Join-Path -ChildPath SourceUtil)


function Add-RMSource {
    param(
        [Alias("sip")]
        [string] $SourceIP,

        [Alias("shpa")]
        [bool] $SourceHasPreinstalledAgent,

        [Alias("scoa")]
        [bool] $StoreCredsOnAppliance,

        [Alias("un")]
        [string] $Username,

        [Alias("pw")]
        [string] $Password,

        [Alias("cpw")]
        [string] $ConfirmPassword,

        [Alias('usshpk')]
        [bool] $UseSSHPrivateKey,

        [Alias("pk")]
        [string] $PrivateKey,

        [Alias("pp")]
        [string] $Passphrase,

        [Alias("cpp")]
        [string] $ConfirmPassphrase,

        [Alias("dm")]
        [string] $Domain,

        [Alias("rmt")]
        [string[]] $RiverMeadowTags,
        
        [Alias("ai")]
        [string[]] $AdvancedInstructions,

        [Alias("cmg")]
        [bool] $CreateMoveGroup,

        [Alias("mgn")]
        [string] $MoveGroupName,

        [Alias("tmd")]
        [string] $TargetMigrationDate,

        [Alias("are")]
        [string] $AssignedResourceEmail
    )
    try {
        $LoginStatus = Test-UserLoggedIn
        if ($LoginStatus.ReturnCode -eq [RMReturn]::ERROR) {
            return $LoginStatus
        }
    
        if (0 -eq $PSBoundParameters.Count) {
            return Add-RMSourceInteractive
        }
        return Add-RMSourceNonInteractive @PSBoundParameters
    } catch {
        Show-RMError -ErrorObj $PSItem
        return Build-RMError -ErrorObj $PSItem
    }
}

function Edit-RMSource {
    param(
        [Alias("sip")]
        [string] $SourceIP,

        [Alias("usip")]
        [string] $UpdateSourceIP,

        [Alias("ost")]
        [string] $OSType,

        [Alias("ec")]
        [bool] $EditCredentials,

        [Alias("shpa")]
        [bool] $SourceHasPreinstalledAgent,

        [Alias("scoa")]
        [bool] $StoreCredsOnAppliance,

        [Alias("un")]
        [string] $Username,

        [Alias("pw")]
        [string] $Password,

        [Alias("cpw")]
        [string] $ConfirmPassword,

        [Alias('usshpk')]
        [bool] $UseSSHPrivateKey,

        [Alias("pk")]
        [string] $PrivateKey,

        [Alias("pp")]
        [string] $Passphrase,

        [Alias("cpp")]
        [string] $ConfirmPassphrase,

        [Alias("dm")]
        [string] $Domain,

        [Alias("rmt")]
        [string[]] $RiverMeadowTags,
        
        [Alias("ai")]
        [string[]] $AdvancedInstructions,

        [Alias("cmg")]
        [bool] $CreateMoveGroup,

        [Alias("mgn")]
        [string] $MoveGroupName,

        [Alias("tmd")]
        [string] $TargetMigrationDate,

        [Alias("are")]
        [string] $AssignedResourceEmail
    )
    try {
        $LoginStatus = Test-UserLoggedIn
        if ($LoginStatus.ReturnCode -eq [RMReturn]::ERROR) {
            return $LoginStatus
        }
    
        if (0 -eq $PSBoundParameters.Count) {
            return Edit-RMSourceInteractive
        }
        return Edit-RMSourceNonInteractive  @PSBoundParameters
    } catch {
        Show-RMError -ErrorObj $PSItem
        return Build-RMError -ErrorObj $PSItem
    }
}


function Add-RMSourceInteractive {
    $CurrentProjectId = Get-Variable -Name "RMContext-CurrentProjectId" -ValueOnly

    [RMReturn] $RMReturn = [RMReturn]::new()
    $SourceIP = Read-RMIPAddress -UserMessage "Enter the source IP address" -ParameterName "Source IP address" -IsRequired $true    
    $SourceHasPreinstalledAgent = Read-RMBoolean -UserMessage "Does source has preinstalled agent" -DefaultValue "false" 

    if(!$SourceHasPreinstalledAgent){
        $StoreCredsOnAppliance = Read-RMBoolean -UserMessage "Store the source credentials on migration appliance" -DefaultValue "false"
    }

    if (!$SourceHasPreinstalledAgent -and !$StoreCredsOnAppliance) {
        $Username = Read-RMString -UserMessage "Enter the username" -ParameterName "Username" -IsRequired $true
        $UseSSHPrivateKey = Read-RMBoolean -UserMessage "Use SSH private key" -DefaultValue "false"

        if ($UseSSHPrivateKey) {
            $PrivateKey = Read-RMString -UserMessage "Enter the private key" -ParameterName "Private key" -IsRequired $true
            $Password = Read-RMSecureString -UserMessage "Enter the passphrase" -ParameterName "Passphrase" `
                -ConfirmMessage "Confirm the passphrase" -ConfirmParameterName "Confirm passphrase" -IsRequired $false
        } else {
            $Password = Read-RMSecureString -UserMessage "Enter the password" -ParameterName "Password" `
                -ConfirmMessage "Confirm the password" -ConfirmParameterName "Confirm password" -IsRequired $true
        }
        $Domain = Read-RMString -UserMessage "Enter the domain name" -ParameterName "Domain name" -IsRequired $false
    }

    $RiverMeadowTags = Read-RMToken -UserMessage "Enter one or more RiverMeadow tags, separated by commas" -DefaultValue "None" `
        -ParameterName "RiverMeadow tags" -Separator "," -IsRequired $false

    $ReadValue = Read-RMPair -UserMessage "Enter one or more advanced instructions in the format 'key=value' and separated by commas" `
        -DefaultValue "None" -Separator "="
    $AdvancedInstructions = Get-RMStringAsHashtable -InputString $ReadValue

    $CreateMoveGroup = Read-RMBoolean -UserMessage "Create new move group" -DefaultValue "false"
    if ($CreateMoveGroup) {
        $DateFormat = "MM/dd/yyyy"
        $UserMessage = "Enter target migration date in the format '{0}'" -f $DateFormat

        $MoveGroupName = Read-RMString -UserMessage "Enter new move group name" -ParameterName "Move group name" -IsRequired $true
        $TargetMigrationDate = Read-RMDate -UserMessage $UserMessage -DateFormat $DateFormat `
            -ParameterName "Target migration date" -DefaultValue "None" -IsRequired $false
        $AssignedResourceEmail = Read-RMString -UserMessage "Enter assigned resource email" -DefaultValue "None" `
            -ParameterName "Assigned resource email" -IsRequired $false
        $MoveGroupId = "00000000-0000-0000-0000-000000000000"
    } else {
        $MoveGroupList = Get-RMMoveGroups -OrganizationId $CurrentProjectId
        $MoveGroups = $MoveGroupList.content.name
        $MoveGroupName = Read-RMString -UserMessage "Enter the move group name to which the source should be added" -Options $MoveGroups -ParameterName "Move Group Name"`
            -DefaultValue 'None' -IsRequired $false
        if ("" -ne $MoveGroupName) {
            $MoveGroup = Get-MoveGroup -MoveGroupName $MoveGroupName -MoveGroupList $MoveGroupList.content
            $MoveGroupId = $MoveGroup.id
        }
    }
   
    $UserInput = @{
        SourceIP = $SourceIP
        CurrentProjectId = $CurrentProjectId
        SourceHasPreinstalledAgent = $SourceHasPreinstalledAgent
        StoreCredsOnAppliance = $StoreCredsOnAppliance
        Username = $Username
        Password = $Password
        PrivateKey = $PrivateKey
        Domain = $Domain
        RiverMeadowTags = $RiverMeadowTags
        AdvancedInstructions = $AdvancedInstructions
        CreateMoveGroup = $CreateMoveGroup
        MoveGroupName = $MoveGroupName
        MoveGroupId = $MoveGroupId
        TargetMigrationDate = $TargetMigrationDate
        AssignedResourceEmail = $AssignedResourceEmail
    }

    $Response = New-RMSource @UserInput
    Write-Output "Source has been added successfully" | Out-Host
    $RMReturn.SetReturnCode([RMReturn]::SUCCESS)
    return $RMReturn
}

function Add-RMSourceNonInteractive {
    param (
        [string] $SourceIP,
        [bool] $SourceHasPreinstalledAgent,
        [bool] $StoreCredsOnAppliance,
        [string] $Username,
        [string] $Password,
        [string] $ConfirmPassword,
        [bool] $UseSSHPrivateKey,
        [string] $PrivateKey,
        [string] $Passphrase,
        [string] $ConfirmPassphrase,
        [string] $Domain,
        [string[]] $RiverMeadowTags,
        [string[]] $AdvancedInstructions,
        [bool] $CreateMoveGroup,
        [string] $MoveGroupName,
        [string] $TargetMigrationDate,
        [string] $AssignedResourceEmail
    )

    [RMReturn] $RMReturn = [RMReturn]::new()
    [array]$Errors = Confirm-RMAddSourceParameter -UserParameter $PSBoundParameters
    $AdvancedInstructionsAsHashTable = $null
    try {
        $AdvancedInstructionsAsHashTable = Get-RMStringArrayAsHashtable -InputItems $AdvancedInstructions -ParameterName "AdvancedInstructions"
    } catch {
        $Errors += $PSItem.Exception.Message
    }

    if ($Errors.Count -gt 0) {
        Add-RMErrorAndWarning -RMReturnObject $RMReturn -ErrorMessage $Errors
        Out-RMUserParameterResult -ErrorMessage $Errors
        return $RMReturn
    }

    $UserInput = @{}
    $CurrentProjectId = Get-Variable -Name "RMContext-CurrentProjectId" -ValueOnly
    $UserInput.Add("CurrentProjectId", $CurrentProjectId)
    $UserInput.Add("SourceIP", $SourceIP)

    if ($SourceHasPreinstalledAgent) {
        $UserInput.Add("SourceHasPreinstalledAgent", $SourceHasPreinstalledAgent)
    } else {
        if ($StoreCredsOnAppliance) {
            $UserInput.Add("StoreCredsOnAppliance", $StoreCredsOnAppliance)
        }
    }

    if (!$SourceHasPreinstalledAgent -and !$StoreCredsOnAppliance) {
        $UserInput.Add("Username", $Username)
        if ($UseSSHPrivateKey) {
            $UserInput.Add("PrivateKey", $PrivateKey)
            $UserInput.Add("Password", $Passphrase)
        } else {
            $UserInput.Add("Password", $Password)
        }
        $UserInput.Add("Domain", $Domain)
    }

    if ($null -eq $RiverMeadowTags) {
        $RiverMeadowTags = @()
    }
    $UserInput.Add("RiverMeadowTags", $RiverMeadowTags)

    $UserInput.Add("AdvancedInstructions", $AdvancedInstructionsAsHashTable)

    if ($CreateMoveGroup) {
        $MoveGroupId = "00000000-0000-0000-0000-000000000000"
        $UserInput.Add("CreateMoveGroup", $CreateMoveGroup)
        $UserInput.Add("MoveGroupName", $MoveGroupName)
        $UserInput.Add("TargetMigrationDate", $TargetMigrationDate)
        $UserInput.Add("AssignedResourceEmail", $AssignedResourceEmail)
        $UserInput.Add("MoveGroupId", $MoveGroupId)
    } else {
        if (![string]::IsNullOrEmpty($MoveGroupName)) {
            $MoveGroup = Get-Variable -Name "RMContext-MoveGroup" -ValueOnly
            $UserInput.Add("MoveGroupName", $MoveGroupName)
            $UserInput.Add("MoveGroupId", $MoveGroup.id)
        }
    }

    $Response = New-RMSource @UserInput
    Write-Output "Source has been added successfully" | Out-Host
    $RMReturn.SetReturnCode([RMReturn]::SUCCESS)
    return $RMReturn
}

function Edit-RMSourceInteractive {
    $OrganizationId = Get-Variable -Name "RMContext-CurrentProjectId" -ValueOnly

    $Source = Read-RMSource -UserMessage "Enter the source IP" -ParameterName "Source IP" -IsRequired $true

    $UpdateSourceIP = Read-RMIPAddress -UserMessage "Enter the update source IP" -DefaultValue $SourceIP -ParameterName "Source IP"`
         -IsRequired $false
    
    if ($SourceIP -ne $UpdateSourceIP) {
        $SourceIP = $UpdateSourceIP
    }

    $OSType = Read-RMString -UserMessage "Enter the OS Type" -Options 'linux','windows'  -DefaultValue $Source.os_type`
        -ParameterName "OS Type" -IsRequired $false

    $EditCredentials = Read-RMBoolean -UserMessage "Edit Credentials" -DefaultValue $false
    if ($EditCredentials) {
        $SourceHasPreinstalledAgentDefaultValue = $false
        $ControlConnectionType = $Source.control_connection_type
        if (![string]::IsNullOrEmpty($ControlConnectionType) -and 'agent' -eq $ControlConnectionType) {
            $SourceHasPreinstalledAgentDefaultValue = $true
        }
        $SourceHasPreinstalledAgent = Read-RMBoolean -UserMessage "Does source has preinstalled agent" -DefaultValue $SourceHasPreinstalledAgentDefaultValue

        $CredentialsStorage = $Source.credentials.storage
        if (!$SourceHasPreinstalledAgent) {
            if (![string]::IsNullOrEmpty($CredentialsStorage) -and 'ca' -eq $CredentialsStorage) {
                $StoreCredsOnAppliance = $true
            }
            $StoreCredsOnAppliance = Read-RMBoolean -UserMessage "Store the source credentials on migration appliance" -DefaultValue $StoreCredsOnAppliance
        }

        if ($SourceHasPreinstalledAgent) {
            $ControlConnectionType = "agent"
        } else {
            $ControlConnectionType = "None"
        }

        if ($StoreCredsOnAppliance) {
            $CredsStorage = 'ca'
        } else {
            $CredsStorage = 'local'
        }

        if (!$SourceHasPreinstalledAgent -and !$StoreCredsOnAppliance) {
            $IsRequired = $true
            $DefaultUserName = $null
            if ("" -ne $Source.Credentials.username) {
                $DefaultUserName = $Source.Credentials.username
                $IsRequired = $false
            }
            
            $UserName = Read-RMString -UserMessage "Enter the username" -DefaultValue $DefaultUserName -ParameterName "Username" -IsRequired $IsRequired

            $UseSSHPrivateKey = Read-RMBoolean -UserMessage "Use SSH private key" -DefaultValue $Source.credentials.privateKeyIsSet

            if ($UseSSHPrivateKey ) {
                $PrivateKey = Read-RMString -UserMessage "Enter the private key" -ParameterName "Private key" -IsRequired $true

                $Password = Read-RMSecureString -UserMessage "Enter the passphrase" -ParameterName "Passphrase" `
                -ConfirmMessage "Confirm the passphrase" -ConfirmParameterName "Confirm passphrase" -IsRequired $false
            } else {
                 $Password = Read-RMSecureString -UserMessage "Enter the password" -ParameterName "Password" `
                -ConfirmMessage "Confirm the password" -ConfirmParameterName "Confirm password" -IsRequired $true
            }

            $DomainDefaultValue = 'None'
            if (![string]::IsNullOrEmpty($Source.credentials.domain)) {
                $DomainDefaultValue = $Source.credentials.domain
            }
            $Domain = Read-RMString -UserMessage "Enter the domain" -ParameterName "Domain" -DefaultValue $DomainDefaultValue -IsRequired $false
        }
    }

    $TagsDefaultValue = 'None'
    if (![string]::IsNullOrEmpty($Source.tags)) {
        $TagsDefaultValue = $Source.tags -join ", "
    }
    $RiverMeadowTags = Read-RMToken -UserMessage "Enter one or more RiverMeadow tags, separated by commas" -Separator ","`
        -DefaultValue $TagsDefaultValue -ParameterName "RiverMeadow Tags" -IsRequired $false

    $AdvancedInstructionsDefaultValue = 'None'
    if (![string]::IsNullOrEmpty($Source.instructions) -and "{}" -ne $Source.instructions) {
        $AdvancedInstructionsDefaultValue = $Source.instructions.Replace('{' , '').Replace('}' , '').Replace(':' , '=').Replace('"', '')
    }
    $AdvancedInstructionsAsString = Read-RMPair -UserMessage "Enter one or more advanced instructions in the format 'key=value' and separated by commas"`
        -Separator "=" -DefaultValue $AdvancedInstructionsDefaultValue

    $AdvancedInstructions = Get-RMStringAsHashtable -InputString $AdvancedInstructionsAsString
    $CreateMoveGroup = Read-RMBoolean -UserMessage "Create new move group" -DefaultValue $false

    if ($CreateMoveGroup) {
        $DateFormat = "MM/dd/yyyy"
        $UserMessage = "Enter target migration date in the format '{0}'" -f $DateFormat

        $MoveGroupName = Read-RMString -UserMessage "Enter new move group name" -ParameterName "Move group name" -IsRequired $true
        $TargetMigrationDate = Read-RMDate -UserMessage $UserMessage -DateFormat $DateFormat `
            -ParameterName "Target migration date" -DefaultValue "None" -IsRequired $false
        $AssignedResourceEmail = Read-RMString -UserMessage "Enter assigned resource email" -DefaultValue "None" `
            -ParameterName "Assigned resource email" -IsRequired $false
        $MoveGroupId = "00000000-0000-0000-0000-000000000000"
    } else {
        $MoveGroupList = Get-RMMoveGroups -OrganizationId $OrganizationId
        $MoveGroups = $MoveGroupList.content.name
        $MoveGroupDefaultValue = 'None'
        if (![string]::IsNullOrEmpty($Source.move_group.name)) {
            $MoveGroupDefaultValue = $Source.move_group.name
        }
        $MoveGroupName = Read-RMString -UserMessage "Enter the move group name to which the source should be added" -Options $MoveGroups -ParameterName "Move Group Name"`
             -DefaultValue $MoveGroupDefaultValue -IsRequired $false
        if ("" -ne $MoveGroupName) {
            $MoveGroup = Get-MoveGroup -MoveGroupName $MoveGroupName -MoveGroupList $MoveGroupList.content
            $MoveGroupId = $MoveGroup.id
        }
    }

    $UserInput = @{
        SourceId = $Source.id
        SourceIP = $SourceIP
        ControlConnectionType = $ControlConnectionType
        CredsStorage = $CredsStorage
        Domain = $Domain
        Password = $Password
        PrivateKey = $PrivateKey
        Storage = $Storage
        UserName = $UserName
        AdvancedInstructions = $AdvancedInstructions
        OrganizationId = $OrganizationId
        OSType = $OSType
        CreateMoveGroup = $CreateMoveGroup
        MoveGroupName = $MoveGroupName
        MoveGroupId = $MoveGroupId
        TargetMigrationDate = $TargetMigrationDate
        AssignedResourceEmail = $AssignedResourceEmail
        RiverMeadowTags = $RiverMeadowTags
    }

    $Response = Update-RMSource @UserInput
    Write-Output "Source has been updated successfully" | Out-Host
    [RMReturn] $RMReturn = [RMReturn]::new()
    $RMReturn.SetReturnCode([RMReturn]::SUCCESS)
    return $RMReturn
}

function Edit-RMSourceNonInteractive {
    param (
        [string] $SourceIP,
        [string] $UpdateSourceIP,
        [string] $OSType,
        [bool] $EditCredentials,
        [bool] $SourceHasPreinstalledAgent,
        [bool] $StoreCredsOnAppliance,
        [string] $Username,
        [string] $Password,
        [string] $ConfirmPassword,
        [bool] $UseSSHPrivateKey,
        [string] $PrivateKey,
        [string] $Passphrase,
        [string] $ConfirmPassphrase,
        [string] $Domain,
        [string[]] $RiverMeadowTags,
        [string[]] $AdvancedInstructions,
        [bool] $CreateMoveGroup,
        [string] $MoveGroupName,
        [string] $TargetMigrationDate,
        [string] $AssignedResourceEmail
    )
    [RMReturn] $RMReturn = [RMReturn]::new()
    $Errors = Confirm-RMEditSourceParameter -UserParameter $PSBoundParameters
    try {
        $Source = Get-RMSourceByIP -IPAddress $SourceIP
    } catch {
        if (![string]::IsNullOrEmpty($SourceIP)) {
            $ProjectName = Get-Variable -Name "RMContext-CurrentProjectName" -ValueOnly
            $OrgName = Get-Variable -Name "RMContext-CurrentOrganizationName" -ValueOnly
            $Errors += "Source with IP address '$SourceIP' does not exist in project '$ProjectName' of organization '$OrgName'."
        }
    }

    $AdvancedInstructionsAsHashTable = $null
    try {
        $AdvancedInstructionsAsHashTable = Get-RMStringArrayAsHashtable -InputItems $AdvancedInstructions -ParameterName "AdvancedInstructions"
    } catch {
        [array] $AdvancedInstructionsErrors = $PSItem.Exception.Message
        $Errors += $AdvancedInstructionsErrors
    }
    
    if ($Errors.Count -gt 0) {
        Add-RMErrorAndWarning -RMReturnObject $RMReturn -ErrorMessage $Errors
        Out-RMUserParameterResult -ErrorMessage $Errors
        return $RMReturn
    }
    

    $UserInput = @{}
    $OrganizationId = Get-Variable -Name "RMContext-CurrentProjectId" -ValueOnly

    $UserInput.Add("SourceId", $Source.id)

    if (![string]::IsNullOrEmpty($UpdateSourceIP)) {
        $SourceIP = $UpdateSourceIP
    }
   
    $UserInput.Add("SourceIP", $SourceIP)
    $UserInput.Add("OrganizationId", $OrganizationId)
    if ([string]::IsNullOrEmpty($OSType)) {
        $OSType = $Source.os_type
    }
    $UserInput.Add("OSType", $OSType)

    if ($EditCredentials) {
        if ($SourceHasPreinstalledAgent) {
            $ControlConnectionType = "agent"
        } else {
            if ($StoreCredsOnAppliance) {
                $CredsStorage = 'ca'
            } else {
                $CredsStorage = 'local'
            }
            $ControlConnectionType = 'None'
        }

        $UserInput.Add("ControlConnectionType", $ControlConnectionType)
        $UserInput.Add("CredsStorage", $CredsStorage)
    
        if (!$SourceHasPreinstalledAgent -and !$StoreCredsOnAppliance) {
            $UserInput.Add("Username", $Username)
            if ($UseSSHPrivateKey) {
                $UserInput.Add("PrivateKey", $PrivateKey)
                $UserInput.Add("Password", $Passphrase)
            } else {
                $UserInput.Add("Password", $Password)
            }
            $UserInput.Add("Domain", $Domain)
        }
    }

    if ($null -eq $RiverMeadowTags) {
        $RiverMeadowTags = @()
    }
    $UserInput.Add("RiverMeadowTags", $RiverMeadowTags)

    $UserInput.Add("AdvancedInstructions", $AdvancedInstructionsAsHashTable)
    
    if ($CreateMoveGroup) {
        $MoveGroupId = "00000000-0000-0000-0000-000000000000"
        $UserInput.Add("CreateMoveGroup", $CreateMoveGroup)
        $UserInput.Add("MoveGroupName", $MoveGroupName)
        $UserInput.Add("TargetMigrationDate", $TargetMigrationDate)
        $UserInput.Add("AssignedResourceEmail", $AssignedResourceEmail)
        $UserInput.Add("MoveGroupId", $MoveGroupId)
    } else {
        if (![string]::IsNullOrEmpty($MoveGroupName)) {
            $MoveGroup = Get-Variable -Name "RMContext-MoveGroup" -ValueOnly
            $UserInput.Add("MoveGroupName", $MoveGroupName)
            $UserInput.Add("MoveGroupId", $MoveGroup.id)
        }
    }

    $Response = Update-RMSource @UserInput
    Write-Output "Source has been updated successfully" | Out-Host
    $RMReturn.SetReturnCode([RMReturn]::SUCCESS)
    return $RMReturn

}

function New-RMSource {
    param(
        [string] $SourceIP,
        [string] $CurrentProjectId,
        [bool] $SourceHasPreinstalledAgent,
        [bool] $StoreCredsOnAppliance,
        [string] $Username,
        [string] $Password,
        [string] $PrivateKey,
        [string] $Domain,
        [string[]] $RiverMeadowTags,
        [hashtable] $AdvancedInstructions,
        [bool] $CreateMoveGroup,
        [string] $MoveGroupName,
        [string] $TargetMigrationDate,
        [string] $AssignedResourceEmail
    )

    $CredsStorage = "local"
    if ($StoreCredsOnAppliance) {
        $CredsStorage = "ca"
    }
    
    $ControlConnectionType = $null
    if ($SourceHasPreinstalledAgent) {
        $ControlConnectionType = "agent"
    }

    if ([string]::IsNullOrEmpty($RiverMeadowTags)) {
        $RiverMeadowTags = @()
    }

    $SourceRequest = @{
        "name"= $SourceIP
        "host"= $SourceIP
        #e.g string format:"Azure:Standard_D5_v2"
        #TODO We will add cloud size features in the future
        "cloud_sizing"= ""
        "instructions"= $AdvancedInstructions
        "credentials"= @{
            "storage" = $CredsStorage
            "username" = $Username
            "password" = $Password
            "domain" = $Domain
            "private_key" = $PrivateKey
        }
        "move_group_id" = $MoveGroupID
        "move_group_name" = $MoveGroupName
        "target_migration_date" = $TargetMigrationDate
        "assigned_resource_email" = $AssignedResourceEmail
        "tags"= $RiverMeadowTags
        "organization_id" = $CurrentProjectId
        "control_connection_type" = $ControlConnectionType
    }
    $SourceRequestJson = $SourceRequest |ConvertTo-Json -Depth 100


    $Uri = Get-Variable -Name "RMContext-ReactorURI" -ValueOnly
    $RMLoginResult = Get-Variable -Name "RMContext-UserLogin" -ValueOnly

    $Headers = @{
        Accept = "application/rm+json"
        "X-Auth-Token" = $RMLoginResult.token
    }

    $Params = @{
        Method = "Post"
        Uri = $Uri + "/sources"
        Body = $SourceRequestJson
        ContentType = "application/json"
        Headers = $Headers
    }

    Invoke-RMRestMethod -Params $Params
}

function Update-RMSource {
    param (
        [string] $SourceId,
        [string] $SourceIP,
        [System.Object] $ControlConnectionType,
        [System.Object] $CredsStorage,
        [string] $Domain,
        [string] $Password,
        [string] $PrivateKey,
        [string] $Storage,
        [string] $UserName,
        [hashtable] $AdvancedInstructions,
        [string] $MoveGroupId,
        [string] $OrganizationId,
        [string] $OSType,
        [bool] $CreateMoveGroup,
        [string] $MoveGroupName,
        [string] $TargetMigrationDate,
        [string] $AssignedResourceEmail,
        [string[]] $RiverMeadowTags
    )

    if ("None" -eq $ControlConnectionType) {
        $ControlConnectionType =  ""
    }

    if ([string]::IsNullOrEmpty($RiverMeadowTags)) {
        $RiverMeadowTags = @()
    }

    $SourceRequest = @{
        "name" = $SourceIP
        "host" = $SourceIP
        #TODO We will add cloud size features in the future
        "cloud_sizing" = ""
        "instructions" = $AdvancedInstructions
        "credentials" = @{
            "domain" = $Domain
            "password" = $Password
            "private_key" = $PrivateKey
            "storage" = $CredsStorage
            "username" = $UserName
        }
        "move_group_id" = $MoveGroupId
        "move_group_name" = $MoveGroupName
        "target_migration_date" = $TargetMigrationDate
        "assigned_resource_email" = $AssignedResourceEmail
        "organization_id" = $OrganizationId
        "os_type" = $OSType
        "tags" = $RiverMeadowTags
        "control_connection_type" = $ControlConnectionType

    }

    $SourceRequestJson = $SourceRequest |ConvertTo-Json -Depth 100

    $Uri = Get-Variable -Name "RMContext-ReactorURI" -ValueOnly
    $RMLoginResult = Get-Variable -Name "RMContext-UserLogin" -ValueOnly

    $Headers = @{
        Accept = "application/rm+json"
        "X-Auth-Token" = $RMLoginResult.token
    }

    $Params = @{
        Method = "Put"
        Uri = $Uri + "/sources/$SourceId"
        Body = $SourceRequestJson
        ContentType = "application/json"
        Headers = $Headers
    }

    Invoke-RMRestMethod -Params $Params
}
function Confirm-RMSourceIP {
    param(
        [hashtable] $UserParameter
    )

    $Errors = @()
    if (!$UserParameter.ContainsKey("SourceIP") -or [string]::IsNullOrEmpty("SourceIP")) {
        $Errors += "SourceIP is required"
    } else {
        $IsValidSourceIP = Confirm-RMIPAddress -IPAddress $UserParameter.SourceIP
        if (!$IsValidSourceIP) {
           $Errors += "Invalid Source IP Address."
        }
    }
    return $Errors
}

function Confirm-RMAddSourceParameter {
    param(
        [hashtable] $UserParameter
    )
    $Errors = @()

    $IsValidDate = Confirm-RMDateFormat -InputDate $TargetMigrationDate -DateFormat "MM/dd/yyyy"
    if (!$IsValidDate) {
        $Errors += "TargetMigrationDate is invalid, the TargetMigrationDate should be in the format 'MM/dd/yyyy'."
    }


    $SourceIPErrors = Confirm-RMSourceIP -UserParameter $UserParameter
    if ($SourceIPErrors -gt 0) {
        $Errors += $SourceIPErrors
    }
    
    $SourceErrors = Confirm-RMSourceParameter -UserParameter $UserParameter
    if ($SourceErrors -gt 0) {
        $Errors += $SourceErrors
    }
    
    $SourceMoveGroupErrors = Confirm-RMMoveGroup -UserParameter $UserParameter
    if ($SourceMoveGroupErrors -gt 0) {
        $Errors += $SourceMoveGroupErrors
    }

    $IsValidDate = Confirm-RMDateFormat -InputDate $UserParameter["TargetMigrationDate"] -DateFormat "MM/dd/yyyy"
    if (!$IsValidDate) {
        $Errors += "TargetMigrationDate is invalid, the TargetMigrationDate should be in the format 'MM/dd/yyyy'."
    }

    return $Errors
}

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

    $Errors = @()
    
    $SourceIPErrors = Confirm-RMSourceIP -UserParameter $UserParameter
    if ($SourceIPErrors -gt 0) {
        $Errors += $SourceIPErrors
    }

    if ($UserParameter.ContainsKey("UpdateSourceIP") -and ![string]::IsNullOrEmpty("UpdateSourceIP")) {
        $IsValidSourceIP = Confirm-RMIPAddress -IPAddress $UserParameter.UpdateSourceIP
        if (!$IsValidSourceIP) {
           $Errors += "Invalid Update Source IP Address."
        }
    }

    if ($UserParameter.ContainsKey("EditCredentials") -and $UserParameter["EditCredentials"]) {
        $SourceErrors = Confirm-RMSourceParameter -UserParameter $UserParameter
        if ($SourceErrors -gt 0) {
            $Errors += $SourceErrors
        }
    }
   
    $SourceMoveGroupErrors = Confirm-RMMoveGroup -UserParameter $UserParameter
    if ($SourceMoveGroupErrors -gt 0) {
        $Errors += $SourceMoveGroupErrors
    }

    $IsValidDate = Confirm-RMDateFormat -InputDate $TargetMigrationDate -DateFormat "MM/dd/yyyy"
    if (!$IsValidDate) {
        $Errors +="TargetMigrationDate is invalid, the TargetMigrationDate should be in the format 'MM/dd/yyyy'."
    }

    return $Errors
}

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

    $Errors = @()
    if (!(($UserParameter.ContainsKey("SourceHasPreinstalledAgent") -and $UserParameter["SourceHasPreinstalledAgent"]) `
            -or ($UserParameter.ContainsKey("StoreCredsOnAppliance") -and $UserParameter["StoreCredsOnAppliance"]))) {
        if (!$UserParameter.ContainsKey("Username") -or [string]::IsNullOrEmpty($UserParameter["Username"])) {
            $Errors += "Username is required"
        }
        if ($UserParameter.ContainsKey("UseSSHPrivateKey") -and $UserParameter["UseSSHPrivateKey"] -eq $true) {
            if (!$UserParameter.ContainsKey("PrivateKey") -or [string]::IsNullOrEmpty($UserParameter["PrivateKey"])) {
                $Errors += "PrivateKey is required, when 'UseSSHPrivateKey' is true"
            }
            if ($UserParameter.ContainsKey("Passphrase") -and ![string]::IsNullOrEmpty($UserParameter["Passphrase"])) {
                if (!$UserParameter.ContainsKey("ConfirmPassphrase") -or [string]::IsNullOrEmpty($UserParameter["ConfirmPassphrase"])) {
                    $Errors += "ConfirmPassphrase is required"
                } elseif ($UserParameter["Passphrase"] -ne $UserParameter["ConfirmPassphrase"]) {
                    $Errors += "Passphrase and ConfirmPassphrase must match"
                }
            }
        } else {
            if(!$UserParameter.ContainsKey("Password") -or [string]::IsNullOrEmpty($UserParameter["Password"])) {
                $Errors += "Password is required"
            }
            if (!$UserParameter.ContainsKey("ConfirmPassword") -or [string]::IsNullOrEmpty($UserParameter["ConfirmPassword"])) {
                $Errors += "ConfirmPassword is required"
            } elseif ($UserParameter["Password"] -ne $UserParameter["ConfirmPassword"]) {
                $Errors += "Password and ConfirmPassword must match"
            }
        }
    }

    return $Errors
}

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

    $Errors = @()
    if ($UserParameter.ContainsKey("CreateMoveGroup") -and $UserParameter["CreateMoveGroup"] -eq $true) {
        if (!$UserParameter.ContainsKey("MoveGroupName") -or [string]::IsNullOrEmpty($UserParameter["MoveGroupName"])) {
            $Errors += "MoveGroupName is required, when 'CreateMoveGroup' is true"
        }
    } else {
        if ($UserParameter.ContainsKey("MoveGroupName") -and ![string]::IsNullOrEmpty($UserParameter["MoveGroupName"])) {
            $OrganizationId = Get-Variable -Name "RMContext-CurrentProjectId" -ValueOnly
            $MoveGroupName = $UserParameter.MoveGroupName
            $MoveGroup = Get-MoveGroupByName -MoveGroupName $MoveGroupName -OrganizationId $OrganizationId
            if ($null -eq $MoveGroup) {
                $ProjectName = Get-Variable -Name "RMContext-CurrentProjectName" -ValueOnly
                $Errors += "Move group '$MoveGroupName' does not exist in project '$ProjectName'. "
            }
        }
    }
    
    return $Errors
}

function Get-RMMoveGroups {
    param(
        [string] $OrganizationId
    )

    $MoveGroupList = @()
    $Response = Get-RMMoveGroupList -Organization $OrganizationId -PageNumber 0
    $MoveGroupList += $Response
    for ($index = 1; $index -lt $Response.page.totalPages; $index++) {
        $MoveGroupList += Get-RMMoveGroupList -OrganizationId $CurrentProjectId -PageNumber $index
    }

    return  $MoveGroupList
}


Export-ModuleMember -Function Add-RMSource, Edit-RMSource