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

Import-Module -Name $PSScriptRoot\..\Util\Util
Import-Module -Name $PSScriptRoot\..\Common\Common

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
    )

    $UserLoginStatus = Test-UserLoggedIn
    if (!$UserLoginStatus) {
        return
    }

    if (0 -eq $PSBoundParameters.Count) {
        Add-RMSourceInteractive
    } else {
        Add-RMSourceNonInteractive @PSBoundParameters
    }
}

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

    $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
    } else {
        $MoveGroupList = @()
        $Response = Get-RMMoveGroupList -Organization $CurrentProjectId -PageNumber 0
        $MoveGroupList += $Response
        for ($index = 1; $index -lt $Response.page.totalPages; $index++) {
            $MoveGroupList += Get-RMMoveGroupList -OrganizationId $CurrentProjectId -PageNumber $index
        }
        $MoveGroupName = Read-RMString -UserMessage "Enter the move group name to which the source should be added" `
            -Options $MoveGroupList.content.name -DefaultValue "None" -IsRequired $false -ParameterName "Move group name"
    }
   
    $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
        TargetMigrationDate = $TargetMigrationDate
        AssignedResourceEmail = $AssignedResourceEmail
    }

    New-RMSource @UserInput
}

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
    )
    $Errors = Confirm-RMAddSourceParameter -UserParameter $PSBoundParameters
    $IsValidDate = Confirm-RMDateFormat -InputDate $TargetMigrationDate -DateFormat "MM/dd/yyyy"

    if ($Errors.Count -gt 0) {
        if (!$IsValidDate) {
            Write-RMError -Message "TargetMigrationDate is invalid, the TargetMigrationDate should be in the format 'mm/dd/yyyy'."
        }
        Out-RMUserParameterResult -ErrorMessage $Errors
        return
    }

    if (!$IsValidDate) {
        Write-RMError -Message "TargetMigrationDate is invalid, the TargetMigrationDate should be in the format 'mm/dd/yyyy'."
        return
    }

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

    $AdvancedInstructionsAsHashTable = Get-RMStringArrayAsHashtable -InputItems $AdvancedInstructions
    $UserInput.Add("AdvancedInstructions", $AdvancedInstructionsAsHashTable)

    if ($CreateMoveGroup) {
        $UserInput.Add("CreateMoveGroup", $CreateMoveGroup)
        $UserInput.Add("MoveGroupName", $MoveGroupName)
        $UserInput.Add("TargetMigrationDate", $TargetMigrationDate)
        $UserInput.Add("AssignedResourceEmail", $AssignedResourceEmail)
    } else {
        $UserInput.Add("MoveGroupName", $MoveGroupName)
    }
    

    New-RMSource @UserInput    
}

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

    $MoveGroupID = $null
    if ($CreateMoveGroup) {
        $MoveGroupID = "00000000-0000-0000-0000-000000000000"
    } else {
        if ("" -ne $MoveGroupName) {
            $MoveGroup = Get-MoveGroupByName -MoveGroupName $MoveGroupName -OrganizationId $CurrentProjectId
            if ($null -eq $MoveGroup) {
                throw "Invalid move group."
            }
            $MoveGroupID = $MoveGroup.id
        }
    }
    
    $ControlConnectionType = $null
    if ($SourceHasPreinstalledAgent) {
        $ControlConnectionType = "agent"
    }

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

    try {
        Invoke-RMRestMethod -Params $Params | Out-Null
    } catch {
        # Invoke-RMRestMethod has already shown the error, so just return
        return
    }
    Write-Output "Source has been added successfully" | Out-Host
}

function Confirm-RMAddSourceParameter {
    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."
        }
    }

    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["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"
            }
            if ($UserParameter["Password"] -ne $UserParameter["ConfirmPassword"]) {
                $Errors += "Password and ConfirmPassword must match"
            }
        }
    }

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

    return $Errors
}

Export-ModuleMember -Function Add-RMSource