Public/Start_Stop/Start-NewPSRemoteSession.ps1

function Start-NewPSRemoteSession {
    <#
    .SYNOPSIS
        Create a remote PSSession, preferring PowerShell 7 endpoint (WSMan) with
        fallback to Windows PowerShell. Also supports SSH transport.
 
    .DESCRIPTION
        - For WSMan: tries -ConfigurationName PowerShell.7 first, then falls
          back to Microsoft.PowerShell.
        - For SSH: uses New-PSSession -HostName (PowerShell 7+ locally).
          Supports password or key.
        - Applies sensible timeouts; returns a live PSSession or throws.
 
    .PARAMETER ComputerName
        Target computer (DNS name or IP).
 
    .PARAMETER Credential
        PSCredential for WSMan or SSH (username/password). For SSH+Key, Username
        can come from the credential or -UserName.
 
    .PARAMETER UseSsh
        Use SSH transport instead of WSMan.
 
    .PARAMETER UseCredSSP
        Enables CredSSP for WSMan remoting and bootstraps the remote host's
        WSMan CredSSP server setting before opening the delegated session.
 
    .PARAMETER Port
        SSH port (default 22).
 
    .PARAMETER Ps7ConfigName
        WSMan endpoint name for PS7 (default 'PowerShell.7').
 
    .PARAMETER WinPsConfigName
        WSMan endpoint name for Windows PowerShell (default
        'Microsoft.PowerShell').
 
    .PARAMETER UserName
        SSH username when not using PSCredential.
 
    .PARAMETER KeyFilePath
        SSH private key path (if using key-based auth).
 
    .PARAMETER ConnectTimeoutSec
        Open/operation timeout (seconds) used in session options.
 
    .PARAMETER IdleTimeoutSec
        Idle timeout (milliseconds) for the session.
 
    .PARAMETER AsList
        When set, returns a list-style summary object for display instead of the
        raw PSSession object.
 
    .OUTPUTS
        System.Management.Automation.Runspaces.PSSession
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$ComputerName,
        [pscredential]$Credential,
        [string]$SessionName,

        [switch]$UseSsh,
        [switch]$UseCredSSP,

        [int]$Port = 22,

        [string]$Ps7ConfigName = 'PowerShell.7',
        [string]$WinPsConfigName = 'Microsoft.PowerShell',

        [string]$UserName,
        [string]$KeyFilePath,

        [int]$ConnectTimeoutSec = 20,
        [int]$IdleTimeoutSec = 1800000,  # 30 minutes

        [switch]$AsList
    )

    Set-StrictMode -Version Latest
    $oldEAP = $ErrorActionPreference
    $ErrorActionPreference = 'Stop'
    try {
        $sessOpts = New-PSSessionOption -OpenTimeout ($ConnectTimeoutSec * 1000) `
            -OperationTimeout ($ConnectTimeoutSec * 1000) `
            -IdleTimeout $IdleTimeoutSec

        function Test-LocalAdministrator {
            $id = [Security.Principal.WindowsIdentity]::GetCurrent()
            $principal = [Security.Principal.WindowsPrincipal]$id
            return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
        }

        function Get-SessionOutputObject {
            param(
                [Parameter(Mandatory)]
                [System.Management.Automation.Runspaces.PSSession]$Session,
                [switch]$AsList
            )

            if (-not $AsList) {
                return $Session
            }

            [pscustomobject]@{
                Id                = $Session.Id
                Name              = $Session.Name
                Transport         = $Session.Transport
                ComputerName      = $Session.ComputerName
                ComputerType      = $Session.ComputerType
                State             = $Session.State
                ConfigurationName = $Session.ConfigurationName
                Availability      = $Session.Availability
                RunspaceId        = $Session.Runspace.InstanceId
                PSSession         = $Session
            }
        }

        function Test-CredSSPClientDelegationConfigured {
            param([Parameter(Mandatory)][string]$TargetComputer)

            # First check the local WSMan CredSSP client auth toggle. This read is
            # available in normal sessions and avoids false negatives when
            # Get-WSManCredSSP itself requires elevation.
            try {
                $authEnabled = (Get-Item -Path WSMan:\localhost\Client\Auth\CredSSP -ErrorAction Stop).Value
            }
            catch {
                return $false
            }

            if ([string]$authEnabled -ne 'true') {
                return $false
            }

            try {
                $status = Get-WSManCredSSP
            }
            catch {
                # In non-elevated sessions this cmdlet can throw AccessDenied.
                # If CredSSP client auth is on, treat configuration as present.
                return $true
            }

            if (-not $status) {
                return $true
            }

            $patterns = @()
            foreach ($line in @($status -split "`r?`n")) {
                $trimmed = $line.Trim()
                if ([string]::IsNullOrWhiteSpace($trimmed)) { continue }

                $matches = [regex]::Matches($trimmed, '(?i)wsman/[^\s,;]+')
                foreach ($match in $matches) {
                    if ($match.Success) {
                        $patterns += $match.Value.ToLowerInvariant()
                    }
                }
            }

            $patterns = @($patterns | Select-Object -Unique)

            if ($patterns.Count -eq 0) {
                return $false
            }

            $shortComputer = ($TargetComputer -split '\.')[0]
            $targetsToMatch = @(
                ("wsman/{0}" -f $TargetComputer),
                ("wsman/{0}" -f $shortComputer),
                $TargetComputer,
                $shortComputer
            )

            foreach ($pattern in $patterns) {
                foreach ($candidate in $targetsToMatch) {
                    if ($candidate -like $pattern) {
                        return $true
                    }
                }
            }

            return $false
        }

        function Get-CredSSPDelegateTargets {
            param([Parameter(Mandatory)][string]$TargetComputer)

            $configuredTargets = @()
            $remoting = $null

            if ($script:cfg -and $script:cfg.settings) {
                $remoting = $script:cfg.settings.remoting
            }

            if ($remoting -and $remoting.ContainsKey('credSSPDelegateComputers')) {
                $rawTargets = $remoting['credSSPDelegateComputers']

                if ($rawTargets -is [string]) {
                    $configuredTargets = @($rawTargets)
                }
                elseif ($rawTargets -is [System.Collections.IEnumerable]) {
                    $configuredTargets = @($rawTargets)
                }
            }

            $normalizedTargets = @(
                $configuredTargets |
                Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_) } |
                ForEach-Object {
                    $value = [string]$_
                    if ($value -like 'wsman/*') {
                        $value.Substring(6)
                    }
                    else {
                        $value
                    }
                } |
                Select-Object -Unique
            )

            if ($normalizedTargets.Count -gt 0) {
                return $normalizedTargets
            }

            return @($TargetComputer)
        }

        function Enable-RemoteCredSSPServer {
            param(
                [Parameter(Mandatory)][string]$TargetComputer,
                [pscredential]$TargetCredential,
                [Parameter(Mandatory)][System.Management.Automation.Remoting.PSSessionOption]$SessionOptions,
                [Parameter(Mandatory)][string]$PrimaryConfig,
                [Parameter(Mandatory)][string]$FallbackConfig,
                [Parameter(Mandatory)][string]$TargetSessionName
            )

            Write-Log -Level Info -Message "Ensuring CredSSP is enabled on $TargetComputer for WSMan remoting."

            $delegateTargets = Get-CredSSPDelegateTargets -TargetComputer $TargetComputer
            $clientConfigured = Test-CredSSPClientDelegationConfigured -TargetComputer $TargetComputer
            if (-not $clientConfigured) {
                if (-not (Test-LocalAdministrator)) {
                    $delegateArg = ($delegateTargets | ForEach-Object { "'{0}'" -f $_ }) -join ', '
                    throw "CredSSP client delegation is not configured locally for $TargetComputer and enabling it requires an elevated PowerShell session. Run once as administrator: Enable-WSManCredSSP -Role Client -DelegateComputer $delegateArg -Force"
                }

                Enable-WSManCredSSP -Role Client -DelegateComputer $delegateTargets -Force | Out-Null
            }

            $bootstrap = $null
            try {
                $bootstrapParams = @{
                    ComputerName      = $TargetComputer
                    Credential        = $TargetCredential
                    Authentication    = 'Default'
                    ConfigurationName = $PrimaryConfig
                    ErrorAction       = 'Stop'
                    SessionOption     = $SessionOptions
                    Name              = "$TargetSessionName:bootstrap"
                }

                try {
                    $bootstrap = New-PSSession @bootstrapParams
                }
                catch {
                    $bootstrapParams.ConfigurationName = $FallbackConfig
                    $bootstrap = New-PSSession @bootstrapParams
                }

                $serverEnabled = Invoke-Command -Session $bootstrap -ScriptBlock {
                    $value = (Get-Item -Path WSMan:\localhost\Service\Auth\CredSSP -ErrorAction Stop).Value
                    return [string]$value -eq 'true'
                } -ErrorAction Stop

                if (-not $serverEnabled) {
                    Invoke-Command -Session $bootstrap -ScriptBlock {
                        Enable-WSManCredSSP -Role Server -Force | Out-Null
                    } -ErrorAction Stop | Out-Null
                }
            }
            finally {
                if ($bootstrap) {
                    Remove-PSSession -Session $bootstrap -ErrorAction SilentlyContinue
                }
            }
        }

        if (-not $SessionName) {
            $SessionName = "TT:Remote:{0}:{1:yyyyMMdd-HHmmss}" -f $ComputerName, (Get-Date)
        }

        if ($UseSsh) {

            # --- Preflight: must be PowerShell 7+ (or 6+) locally for SSH remoting parameter set ---
            if ($PSVersionTable.PSVersion.Major -lt 7) {
                throw "SSH transport requires PowerShell 7+ locally."
            } # PowerShell remoting over SSH is supported in PowerShell 6+ / 7+. [1](https://learn.microsoft.com/en-us/powershell/scripting/security/remoting/ssh-remoting-in-powershell?view=powershell-7.6)[3](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_remote_requirements?view=powershell-7.5)

            # --- Preflight: ensure OpenSSH client is present (ssh.exe) ---
            if (-not (Get-Command ssh -ErrorAction SilentlyContinue)) {
                throw "SSH transport requires OpenSSH client (ssh.exe) available on PATH."
            } # SSH transport depends on SSH being installed. [1](https://learn.microsoft.com/en-us/powershell/scripting/security/remoting/ssh-remoting-in-powershell?view=powershell-7.6)

            # --- Resolve username ---
            $resolvedUser = $null
            if ($UserName) {
                $resolvedUser = $UserName
            }
            elseif ($Credential) {
                $resolvedUser = $Credential.UserName
            }

            # --- Guardrail: by default, require KeyFilePath to avoid interactive prompts (worker-safe) ---
            # If you *want* to allow prompting, do it only when truly interactive.
            $isInteractive = $Host.Name -match 'ConsoleHost|Visual Studio Code Host' -and
            [Environment]::UserInteractive -and
            -not [Console]::IsInputRedirected

            if (-not $KeyFilePath) {
                if (-not $isInteractive) {
                    throw "SSH requires -KeyFilePath in non-interactive contexts to prevent password prompts/hangs."
                }

                # Interactive session without key: require username so prompt can occur
                if (-not $resolvedUser) {
                    throw "For interactive SSH without -KeyFilePath, specify -UserName or -Credential (username only)."
                }
            }
            else {
                # Key auth requires username (PowerShell uses -UserName + -KeyFilePath). [1](https://learn.microsoft.com/en-us/powershell/scripting/security/remoting/ssh-remoting-in-powershell?view=powershell-7.6)[2](https://bing.com/search?q=PowerShell+remoting+over+SSH+New-PSSession+-HostName+-UserName+-SSHTransport+key+authentication+requirements)
                if (-not $resolvedUser) {
                    throw "For SSH key auth, specify -UserName or -Credential (username only)."
                }

                if (-not (Test-Path -LiteralPath $KeyFilePath)) {
                    throw "SSH key file not found: $KeyFilePath"
                }
            }

            # --- Build SSH session params (only params valid/meaningful for SSH parameter set) ---
            $sshParams = @{
                HostName    = $ComputerName
                ErrorAction = 'Stop'
                Name        = $SessionName
            }

            # Only add Port if user supplied it (avoid passing null/0)
            if ($PSBoundParameters.ContainsKey('Port') -and $Port) {
                $sshParams.Port = $Port
            }

            if ($resolvedUser) {
                $sshParams.UserName = $resolvedUser
            }

            if ($KeyFilePath) {
                $sshParams.KeyFilePath = $KeyFilePath
            }

            # Optional: session options are fine to pass
            if ($sessOpts) {
                $sshParams.SessionOption = $sessOpts
            }

            # NOTE: No ConfigurationName here - SSH remoting doesn't use WinRM-style endpoint configuration/JEA. [1](https://learn.microsoft.com/en-us/powershell/scripting/security/remoting/ssh-remoting-in-powershell?view=powershell-7.6)
            $s = New-PSSession @sshParams

            $portInfo = if ($sshParams.Port) { " (port $($sshParams.Port))" } else { "" }
            Write-Log -Level Ok -Message "Connected to $ComputerName via SSH$portInfo."
            return (Get-SessionOutputObject -Session $s -AsList:$AsList)
        }
        else {
            if ($UseCredSSP) {
                Enable-RemoteCredSSPServer `
                    -TargetComputer $ComputerName `
                    -TargetCredential $Credential `
                    -SessionOptions $sessOpts `
                    -PrimaryConfig $Ps7ConfigName `
                    -FallbackConfig $WinPsConfigName `
                    -TargetSessionName $SessionName
            }

            $auth = if ($UseCredSSP) { 'CredSSP' } else { 'Default' }

            # WSMan: PS7 endpoint first
            try {
                $wsmanParams = @{
                    ComputerName      = $ComputerName
                    Credential        = $Credential
                    Authentication    = $auth
                    ConfigurationName = $Ps7ConfigName
                    ErrorAction       = 'Stop'
                    SessionOption     = $sessOpts
                    Name              = $SessionName
                }
                $s = New-PSSession @wsmanParams
                Write-Log -Level Ok -Message "Connected to $ComputerName via WSMan ($Ps7ConfigName)."
                return (Get-SessionOutputObject -Session $s -AsList:$AsList)
            }
            catch {
                # Fallback to Windows PowerShell endpoint
                $wsmanParams = @{
                    ComputerName      = $ComputerName
                    Credential        = $Credential
                    Authentication    = $auth
                    ConfigurationName = $WinPsConfigName
                    ErrorAction       = 'Stop'
                    SessionOption     = $sessOpts
                    Name              = $SessionName
                }
                $s = New-PSSession @wsmanParams
                Write-Log -Level Ok -Message "Connected to $ComputerName via WSMan ($WinPsConfigName)."
                return (Get-SessionOutputObject -Session $s -AsList:$AsList)
            }
        }
    }
    catch {
        $msg = $_.Exception.Message
        Write-Log -Level Error -Message "Failed to create PSSession to ${ComputerName}: $msg"
        throw
    }
    finally {
        $ErrorActionPreference = $oldEAP
    }
}

# SIG # Begin signature block
# MIIfAgYJKoZIhvcNAQcCoIIe8zCCHu8CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAnDxubrT0sWDMO
# MkzRGCA3xWWIkTP2seNiIez3K4IR3KCCGEowggUMMIIC9KADAgECAhAR+U4xG7FH
# qkyqS9NIt7l5MA0GCSqGSIb3DQEBCwUAMB4xHDAaBgNVBAMME1ZBRFRFSyBDb2Rl
# IFNpZ25pbmcwHhcNMjUxMjE5MTk1NDIxWhcNMjYxMjE5MjAwNDIxWjAeMRwwGgYD
# VQQDDBNWQURURUsgQ29kZSBTaWduaW5nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
# MIICCgKCAgEA3pzzZIUEY92GDldMWuzvbLeivHOuMupgpwbezoG5v90KeuN03S5d
# nM/eom/PcIz08+fGZF04ueuCS6b48q1qFnylwg/C/TkcVRo0WFcKoFGT8yGxdfXi
# caHtapZfbSRh73r7qR7w0CioVveNBVgfMsTgE0WKcuwxemvIe/ptmkfzwAiw/IAC
# Ib0E0BjiX4PySbwWy/QKy/qMXYY19xpRItVTKNBtXzADUtzPzUcFqJU83vM2gZFs
# Or0MhPvM7xEVkOWZFBAWAubbMCJ3rmwyVv9keVDJChhCeLSz2XR11VGDOEA2OO90
# Y30WfY9aOI2sCfQcKMeJ9ypkHl0xORdhUwZ3Wz48d3yJDXGkduPm2vl05RvnA4T6
# 29HVZTmMdvP2475/8nLxCte9IB7TobAOGl6P1NuwplAMKM8qyZh62Br23vcx1fXZ
# TJlKCxBFx1nTa6VlIJk+UbM4ZPm954peB/fIqEacm8LkZ0cPwmLE5ckW7hfK4Trs
# o+RaudU1sKeA+FvpOWgsPccVRWcEYyGkwbyTB3xrIBXA+YckbANZ0XL7fv7x29hn
# gXbZipGu3DnTISiFB43V4MhNDKZYfbWdxze0SwLe8KzIaKnwlwRgvXDMwXgk99Mi
# EbYa3DvA/5ZWikLW9PxBFD7Vdr8ZiG/tRC9I2Y6fnb+PVoZKc/2xsW0CAwEAAaNG
# MEQwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQW
# BBRfYLVE8caSc990rnrIHUjoB7X/KjANBgkqhkiG9w0BAQsFAAOCAgEAiGB2Wmk3
# QBtd1LcynmxHzmu+X4Y5DIpMMNC2ahsqZtPUVcGqmb5IFbVuAdQphL6PSrDjaAR8
# 1S8uTfUnMa119LmIb7di7TlH2F5K3530h5x8JMj5EErl0xmZyJtSg7BTiBA/UrMz
# 6WCf8wWIG2/4NbV6aAyFwIojfAcKoO8ng44Dal/oLGzLO3FDE5AWhcda/FbqVjSJ
# 1zMfiW8odd4LgbmoyEI024KkwOkkPyJQ2Ugn6HMqlFLazAmBBpyS7wxdaAGrl18n
# 6bS7QuAwCd9hitdMMitG8YyWL6tKeRSbuTP5E+ASbu0Ga8/fxRO5ZSQhO6/5ro1j
# PGe1/Kr49Uyuf9VSCZdNIZAyjjeVAoxmV0IfxQLKz6VOG0kGDYkFGskvllIpQbQg
# WLuPLJxoskJsoJllk7MjZJwrpr08+3FQnLkRuisjDOc3l4VxFUsUe4fnJhMUONXT
# Sk7vdspgxirNbLmXU4yYWdsizz3nMUR0zebUW29A+HYme16hzrMPOeyoQjy4I5XX
# 3wXAFdworfPEr/ozDFrdXKgbLwZopymKbBwv6wtT7+1zVhJXr+jGVQ1TWr6R+8ea
# tIOFnY7HqGaxe5XB7HzOwJKdj+bpHAfXft1vUoiKr16VajLigcYCG8MdwC3sngO3
# JDyv2V+YMfsYBmItMGBwvizlQ6557NbK95EwggWNMIIEdaADAgECAhAOmxiO+dAt
# 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa
# Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD
# ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E
# MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy
# unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF
# xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1
# 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB
# MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR
# WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6
# nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB
# YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S
# UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x
# q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB
# NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP
# TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC
# AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
# Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0
# aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB
# LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc
# Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov
# Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy
# oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW
# juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF
# mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z
# twGpn1eqXijiuZQwgga0MIIEnKADAgECAhANx6xXBf8hmS5AQyIMOkmGMA0GCSqG
# SIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy
# dXN0ZWQgUm9vdCBHNDAeFw0yNTA1MDcwMDAwMDBaFw0zODAxMTQyMzU5NTlaMGkx
# CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4
# RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYg
# MjAyNSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC0eDHTCphB
# cr48RsAcrHXbo0ZodLRRF51NrY0NlLWZloMsVO1DahGPNRcybEKq+RuwOnPhof6p
# vF4uGjwjqNjfEvUi6wuim5bap+0lgloM2zX4kftn5B1IpYzTqpyFQ/4Bt0mAxAHe
# HYNnQxqXmRinvuNgxVBdJkf77S2uPoCj7GH8BLuxBG5AvftBdsOECS1UkxBvMgEd
# gkFiDNYiOTx4OtiFcMSkqTtF2hfQz3zQSku2Ws3IfDReb6e3mmdglTcaarps0wjU
# jsZvkgFkriK9tUKJm/s80FiocSk1VYLZlDwFt+cVFBURJg6zMUjZa/zbCclF83bR
# VFLeGkuAhHiGPMvSGmhgaTzVyhYn4p0+8y9oHRaQT/aofEnS5xLrfxnGpTXiUOeS
# LsJygoLPp66bkDX1ZlAeSpQl92QOMeRxykvq6gbylsXQskBBBnGy3tW/AMOMCZIV
# NSaz7BX8VtYGqLt9MmeOreGPRdtBx3yGOP+rx3rKWDEJlIqLXvJWnY0v5ydPpOjL
# 6s36czwzsucuoKs7Yk/ehb//Wx+5kMqIMRvUBDx6z1ev+7psNOdgJMoiwOrUG2Zd
# SoQbU2rMkpLiQ6bGRinZbI4OLu9BMIFm1UUl9VnePs6BaaeEWvjJSjNm2qA+sdFU
# eEY0qVjPKOWug/G6X5uAiynM7Bu2ayBjUwIDAQABo4IBXTCCAVkwEgYDVR0TAQH/
# BAgwBgEB/wIBADAdBgNVHQ4EFgQU729TSunkBnx6yuKQVvYv1Ensy04wHwYDVR0j
# BBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1Ud
# JQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0
# cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0
# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8E
# PDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz
# dGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
# DQYJKoZIhvcNAQELBQADggIBABfO+xaAHP4HPRF2cTC9vgvItTSmf83Qh8WIGjB/
# T8ObXAZz8OjuhUxjaaFdleMM0lBryPTQM2qEJPe36zwbSI/mS83afsl3YTj+IQhQ
# E7jU/kXjjytJgnn0hvrV6hqWGd3rLAUt6vJy9lMDPjTLxLgXf9r5nWMQwr8Myb9r
# EVKChHyfpzee5kH0F8HABBgr0UdqirZ7bowe9Vj2AIMD8liyrukZ2iA/wdG2th9y
# 1IsA0QF8dTXqvcnTmpfeQh35k5zOCPmSNq1UH410ANVko43+Cdmu4y81hjajV/gx
# dEkMx1NKU4uHQcKfZxAvBAKqMVuqte69M9J6A47OvgRaPs+2ykgcGV00TYr2Lr3t
# y9qIijanrUR3anzEwlvzZiiyfTPjLbnFRsjsYg39OlV8cipDoq7+qNNjqFzeGxcy
# tL5TTLL4ZaoBdqbhOhZ3ZRDUphPvSRmMThi0vw9vODRzW6AxnJll38F0cuJG7uEB
# YTptMSbhdhGQDpOXgpIUsWTjd6xpR6oaQf/DJbg3s6KCLPAlZ66RzIg9sC+NJpud
# /v4+7RWsWCiKi9EOLLHfMR2ZyJ/+xhCx9yHbxtl5TPau1j/1MIDpMPx0LckTetiS
# uEtQvLsNz3Qbp7wGWqbIiOWCnb5WqxL3/BAPvIXKUjPSxyZsq8WhbaM2tszWkPZP
# ubdcMIIG7TCCBNWgAwIBAgIQCoDvGEuN8QWC0cR2p5V0aDANBgkqhkiG9w0BAQsF
# ADBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNV
# BAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hB
# MjU2IDIwMjUgQ0ExMB4XDTI1MDYwNDAwMDAwMFoXDTM2MDkwMzIzNTk1OVowYzEL
# MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJE
# aWdpQ2VydCBTSEEyNTYgUlNBNDA5NiBUaW1lc3RhbXAgUmVzcG9uZGVyIDIwMjUg
# MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANBGrC0Sxp7Q6q5gVrMr
# V7pvUf+GcAoB38o3zBlCMGMyqJnfFNZx+wvA69HFTBdwbHwBSOeLpvPnZ8ZN+vo8
# dE2/pPvOx/Vj8TchTySA2R4QKpVD7dvNZh6wW2R6kSu9RJt/4QhguSssp3qome7M
# rxVyfQO9sMx6ZAWjFDYOzDi8SOhPUWlLnh00Cll8pjrUcCV3K3E0zz09ldQ//nBZ
# ZREr4h/GI6Dxb2UoyrN0ijtUDVHRXdmncOOMA3CoB/iUSROUINDT98oksouTMYFO
# nHoRh6+86Ltc5zjPKHW5KqCvpSduSwhwUmotuQhcg9tw2YD3w6ySSSu+3qU8DD+n
# igNJFmt6LAHvH3KSuNLoZLc1Hf2JNMVL4Q1OpbybpMe46YceNA0LfNsnqcnpJeIt
# K/DhKbPxTTuGoX7wJNdoRORVbPR1VVnDuSeHVZlc4seAO+6d2sC26/PQPdP51ho1
# zBp+xUIZkpSFA8vWdoUoHLWnqWU3dCCyFG1roSrgHjSHlq8xymLnjCbSLZ49kPmk
# 8iyyizNDIXj//cOgrY7rlRyTlaCCfw7aSUROwnu7zER6EaJ+AliL7ojTdS5PWPsW
# eupWs7NpChUk555K096V1hE0yZIXe+giAwW00aHzrDchIc2bQhpp0IoKRR7YufAk
# prxMiXAJQ1XCmnCfgPf8+3mnAgMBAAGjggGVMIIBkTAMBgNVHRMBAf8EAjAAMB0G
# A1UdDgQWBBTkO/zyMe39/dfzkXFjGVBDz2GM6DAfBgNVHSMEGDAWgBTvb1NK6eQG
# fHrK4pBW9i/USezLTjAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYB
# BQUHAwgwgZUGCCsGAQUFBwEBBIGIMIGFMCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz
# cC5kaWdpY2VydC5jb20wXQYIKwYBBQUHMAKGUWh0dHA6Ly9jYWNlcnRzLmRpZ2lj
# ZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFRpbWVTdGFtcGluZ1JTQTQwOTZTSEEy
# NTYyMDI1Q0ExLmNydDBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRUaW1lU3RhbXBpbmdSU0E0MDk2U0hB
# MjU2MjAyNUNBMS5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
# MA0GCSqGSIb3DQEBCwUAA4ICAQBlKq3xHCcEua5gQezRCESeY0ByIfjk9iJP2zWL
# pQq1b4URGnwWBdEZD9gBq9fNaNmFj6Eh8/YmRDfxT7C0k8FUFqNh+tshgb4O6Lgj
# g8K8elC4+oWCqnU/ML9lFfim8/9yJmZSe2F8AQ/UdKFOtj7YMTmqPO9mzskgiC3Q
# YIUP2S3HQvHG1FDu+WUqW4daIqToXFE/JQ/EABgfZXLWU0ziTN6R3ygQBHMUBaB5
# bdrPbF6MRYs03h4obEMnxYOX8VBRKe1uNnzQVTeLni2nHkX/QqvXnNb+YkDFkxUG
# tMTaiLR9wjxUxu2hECZpqyU1d0IbX6Wq8/gVutDojBIFeRlqAcuEVT0cKsb+zJNE
# suEB7O7/cuvTQasnM9AWcIQfVjnzrvwiCZ85EE8LUkqRhoS3Y50OHgaY7T/lwd6U
# Arb+BOVAkg2oOvol/DJgddJ35XTxfUlQ+8Hggt8l2Yv7roancJIFcbojBcxlRcGG
# 0LIhp6GvReQGgMgYxQbV1S3CrWqZzBt1R9xJgKf47CdxVRd/ndUlQ05oxYy2zRWV
# FjF7mcr4C34Mj3ocCVccAvlKV9jEnstrniLvUxxVZE/rptb7IRE2lskKPIJgbaP5
# t2nGj/ULLi49xTcBZU8atufk+EMF/cWuiC7POGT75qaL6vdCvHlshtjdNXOCIUjs
# arfNZzGCBg4wggYKAgEBMDIwHjEcMBoGA1UEAwwTVkFEVEVLIENvZGUgU2lnbmlu
# ZwIQEflOMRuxR6pMqkvTSLe5eTANBglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3
# AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisG
# AQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCB8gPHl3FeN
# zqMuh81vKygRX6qY8wIrGbD4o7KviNYU/zANBgkqhkiG9w0BAQEFAASCAgABIJxZ
# fKyezNp8UO8mBKYDy6Iu3bcUp1mVXH8UJvS1a4T51PsDi9i74SvkKO1sTDPOtDHe
# m9ehC7aZKb5ynC6hsDvRDJxTpYHuswaKCkT/E4mC6uGW/jJavX011/VyECtGC0bJ
# bKqQP9GOjZ1dwCXHUcxdaf7XKPwD9NtQrcFouLLWjpUWqWEMwkRBsDSbFOzhjdWE
# CkXCDftYFQg3hwyUc4ld8NSDge9MupzjwTuz1Ewaowep6YvnYuV2zQp1XzgvmOFV
# SwDIRz2zxBxSv1KCZG1u2B5OSLPOBG2r6xb69XLAy2gRiv6HY0xYGx8tmncCy8Ts
# tg2aepR7SIwE7sHELMaXk+UiCmglmGne2XO2OxUUctKkiP3OefCqfZvtiPCv6djt
# fpBBVinM9y9bM+motW8Y+7ZtWhfxSbVwzGFXkT36jVtUnliPe350uMLLsj7Rfrfh
# Ng2UE/7WgD9BR16LPvCq0hGVqBTjsWSpf97vLJPfIX3A6u7M2SH/h2ypwXcVEPis
# vIKqr4ZJ0j4jmTl57SXVNhDfSmu25Vdhw3tzuO88R0X63skjByvj+oLhAYLCTzmU
# l3PdWD6wsmjrPcRGHa0DbfKjbRIphWcEu0kO3Viz3BtLlqdXAZSo2/nVf8f6OfJU
# urfuR6FbP624p9rGdKURwJ8Lht45Z8atHFjh9qGCAyYwggMiBgkqhkiG9w0BCQYx
# ggMTMIIDDwIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcg
# UlNBNDA5NiBTSEEyNTYgMjAyNSBDQTECEAqA7xhLjfEFgtHEdqeVdGgwDQYJYIZI
# AWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJ
# BTEPFw0yNjA1MjgxMzU3NTFaMC8GCSqGSIb3DQEJBDEiBCC/cX5geNLp7o0xlaoN
# kjTL4Lzg8fQGiuSjFRIXsUFqczANBgkqhkiG9w0BAQEFAASCAgBrR9EmdlKFMGle
# SEClfs91rXhpKwiNRZfWGVIr8AC0SB+Ec+Dyd4yKGlUpSHs1/iYyp9wdqdtq2uyK
# Kg5rnk/5gD0QP6biCwOLiD1lw2CYBFi+SlCOny17Y3KFbxrLmcKjaKWZmsm2OEp6
# u3Rr81s8grfTdOP+99tYBW9Ny/jRdBZ2LWoBqMLCvxrBxfsaYEiveJutz9/0Njsm
# smK29psZcarp++fI9zs+pb8UhRWaLzvLqSRXvIOaRquXldtsW+cy5AUfJVnhZJSb
# tkhRFzbwpRgDlhHzTsTgImUrmDbeenyFFYktlsp4Pb9DneepTjKEUIWBBfiffH2h
# ktZHI6BPlb4AaVfocQYRdhkknk/ni5NGXYzv8j+6NM1JM/2GWRtWx2nq3D5paZLo
# CJ9cdt5c+DNN3uSkt+veLqNThuYPePoMr2KdmQFttvrfh1ChsC+9zuFap6cZmeKn
# GfAL2I1paIQrQVat/WAUGqznnZdadickl600pE4X4kbOgGusjMcceqsV1NO9rGSV
# s1QF0AyY4H6mOqRkqbr/CD7Ue/QBm4EG0oa4Nv31WTTyux+ew+pIS7X8+fFhyKIv
# xBkPs3l4kjY4vTMrmSztczMUVZKyDLXwS1KX7RU1lEYAU5NFjJL24GJfgWu2SNso
# Ec66N5LEGUbDMDm78bMu+QdLsvn+ug==
# SIG # End signature block