Public/Bootstrap-PowerShellCore.ps1

<#
    .SYNOPSIS
        Use PowerShell to Update PowerShell Core. If you're on Windows, this function can be used to do the initial
        install of PowerShell Core. On any other OS, a version of PowerShell Core (at least 6.0.0-beta) must already
        be installed and used to run this function.
 
    .DESCRIPTION
        See SYNOPSIS
 
    .PARAMETER RemoteOSGuess
        This parameter is OPTIONAL.
         
        This parameter takes a string (either "Windows" or "Linux") that represents the type of platform you anticipate the
        Remote Host is running. The default value for this parameter is "Windows".
 
        IMPORTANT NOTE: If you specify "Linux" and it turns out that the Remote Host is running Windows, this function will fail.
        So, if you're not sure, leave the default value "Windows".
 
    .PARAMETER RemoteHostNameOrIP
        This parameter is MANDATORY.
 
        This parameter takes a string that represents the DNS-resolvable HostName/FQDN or IPv4 Address of the target Remote Host
 
    .PARAMETER LocalUserName
        This parameter is MANDATORY for the Parameter Set 'Local'.
 
        This parameter takes a string that represents the Local User Account on the Remote Host that you are using to ssh into
        the Remote Host. This string must be in format: <RemoteHostName>\<UserName>
 
    .Parameter DomainUserName
        This parameter is MANDATORY for the Parameter Set 'Domain'.
 
        This parameter takes a string that represents the Domain User Account on the Remote Host that you are using to ssh into
        the Remote Host. This string must be in format: <DomainShortName>\<UserName>
 
    .Parameter LocalPasswordSS
        This parameter is OPTIONAL. (However, either -LocalPasswordSS or -KeyFilePath is mandatory for the 'Domain' Parameter Set)
 
        This parameter takes a securestring that represents the password for the -LocalUserName you are using to ssh into the
        Remote Host.
 
    .Parameter DomainPasswordSS
        This parameter is OPTIONAL. (However, either -DomainPasswordSS or -KeyFilePath is mandatory for the 'Domain' Parameter Set)
 
        This parameter takes a securestring that represents the password for the -DomainUserName you are using to ssh into the
        Remote Host.
 
    .PARAMETER KeyFilePath
        This parameter is OPTIONAL. (However, either -DomainPasswordSS, -LocalPasswordSS, or -KeyFilePath is required)
 
        This parameter takes a string that represents the full path to the Key File you are using to ssh into the Remote Host.
        Use this parameter instead of -LocalPasswordSS or -DomainPasswordSS.
 
    .PARAMETER OS
        This parameter is OPTIONAL.
 
        By default, this function probes the Remote Host to determine the OS running on the Remote Host. If you know in advance
        the OS running on the Remote Host, or if the Get-SSHProbe function returns incorrect information, use this parameter
        to specify one of the following values:
            "Ubuntu1404","Ubuntu1604","Ubuntu1804","Ubuntu1810","Debian8","Debian9","CentOS7","RHEL7","OpenSUSE423","Fedora","Raspbian"
 
    .PARAMETER UsePackageManagement
        This parameter is OPTIONAL, however, it has a default value of $True
 
        This parameter is a switch. If used (default behavior), the appropriate Package Management system on the Remote Host
        will be used to install PowerShell Core.
 
        If explicitly set to $False, the appropriate PowerShell Core installation package will be downloaded directly from GitHub
        and installed on the Remote Host.
 
    .PARAMETER ConfigurePSRemoting
        This parameter is OPTIONAL.
 
        This parameter is a switch. If used, in addition to installing PowerShell Core, sshd_config will be modified in order to enable
        PSRemoting using PowerShell Core.
 
    .EXAMPLE
        # Minimal parameters...
 
        $BootstrapPwshSplatParams = @{
            RemoteHostNameOrIP = "zerowin16sshb"
            DomainUserNameSS = "zero\zeroadmin"
            DomainPasswordSS = $(Read-Host -Prompt "Enter password" -AsSecureString)
        }
        Bootstrap-PowerShellCore @BootstrapPwshSplatParams
 
    .EXAMPLE
        # Install pwsh AND configure sshd_config for PSRemoting...
 
        $BootstrapPwshSplatParams = @{
            RemoteHostNameOrIP = "centos7nodomain"
            LocalUserNameSS = "centos7nodomain\vagrant"
            LocalPasswordSS = $(Read-Host -Prompt "Enter password" -AsSecureString)
            ConfigurePSRemoting = $True
        }
        Bootstrap-PowerShellCore @BootstrapPwshSplatParams
 
    .EXAMPLE
        # Instead of using the Remote Host's Package Management System (which is default behavior),
        # download and install the appropriate pwsh package directly from GitHub
 
        $BootstrapPwshSplatParams = @{
            RemoteHostNameOrIP = "centos7nodomain"
            LocalUserNameSS = "centos7nodomain\vagrant"
            LocalPasswordSS = $(Read-Host -Prompt "Enter password" -AsSecureString)
            UsePackageManagement = $False
        }
        Bootstrap-PowerShellCore @BootstrapPwshSplatParams
         
#>

function Bootstrap-PowerShellCore {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [ValidateSet("Windows","Linux")]
        [string]$RemoteOSGuess = "Windows",

        [Parameter(Mandatory=$True)]
        [string]$RemoteHostNameOrIP,

        [Parameter(
            Mandatory=$True,
            ParameterSetName='Local'
        )]
        [ValidatePattern("\\")] # Must be in format <RemoteHostName>\<User>
        [string]$LocalUserName,

        [Parameter(
            Mandatory=$True,
            ParameterSetName='Domain'    
        )]
        [ValidatePattern("\\")] # Must be in format <DomainShortName>\<User>
        [string]$DomainUserName,

        [Parameter(
            Mandatory=$False,
            ParameterSetName='Local'    
        )]
        [securestring]$LocalPasswordSS,

        [Parameter(
            Mandatory=$False,
            ParameterSetName='Domain'
        )]
        [securestring]$DomainPasswordSS,

        [Parameter(Mandatory=$False)]
        [string]$KeyFilePath,

        [Parameter(Mandatory=$False)]
        [ValidateSet("Windows","MacOS","Ubuntu1404","Ubuntu1604","Ubuntu1804","Ubuntu1810","Debian8","Debian9","CentOS7","RHEL7","OpenSUSE423","Fedora","Arch","Raspbian")]
        [string]$OS,

        [Parameter(Mandatory=$False)]
        [switch]$UsePackageManagement = $True,

        [Parameter(Mandatory=$False)]
        [switch]$ConfigurePSRemoting
    )

    #region >> Prep

    if (!$(GetElevation)) {
        Write-Error "Please run PowerShell with elevated privileges and try again. Halting!"
        $global:FunctionResult = "1"
        return
    }

    if (!$(Get-Command ssh -ErrorAction SilentlyContinue)) {
        Write-Error "Unable to find 'ssh'! Please make sure it is installed and part of your Environment/System Path! Halting!"
        $global:FunctionResult = "1"
        return
    }

    if ($KeyFilePath) {
        if (!$(Test-Path $KeyFilePath)) {
            Write-Error "Unable to find KeyFilePath '$KeyFilePath'! Halting!"
            $global:FunctionResult = "1"
            return
        }
    }

    try {
        $RemoteHostNetworkInfo = ResolveHost -HostNameOrIP $RemoteHostNameOrIP -ErrorAction Stop
    }
    catch {
        Write-Error $_
        Write-Error "Unable to resolve '$RemoteHostNameOrIP'! Halting!"
        $global:FunctionResult = "1"
        return
    }

    if ($KeyFilePath  -and !$($LocalPasswordSS -or $DomainPasswordSS)) {
        $WrnMsg = "If $RemoteHostNameOrIP is running Linux, you will be prompted for a sudo password! If you would like to avoid this prompt, " +
        "please run this function again and include either the -LocalPasswordSS or -DomainPasswordSS parameter."
    }

    if ($LocalUserName) {
        if ($($LocalUserName -split "\\")[0] -ne $RemoteHostNetworkInfo.HostName) {
            $ErrMsg = "The HostName indicated by -LocalUserName (i.e. $($($LocalUserName -split "\\")[0]) is not the same as " +
            "the HostName as determined by network resolution (i.e. $($RemoteHostNetworkInfo.HostName))! Halting!"
            Write-Error $ErrMsg
            $global:FunctionResult = "1"
            return
        }
    }
    if ($DomainUserName) {
        if ($($DomainUserName -split "\\")[0] -ne $($RemoteHostNetworkInfo.Domain -split "\.")[0]) {
            $ErrMsg = "The Domain indicated by -DomainUserName (i.e. '$($($DomainUserName -split "\\")[0])') is not the same as " +
            "the Domain as determined by network resolution (i.e. '$($($RemoteHostNetworkInfo.Domain -split "\.")[0])')! Halting!"
            Write-Error $ErrMsg
            $global:FunctionResult = "1"
            return
        }
    }

    # Create PSCustomObjects with all applicable installation info
    Write-Host "Determining latest PowerShell Core Packages..."
    $ReleaseInfo = Invoke-RestMethod https://api.github.com/repos/PowerShell/PowerShell/releases/latest
    $PSCorePackageUrls = $ReleaseInfo.assets.browser_download_url
    $PSCorePackageNames = $ReleaseInfo.assets.name
    Write-Host "Determined latest PowerShell Core Packages."
    <#
        https://github.com/PowerShell/PowerShell/releases/download/v6.1.0/powershell-6.1.0-1.rhel.7.x86_64.rpm
        https://github.com/PowerShell/PowerShell/releases/download/v6.1.0/powershell-6.1.0-linux-arm32.tar.gz
        https://github.com/PowerShell/PowerShell/releases/download/v6.1.0/powershell-6.1.0-linux-musl-x64.tar.gz
        https://github.com/PowerShell/PowerShell/releases/download/v6.1.0/powershell-6.1.0-linux-x64.tar.gz
        https://github.com/PowerShell/PowerShell/releases/download/v6.1.0/powershell-6.1.0-osx-x64.pkg
        https://github.com/PowerShell/PowerShell/releases/download/v6.1.0/powershell-6.1.0-osx-x64.tar.gz
        https://github.com/PowerShell/PowerShell/releases/download/v6.1.0/PowerShell-6.1.0-win-arm32.zip
        https://github.com/PowerShell/PowerShell/releases/download/v6.1.0/PowerShell-6.1.0-win-arm64.zip
        https://github.com/PowerShell/PowerShell/releases/download/v6.1.0/PowerShell-6.1.0-win-x64.msi
        https://github.com/PowerShell/PowerShell/releases/download/v6.1.0/PowerShell-6.1.0-win-x64.zip
        https://github.com/PowerShell/PowerShell/releases/download/v6.1.0/PowerShell-6.1.0-win-x86.msi
        https://github.com/PowerShell/PowerShell/releases/download/v6.1.0/PowerShell-6.1.0-win-x86.zip
        https://github.com/PowerShell/PowerShell/releases/download/v6.1.0/powershell_6.1.0-1.debian.8_amd64.deb
        https://github.com/PowerShell/PowerShell/releases/download/v6.1.0/powershell_6.1.0-1.debian.9_amd64.deb
        https://github.com/PowerShell/PowerShell/releases/download/v6.1.0/powershell_6.1.0-1.ubuntu.14.04_amd64.deb
        https://github.com/PowerShell/PowerShell/releases/download/v6.1.0/powershell_6.1.0-1.ubuntu.16.04_amd64.deb
        https://github.com/PowerShell/PowerShell/releases/download/v6.1.0/powershell_6.1.0-1.ubuntu.18.04_amd64.deb
    #>

    switch ($PSCorePackageUrls) {
        {$_ -match "ubuntu" -and $_ -match "14\.04" -and $_ -match "\.deb"} {
            $Ubuntu1404PackageUrl = $_
            $Ubuntu1404PackageName = $($_ -split '/')[-1]
        }
        {$_ -match "ubuntu" -and $_ -match "16\.04" -and $_ -match "\.deb"} {
            $Ubuntu1604PackageUrl = $ArchPackageUrl = $_
            $Ubuntu1604PackageName = $ArchPackageName = $($_ -split '/')[-1]
        }
        {$_ -match "ubuntu" -and $_ -match "18\.04" -and $_ -match "\.deb"} {
            $Ubuntu1804PackageUrl = $_
            $Ubuntu1804PackageName = $($_ -split '/')[-1]
        }
        {$_ -match "debian\.8" -and $_ -match "\.deb"} {
            $Debian8PackageUrl = $_
            $Debian8PackageName = $($_ -split '/')[-1]
        }
        {$_ -match "debian\.9" -and $_ -match "\.deb"} {
            $Debian9PackageUrl = $_
            $Debian9PackageName = $($_ -split '/')[-1]
        }
        {$_ -match "rhel\.7" -and $_ -match "\.rpm"} {
            $CentOS7PackageUrl = $RHEL7PackageUrl = $OpenSUSE423PackageUrl = $Fedora27PackageUrl = $Fedora28PackageUrl = $_
            $CentOS7PackageName = $RHEL7PackageName = $OpenSUSE423PackageName = $Fedora27PackageName = $Fedora28PackageName = $($_ -split '/')[-1]
        }
        {$_ -match "osx" -and $_ -match "\.pkg"} {
            $MacOSPackageUrl = $_
            $MacOSPackageName = $($_ -split '/')[-1]
        }
        {$_ -match "win" -and $_ -match "x64" -and $_ -match "\.msi"} {
            $Win64PackageUrl = $_
            $Win64PackageName = $($_ -split '/')[-1]
        }
        {$_ -match "win" -and $_ -match "x86" -and $_ -match "\.msi"} {
            $Win32PackageUrl = $_
            $Win32PackageName = $($_ -split '/')[-1]
        }
        {$_ -match "win" -and $_ -match "arm64" -and $_ -match "\.zip"} {
            $WinArm64PackageUrl = $_
            $WinArm64PackageName = $($_ -split '/')[-1]
        }
        {$_ -match "win" -and $_ -match "arm32" -and $_ -match "\.zip"} {
            $WinArm32PackageUrl = $_
            $WinArm32PackageName = $($_ -split '/')[-1]
        }
        {$_ -match "linux" -and $_ -match "x64" -and $_ -match "\.tar\.gz"} {
            $LinuxGenericPackageUrl = $_
            $LinuxGenericPackageName = $($_ -split '/')[-1]
        }
        {$_ -match "linux" -and $_ -match "arm32" -and $_ -match "\.tar\.gz"} {
            $LinuxGenericArmPackageUrl = $RaspbianArmPackageUrl = $_
            $LinuxGenericArmPackageName = $RaspbianArmPackageName = $($_ -split '/')[-1]
        }
    }

    # Windows Install Scripts
    # $Windows is a PSCustomObject containing properties: PackageManagerInstallScript, ManualInstallScript, UninstallScript, ConfigurePwshRemotingScript
    $Windows = GetWindowsScripts -Win64PackageUrl $Win64PackageUrl -Win64PackageName $Win64PackageName
    
    # Ubuntu 14.04 Install Info
    $Ubuntu1404 = GetUbuntu1404Scripts -Ubuntu1404PackageUrl $Ubuntu1404PackageUrl -Ubuntu1404PackageName $Ubuntu1404PackageName

    # Ubuntu 16.04 Install Info
    $Ubuntu1604 = GetUbuntu1604Scripts -Ubuntu1604PackageUrl $Ubuntu1604PackageUrl -Ubuntu1604PackageName $Ubuntu1604PackageName

    # Ubuntu 18.04 Install Info
    $Ubuntu1804 = GetUbuntu1804Scripts -Ubuntu1804PackageUrl $Ubuntu1804PackageUrl -Ubuntu1804PackageName $Ubuntu1804PackageName

    # Debian 8 Install Info
    $Debian8 = GetDebian8Scripts -Debian8PackageUrl $Debian8PackageUrl -Debian8PackageName $Debian8PackageName

    # Debian 9 Install Info
    $Debian9 = GetDebian9Scripts -Debian9PackageUrl $Debian9PackageUrl -Debian9PackageName $Debian9PackageName

    # CentOS 7 and RHEL 7 Install Info
    $CentOS7 = GetCentOS7Scripts -CentOS7PackageUrl $CentOS7PackageUrl -CentOS7PackageName $CentOS7PackageName

    # OpenSUSE 42.3 Install Info
    $OpenSUSE423 = GetOpenSUSE423Scripts -OpenSUSE423PackageUrl $OpenSUSE423PackageUrl -OpenSUSE423PackageName $OpenSUSE423PackageName

    # Fedora Install Info
    $Fedora = GetFedoraScripts -FedoraPackageUrl $Fedora28PackageUrl -FedoraPackageName $Fedora28PackageName

    # Raspbian Install Info
    $Raspbian = GetRaspbianScripts -LinuxGenericArmPackageUrl $LinuxGenericArmPackageUrl -LinuxGenericArmPackageName $LinuxGenericArmPackageName

    # The below Operating Systems (Arch and MacOS) are situations where some operations MUST NOT be performed
    # using sudo and others MUST be performed using sudo.

    # Arch Install Info
    $Arch = GetArchScripts

    # MacOS Install Info
    $MacOS = GetMacOSScripts

    #endregion >> Prep

    #region >> Main Body

    # Probe the Remote Host to get OS and Shell Info
    try {
        Write-Host "Probing $RemoteHostNameOrIP to determine OS and available shell..."

        $GetSSHProbeSplatParams = @{
            RemoteHostNameOrIP  = $RemoteHostNameOrIP
        }
        if ($KeyFilePath) {
            $GetSSHProbeSplatParams.Add("KeyFilePath",$KeyFilePath)
        }
        if ($LocalUserName) {
            $GetSSHProbeSplatParams.Add("LocalUserName",$LocalUserName)
        }
        if ($DomainUserName) {
            $GetSSHProbeSplatParams.Add("DomainUserName",$DomainUserName)
        }
        if ($LocalPasswordSS -and !$KeyFilePath) {
            $GetSSHProbeSplatParams.Add("LocalPasswordSS",$LocalPasswordSS)
        }
        if ($DomainPasswordSS -and !$KeyFilePath) {
            $GetSSHProbeSplatParams.Add("DomainPasswordSS",$DomainPasswordSS)
        }
        if ($RemoteOSGuess) {
            $GetSSHProbeSplatParams.Add("RemoteOSGuess",$RemoteOSGuess)
        }
        
        $OSCheck = Get-SSHProbe @GetSSHProbeSplatParams -ErrorAction Stop
    }
    catch {
        Write-Verbose $_.Exception.Message
        $global:FunctionResult = "1"

        try {
            $null = Stop-AwaitSession
        }
        catch {
            Write-Verbose $_.Exception.Message
        }
    }

    if (!$OSCheck.OS -or !$OSCheck.Shell) {
        try {
            Write-Host "Probing $RemoteHostNameOrIP to determine OS and available shell..."

            $GetSSHProbeSplatParams = @{
                RemoteHostNameOrIP  = $RemoteHostNameOrIP
            }
            if ($KeyFilePath) {
                $GetSSHProbeSplatParams.Add("KeyFilePath",$KeyFilePath)
            }
            if ($LocalUserName) {
                $GetSSHProbeSplatParams.Add("LocalUserName",$LocalUserName)
            }
            if ($DomainUserName) {
                $GetSSHProbeSplatParams.Add("DomainUserName",$DomainUserName)
            }
            if ($LocalPasswordSS -and !$KeyFilePath) {
                $GetSSHProbeSplatParams.Add("LocalPasswordSS",$LocalPasswordSS)
            }
            if ($DomainPasswordSS -and !$KeyFilePath) {
                $GetSSHProbeSplatParams.Add("DomainPasswordSS",$DomainPasswordSS)
            }
            if ($RemoteOSGuess) {
                $GetSSHProbeSplatParams.Add("RemoteOSGuess",$RemoteOSGuess)
            }
            
            $OSCheck = Get-SSHProbe @GetSSHProbeSplatParams -ErrorAction Stop
        }
        catch {
            Write-Error $_
            $global:FunctionResult = "1"
    
            try {
                $null = Stop-AwaitSession
            }
            catch {
                Write-Verbose $_.Exception.Message
            }
    
            return
        }
    }

    if (!$OSCheck.OS -or !$OSCheck.Shell) {
        Write-Error "The Get-SSHProbe function was unable to identify $RemoteHostNameOrIP's platform or default shell! Please check your ssh connection/credentials. Halting!"
        $global:FunctionResult = "1"
        return
    }

    Write-Host "OS: $OSCheck.OS"
    Write-Host "Shell: $OSCheck.Shell"
    
    if ($OSCheck.OS -eq "Linux") {
        # Check to make sure the user has sudo privileges
        try {
            $GetSudoStatusSplatParams = @{
                RemoteHostNameOrIP  = $RemoteHostNameOrIP
            }
            if ($KeyFilePath) {
                $GetSudoStatusSplatParams.Add("KeyFilePath",$KeyFilePath)
            }
            if ($LocalPasswordSS) {
                $GetSudoStatusSplatParams.Add("LocalPasswordSS",$LocalPasswordSS)
            }
            if ($DomainPasswordSS) {
                $GetSudoStatusSplatParams.Add("DomainPasswordSS",$DomainPasswordSS)
            }
            if ($LocalUserName) {
                $GetSudoStatusSplatParams.Add("LocalUserName",$LocalUserName)
            }
            if ($DomainUserName) {
                $GetSudoStatusSplatParams.Add("DomainUserName",$DomainUserName)
            }
            
            $GetSudoStatusResult = Get-SudoStatus @GetSudoStatusSplatParams
        }
        catch {
            Write-Error $_
            $global:FunctionResult = "1"
            return
        }
        
        if (!$GetSudoStatusResult.HasSudoPrivileges) {
            Write-Error "The user does not appear to have sudo privileges on $RemoteHostNameOrIP! Halting!"
            $global:FunctionResult = "1"
            return
        }

        # If the user has sudo privileges but there's a password prompt, but -LocalPasswordSS and -DomainPasswordSS
        # parameters were not used, we need to halt
        if ($GetSudoStatusResult.PasswordPrompt) {
            if (!$LocalPasswordSS -and !$DomainPasswordSS) {
                Write-Error "The user will be prompted for a sudo password, but neither the -LocalPasswordSS nor -DomainPasswordSS parameter was provided! Halting!"
                $global:FunctionResult = "1"
                return
            }
        }
    }

    Write-Host "Get-SSHProbe identified OS: $($OSCheck.OS); Shell: $($OSCheck.Shell)"

    # It's possible that the OSVersionInfo property is an array of strings, but we don't want the below switch to loop through each one,
    # so we have to make sure we only give the switch one string object (i.e. $SanitizedOSVersionInfo)
    $SanitizedOSVersionInfo = $($OSCheck.OSVersionInfo | foreach {$_ -split "`n"}) -join "`n"
    switch ($SanitizedOSVersionInfo) {
        {$($_ -match 'Microsoft|Windows' -and ![bool]$($_ -match "Linux")) -or $OSCheck.OS -eq "Windows"} {
            $OSDetermination = "Windows"
            $WindowsVersion = $OSCheck.OSVersionInfo
        }

        {$_ -match 'Darwin'} {
            $OSDetermination = "MacOS"
            $MacOSVersion = $OSCheck.OSVersionInfo
        }

        {$_ -match "Ubuntu 18\.04|18\.04\.[0-9]+-Ubuntu" -or $_ -match "Ubuntu.*1804|Ubuntu.*18\.04|1804.*Ubuntu|18\.04.*Ubuntu"} {
            $OSDetermination = "Ubuntu1804"
            $UbuntuVersion = "18.04"
        }

        {$_ -match "Ubuntu 16.04|16.04.[0-9]+-Ubuntu" -or $_ -match "Ubuntu.*1604|Ubuntu.*16\.04|1604.*Ubuntu|16\.04.*Ubuntu"} {
            $OSDetermination = "Ubuntu1604"
            $UbuntuVersion = "16.04"
        }

        {$_ -match "Ubuntu 14.04|14.04.[0-9]+-Ubuntu" -or $_ -match "Ubuntu.*1404|Ubuntu.*14\.04|1404.*Ubuntu|14\.04.*Ubuntu"} {
            $OSDetermination = "Ubuntu1404"
            $UbuntuVersion = "14.04"
        }

        {$_ -match 'Debian GNU/Linux 8|\+deb8' -or $_ -match "jessie"} {
            $OSDetermination = "Debian8"
            $DebianVersion = "8"
        }

        {$_ -match 'Debian GNU/Linux 9|\+deb9' -or $_ -match "stretch"} {
            $OSDetermination = "Debian9"
            $DebianVersion = "9"
        }

        {$_ -match 'CentOS|\.el[0-9]\.'} {
            $OSDetermination = "CentOS7"
            $CentOSVersion = "7"
        }

        {$_ -match 'RedHat'} {
            $OSDetermination = "RHEL7"
            $RHELVersion = "7"
        }

        {$_ -match 'openSUSE|leap.*42\.3|Leap 42\.3|openSUSE Leap'} {
            $OSDetermination = "OpenSUSE423"
            $OpenSUSEVersion = "42.3"
        }

        {$_ -match 'Arch Linux|arch[0-9]|-ARCH'} {
            $OSDetermination = "Arch"
            $OSVersionInfoLines = $_ -split "`n"
            $KernelVersion = $($OSVersionInfoLines -match "Kernel: ") -split " " -split "-" -match "[0-9]+\.[0-9]+\.[0-9]+"
            $ArchReleaseInfo = Invoke-RestMethod -Uri "https://www.archlinux.org/releng/releases/json"
            $ArchVersionPrep = $($ArchReleaseInfo.releases | Where-Object {$_.kernel_version -eq $KernelVersion}).version
            if ($ArchVersionPrep) {
                $ArchVersion = @($ArchVersionPrep)[0]
            }
            else {
                $ArchVersion = @(
                    $ArchReleaseInfo.releases | Where-Object {
                        $_.kernel_version -match $('^' + $($($KernelVersion -split "\.")[0..1] -join '\.'))
                    }
                )[0]
            }
        }

        {$_ -match 'Fedora 28|fedora:28'} {
            $OSDetermination = "Fedora"
            $FedoraVersion = "28"
        }

        {$_ -match 'Fedora 27|fedora:27'} {
            $OSDetermination = "Fedora"
            $FedoraVersion = "27"
        }

        {$_ -match 'armv.*GNU'} {
            $OSDetermination = "Raspbian"
            $RaspbianVersion = "stretch"
        }
    }

    if (!$OSDetermination) {
        Write-Error "Unable to determine OS Version Information for $RemoteHostNameOrIP! Halting!"
        $global:FunctionResult = "1"
        return
    }

    if ($OS) {
        if ($OS -ne $OSDetermination) {
            Write-Error "The Get-SSHProbe function reports that $RemoteHostNameOrIP is running $OSDetermination, however, the user explicitly specified -OS as $OS! Halting!"
            $global:FunctionResult = "1"
            return
        }
    }
    else {
        $OS = $OSDetermination
    }

    Write-Host "`$OS is: $OS"

    if ($LocalPasswordSS) {
        $LocalPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($LocalPasswordSS))
    }
    If ($DomainPasswordSS) {
        $DomainPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($DomainPasswordSS))
    }

    $TargetOSScripts = Get-Variable -Name $OS -ValueOnly

    $SSHScriptBuilderSplatParams = @{
        RemoteHostNameOrIP      = $RemoteHostNameOrIP
    }
    if ($LocalUserName) {
        $null = $SSHScriptBuilderSplatParams.Add('LocalUserName',$LocalUserName)
    }
    if ($DomainUserName) {
        $null = $SSHScriptBuilderSplatParams.Add('DomainUserName',$DomainUserName)
    }
    if ($LocalPassword) {
        $null = $SSHScriptBuilderSplatParams.Add('LocalPassword',$LocalPassword)
    }
    if ($DomainPassword) {
        $null = $SSHScriptBuilderSplatParams.Add('DomainPassword',$DomainPassword)
    }
    if ($KeyFilePath) {
        $null = $SSHScriptBuilderSplatParams.Add('KeyFilePath',$KeyFilePath)
    }

    $OnWindows = !$PSVersionTable.Platform -or $PSVersionTable.Platform -eq "Win32NT"

    if ($OSCheck.OS -eq "Windows") {
        $null = $SSHScriptBuilderSplatParams.Add('WindowsTarget',$True)

        if ($UsePackageManagement) {
            if ($OnWindows) {
                $null = $SSHScriptBuilderSplatParams.Add('WindowsWaitTimeMin',3)
            }
            else {
                #$null = $TargetOSScripts.PackageManagerInstallScript.Add('echo powershellInstallComplete')
            }

            if ($ConfigurePSRemoting) {
                $SSHScriptArray = $TargetOSScripts.ConfigurePwshRemotingScript

                $null = $SSHScriptBuilderSplatParams.Add('SSHScriptArray',$SSHScriptArray)
                $null = $SSHScriptBuilderSplatParams.Add('ScriptCompleteFlag','powershellInstallComplete|pwshConfigComplete')
            }
            else {
                $SSHScriptArray = $TargetOSScripts.PackageManagerInstallScript

                $null = $SSHScriptBuilderSplatParams.Add('SSHScriptArray',$SSHScriptArray)
                $null = $SSHScriptBuilderSplatParams.Add('ScriptCompleteFlag','powershellInstallComplete')
            }
        }
        else {
            if ($OnWindows) {
                $null = $SSHScriptBuilderSplatParams.Add('WindowsWaitTimeMin',3)
            }
            else {
                #$null = $TargetOSScripts.ManualInstallScript.Add('echo powershellInstallComplete')
            }

            if ($ConfigurePSRemoting) {
                $SSHScriptArray = $TargetOSScripts.ConfigurePwshRemotingScript

                $null = $SSHScriptBuilderSplatParams.Add('SSHScriptArray',$SSHScriptArray)
                $null = $SSHScriptBuilderSplatParams.Add('ScriptCompleteFlag','powershellInstallComplete|pwshConfigComplete')
            }
            else {
                $SSHScriptArray = $TargetOSScripts.ManualInstallScript

                $null = $SSHScriptBuilderSplatParams.Add('SSHScriptArray',$SSHScriptArray)
                $null = $SSHScriptBuilderSplatParams.Add('ScriptCompleteFlag','powershellInstallComplete')
            }
        }
    }
    if ($OSCheck.OS -eq "Linux" -and $OS -ne "Arch" -and $OS -ne "MacOS") {
        if ($UsePackageManagement) {
            if ($OnWindows) {
                $null = $TargetOSScripts.PackageManagerInstallScript.Insert($($TargetOSScripts.PackageManagerInstallScript.Count-1),'echo powershellInstallComplete')
                $null = $SSHScriptBuilderSplatParams.Add('WindowsWaitTimeMin',1)
            }
            else {
                $null = $TargetOSScripts.PackageManagerInstallScript.Add('echo powershellInstallComplete')
            }

            if ($ConfigurePSRemoting) {
                $null = $TargetOSScripts.ConfigurePwshRemotingScript.Add('echo pwshConfigComplete')

                $SSHScriptArray = $TargetOSScripts.PackageManagerInstallScript + $TargetOSScripts.ConfigurePwshRemotingScript
                
                $null = $SSHScriptBuilderSplatParams.Add('ElevatedSSHScriptArray',$SSHScriptArray)
                $null = $SSHScriptBuilderSplatParams.Add('ScriptCompleteFlag','powershellInstallComplete|pwshConfigComplete')
            }
            else {
                $SSHScriptArray = $TargetOSScripts.PackageManagerInstallScript
                
                $null = $SSHScriptBuilderSplatParams.Add('ElevatedSSHScriptArray',$SSHScriptArray)
                $null = $SSHScriptBuilderSplatParams.Add('ScriptCompleteFlag','powershellInstallComplete')
            }
        }
        else {
            if ($OnWindows) {
                $null = $TargetOSScripts.ManualInstallScript.Insert($($TargetOSScripts.ManualInstallScript.Count-1),'echo powershellInstallComplete')
                $null = $SSHScriptBuilderSplatParams.Add('WindowsWaitTimeMin',1)
            }
            else {
                $null = $TargetOSScripts.ManualInstallScript.Add('echo powershellInstallComplete')
            }

            if ($ConfigurePSRemoting) {
                $null = $TargetOSScripts.ConfigurePwshRemotingScript.Add('echo pwshConfigComplete')

                $SSHScriptArray = $TargetOSScripts.ManualInstallScript + $TargetOSScripts.ConfigurePwshRemotingScript

                $null = $SSHScriptBuilderSplatParams.Add('ElevatedSSHScriptArray',$SSHScriptArray)
                $null = $SSHScriptBuilderSplatParams.Add('ScriptCompleteFlag','powershellInstallComplete|pwshConfigComplete')
            }
            else {
                $SSHScriptArray = $TargetOSScripts.ManualInstallScript

                $null = $SSHScriptBuilderSplatParams.Add('ElevatedSSHScriptArray',$SSHScriptArray)
                $null = $SSHScriptBuilderSplatParams.Add('ScriptCompleteFlag','powershellInstallComplete')
            }
        }
    }
    if ($OS -eq "Arch" -or $OS -eq "MacOS") {
        if ($UsePackageManagement) {
            if ($OnWindows) {
                $null = $TargetOSScripts.PackageManagerInstallScript.Insert($($TargetOSScripts.PackageManagerInstallScript.Count-1),'echo powershellInstallComplete')
                if ($OS -eq "MacOS") {
                    $null = $SSHScriptBuilderSplatParams.Add('WindowsWaitTimeMin',12)
                }
                else {
                    $null = $SSHScriptBuilderSplatParams.Add('WindowsWaitTimeMin',3)
                }
                if ($OS -eq "Arch") {
                    $null = $SSHScriptBuilderSplatParams.Add('PwdPromptDelaySeconds',180)
                }
            }
            else {
                $null = $TargetOSScripts.PackageManagerInstallScript.Add('echo powershellInstallComplete')
            }

            if ($ConfigurePSRemoting) {
                $null = $TargetOSScripts.ConfigurePwshRemotingScript.Add('echo pwshConfigComplete')
                
                $null = $SSHScriptBuilderSplatParams.Add('SSHScriptArray',$TargetOSScripts.PackageManagerInstallScript)
                $null = $SSHScriptBuilderSplatParams.Add('ElevatedSSHScriptArray',$TargetOSScripts.ConfigurePwshRemotingScript)
                $null = $SSHScriptBuilderSplatParams.Add('ScriptCompleteFlag','powershellInstallComplete|pwshConfigComplete')
            }
            else {
                $null = $SSHScriptBuilderSplatParams.Add('SSHScriptArray',$TargetOSScripts.PackageManagerInstallScript)
                $null = $SSHScriptBuilderSplatParams.Add('ScriptCompleteFlag','powershellInstallComplete')
            }
        }
        else {
            if ($OnWindows) {
                $null = $TargetOSScripts.ManualInstallScript.Insert($($TargetOSScripts.ManualInstallScript.Count-1),'echo powershellInstallComplete')
                if ($OS -eq "MacOS") {
                    $null = $SSHScriptBuilderSplatParams.Add('WindowsWaitTimeMin',12)
                }
                else {
                    $null = $SSHScriptBuilderSplatParams.Add('WindowsWaitTimeMin',3)
                }
                if ($OS -eq "Arch") {
                    $null = $SSHScriptBuilderSplatParams.Add('PwdPromptDelaySeconds',180)
                }
            }
            else {
                $null = $TargetOSScripts.ManualInstallScript.Add('echo powershellInstallComplete')
            }

            if ($ConfigurePSRemoting) {
                $null = $TargetOSScripts.ConfigurePwshRemotingScript.Add('echo pwshConfigComplete')

                $null = $SSHScriptBuilderSplatParams.Add('SSHScriptArray',$TargetOSScripts.ManualInstallScript)
                $null = $SSHScriptBuilderSplatParams.Add('ElevatedSSHScriptArray',$TargetOSScripts.ConfigurePwshRemotingScript)
                $null = $SSHScriptBuilderSplatParams.Add('ScriptCompleteFlag','powershellInstallComplete|pwshConfigComplete')
            }
            else {
                $null = $SSHScriptBuilderSplatParams.Add('SSHScriptArray',$TargetOSScripts.ManualInstallScript)
                $null = $SSHScriptBuilderSplatParams.Add('ScriptCompleteFlag','powershellInstallComplete')
            }
        }
    }

    $FinalOutput = SSHScriptBuilder @SSHScriptBuilderSplatParams

    $FinalOutput
    
    #endregion >> Main Body
}