Public/Invoke-StubScript.ps1

function Invoke-StubScript {
    <#
    .SYNOPSIS
        Run a stub script's payload with the standard Stub-Boilerplate around it.
 
    .DESCRIPTION
        Wraps a payload ScriptBlock in the boilerplate that almost every
        convenience-stub needs:
 
          - Strict mode and "stop on error" preference for the payload.
          - TLS 1.2 fallback for environments that still default to TLS 1.0
            (matters when the payload calls Install-Module or Invoke-WebRequest).
          - Caller tracking via Set-PSScriptID. Always runs, regardless of
            elevation. The PSScriptID powers Exit-AndWaitOnUI's "press any key"
            behaviour for Explorer / double-click launches.
          - Optional self-elevation via Restart-SelfElevated. When -RequireElevation
            is set, the entire payload (including any Read-Host prompts) runs in
            the elevated process. Do NOT prompt the user before calling this
            function in that case - the input would happen in the unelevated
            window and be lost when the elevated relaunch starts a new process.
          - -WhatIf, -Confirm, -Verbose, -Debug, -WarningAction,
            -InformationAction and -ProgressAction (PS 7.4+ only) are
            forwarded into the payload by overwriting the matching
            $global: preference variables for the duration of the
            payload, so cmdlets in the payload pick them up via the
            normal scope chain. The effective values are resolved with
            this priority:
              1. The switch / action parameter bound directly on
                 Invoke-StubScript.
              2. The caller frame's $WhatIfPreference /
                 $ConfirmPreference / $VerbosePreference /
                 $DebugPreference / $WarningPreference /
                 $InformationPreference / $ProgressPreference, read via
                 Get-PSCallStack. This is the cross-module-stub path:
                 PowerShell's parameter binder does NOT inherit these
                 preferences across module boundaries, so the lookup is
                 explicit.
              3. Defaults ($false for WhatIf, High for Confirm,
                 SilentlyContinue for Verbose / Debug /
                 InformationAction, Continue for WarningAction /
                 ProgressAction).
            -ErrorAction is intentionally NOT forwarded - the function
            sets $ErrorActionPreference = 'Stop' as part of its own
            try/catch boilerplate and overriding that would defeat the
            stub-script error contract.
            The previous global values are snapshotted before mutation
            and restored unconditionally in the finally block, even on
            payload error.
          - Status messages before and after the payload (Write-Host, neutral
            colour - matches PowerShell's standard info-stream behaviour).
          - try/catch/finally with structured error re-throw via
            Write-Error -ErrorRecord. The original ErrorRecord (with
            ScriptStackTrace, inner exceptions etc.) is preserved.
          - Exit-AndWaitOnUI at the end. Always runs, regardless of elevation.
            It auto-detects the caller (Explorer vs. console) and either pauses
            with "press any key to continue" (UI launch) or exits immediately
            (console launch). The current process terminates either way.
 
        Variables defined in the calling stub are visible inside the payload
        because a ScriptBlock resolves variables through the SessionState in
        which it was created, not through the scope at the call site. The
        payload is therefore invoked with the call operator (`&`); the
        SessionState binding does the real work, and the call operator avoids
        leaking variables created inside the payload back into the caller.
 
    .PARAMETER ScriptBlock
        The payload to run. This is the actual work of the stub - typically a
        handful of cmdlet calls. Variables defined in the calling stub are
        visible inside the ScriptBlock because the ScriptBlock carries the
        stub's SessionState.
 
    .PARAMETER RequireElevation
        When set, the function calls Restart-SelfElevated before running the
        payload. Use this for any payload that touches Machine-scope state
        (registry, machine-wide env vars, etc.).
 
        Both Set-PSScriptID and Exit-AndWaitOnUI run regardless of this switch -
        they handle caller tracking and UI-aware exit, not elevation.
 
    .PARAMETER StatusMessage
        Optional message printed via Write-Host before the payload runs. Use to
        tell the user what is about to happen.
 
    .PARAMETER DoneMessage
        Message printed via Write-Host after a successful payload run.
        Defaults to 'Done.'. Pass an empty string to suppress.
 
    .OUTPUTS
        None. The function never returns to the caller because Exit-AndWaitOnUI
        terminates the process at the end. The exit code is 0 on success,
        1 on caught error.
 
    .EXAMPLE
        # Simple user-scope stub.
        Invoke-StubScript -StatusMessage 'Opting out of telemetry...' {
            Set-EnvVar -Name 'DOTNET_CLI_TELEMETRY_OPTOUT' -Value '1'
            Set-EnvVar -Name 'DOTNET_EnableDiagnostics' -Value '1'
        }
 
    .EXAMPLE
        # Machine-scope stub with elevation. Read-Host runs INSIDE the
        # ScriptBlock, i.e. after the elevated relaunch.
        Invoke-StubScript -RequireElevation -StatusMessage 'Setting JAVA_HOME...' {
            $value = Read-Host 'Path to JDK install'
            Set-EnvVar -Name 'JAVA_HOME' -Value $value -Target Machine
        }
 
    .EXAMPLE
        # Forwarding -WhatIf / -Verbose works automatically.
        # Caller stub uses [CmdletBinding(SupportsShouldProcess)] and is
        # invoked with -WhatIf -Verbose. Invoke-StubScript reads the caller's
        # $WhatIfPreference and $VerbosePreference via Get-PSCallStack and
        # publishes them as $global:WhatIfPreference / $global:VerbosePreference
        # for the payload. No explicit forwarding needed inside the stub.
 
    .NOTES
        Set-PSScriptID, Restart-SelfElevated and Exit-AndWaitOnUI come from the
        Execution module and must be available when this function is called.
 
        Variable visibility from the calling stub is provided by the
        ScriptBlock's SessionState binding, not by the call operator.
        The payload is invoked with `&`, which prevents the payload's
        local variables from leaking back into the caller's scope.
 
        -WhatIf / -Confirm / -Verbose / -Debug / -WarningAction /
        -InformationAction / -ProgressAction forwarding does NOT rely on
        PowerShell's parameter binder inheriting the caller's preferences
        automatically; it does not, across module boundaries. The function
        instead resolves the effective values explicitly:
          1. The switch / action parameter bound directly on
             Invoke-StubScript wins.
          2. Otherwise the caller frame's corresponding preference variable
             is read via (Get-PSCallStack)[1].GetFrameVariables().
          3. Otherwise the documented defaults apply.
        The chosen values are written into the matching $global: preference
        variables for the duration of the payload, then restored
        unconditionally in finally - even on payload error.
 
        -ErrorAction is intentionally not forwarded; the function commits
        to $ErrorActionPreference = 'Stop' for its try/catch contract.
 
        -ProgressAction was added in PowerShell 7.4. On older hosts the
        parameter is unknown to the binder; resolution falls straight
        through to the caller-frame lookup (which finds $ProgressPreference
        on every supported version) or the default.
 
        Testing interactively: calling this function from a normal PowerShell
        prompt will terminate the session via Exit-AndWaitOnUI's exit. Use a
        separate session (pwsh -NoExit -Command ...) for interactive smoke tests.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([void])]
    param(
        [Parameter(Mandatory, Position = 0)]
        [ValidateNotNull()]
        [scriptblock] $ScriptBlock,

        [switch] $RequireElevation,

        [string] $StatusMessage,

        [string] $DoneMessage = 'Done.'
    )

    # Defensive defaults for the payload context. The caller can still
    # override these inside the ScriptBlock if a particular stub needs
    # different behaviour.
    Set-StrictMode -Version Latest
    $ErrorActionPreference = 'Stop'
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

    # Caller tracking: always register the script, regardless of elevation.
    $PSScriptID = Set-PSScriptID

    if ($RequireElevation) {
        Restart-SelfElevated -PSScriptID $PSScriptID
    }

    # Resolve the effective preferences. PowerShell's CmdletBinding parameter
    # binder does NOT propagate $WhatIfPreference / $ConfirmPreference /
    # $VerbosePreference / $DebugPreference / $WarningPreference /
    # $InformationPreference / $ProgressPreference across module boundaries.
    # A stub with [CmdletBinding(SupportsShouldProcess)] called with -Verbose
    # has $VerbosePreference = 'Continue' in its own SessionState, but this
    # function's own $VerbosePreference defaults back to 'SilentlyContinue'
    # unless -Verbose is bound explicitly. Reading the caller's frame
    # variables closes that gap so callers do not have to write
    # `-Verbose:$VerbosePreference` by hand for every switch.
    #
    # -ErrorAction is intentionally not forwarded; the function commits to
    # $ErrorActionPreference = 'Stop' for the wrapper's try/catch contract.
    $callerVars = (Get-PSCallStack)[1].GetFrameVariables()

    $effectiveWhatIf = if ($PSBoundParameters.ContainsKey('WhatIf')) {
        [bool]$WhatIfPreference
    } elseif ($callerVars.ContainsKey('WhatIfPreference')) {
        [bool]$callerVars['WhatIfPreference'].Value
    } else {
        $false
    }

    $effectiveConfirm = if ($PSBoundParameters.ContainsKey('Confirm')) {
        $ConfirmPreference
    } elseif ($callerVars.ContainsKey('ConfirmPreference')) {
        $callerVars['ConfirmPreference'].Value
    } else {
        [System.Management.Automation.ConfirmImpact]::High
    }

    # Each block follows the same shape: when the parameter is bound on
    # this function, the binder has already set the matching preference
    # variable in the function's local scope - read it directly. When the
    # parameter is not bound, fall through to the caller frame and finally
    # to the documented default. -ProgressAction is PS 7.4+ only as a
    # bound parameter; on older hosts that branch never fires and the
    # caller-frame lookup handles $ProgressPreference uniformly.
    $effectiveVerbose = if ($PSBoundParameters.ContainsKey('Verbose')) {
        $VerbosePreference
    } elseif ($callerVars.ContainsKey('VerbosePreference')) {
        $callerVars['VerbosePreference'].Value
    } else {
        [System.Management.Automation.ActionPreference]::SilentlyContinue
    }

    $effectiveDebug = if ($PSBoundParameters.ContainsKey('Debug')) {
        $DebugPreference
    } elseif ($callerVars.ContainsKey('DebugPreference')) {
        $callerVars['DebugPreference'].Value
    } else {
        [System.Management.Automation.ActionPreference]::SilentlyContinue
    }

    $effectiveWarning = if ($PSBoundParameters.ContainsKey('WarningAction')) {
        $WarningPreference
    } elseif ($callerVars.ContainsKey('WarningPreference')) {
        $callerVars['WarningPreference'].Value
    } else {
        [System.Management.Automation.ActionPreference]::Continue
    }

    $effectiveInformation = if ($PSBoundParameters.ContainsKey('InformationAction')) {
        $InformationPreference
    } elseif ($callerVars.ContainsKey('InformationPreference')) {
        $callerVars['InformationPreference'].Value
    } else {
        [System.Management.Automation.ActionPreference]::SilentlyContinue
    }

    $effectiveProgress = if ($PSBoundParameters.ContainsKey('ProgressAction')) {
        $ProgressPreference
    } elseif ($callerVars.ContainsKey('ProgressPreference')) {
        $callerVars['ProgressPreference'].Value
    } else {
        [System.Management.Automation.ActionPreference]::Continue
    }

    $previousGlobalWhatIfPreference = $global:WhatIfPreference
    $previousGlobalConfirmPreference = $global:ConfirmPreference
    $previousGlobalVerbosePreference = $global:VerbosePreference
    $previousGlobalDebugPreference = $global:DebugPreference
    $previousGlobalWarningPreference = $global:WarningPreference
    $previousGlobalInformationPreference = $global:InformationPreference
    $previousGlobalProgressPreference = $global:ProgressPreference

    $global:WhatIfPreference = $effectiveWhatIf
    $global:ConfirmPreference = $effectiveConfirm
    $global:VerbosePreference = $effectiveVerbose
    $global:DebugPreference = $effectiveDebug
    $global:WarningPreference = $effectiveWarning
    $global:InformationPreference = $effectiveInformation
    $global:ProgressPreference = $effectiveProgress

    $ExitCode = 0
    try {
        if ($StatusMessage) { Write-Host $StatusMessage }

        # Invoke with the call operator. The ScriptBlock carries the stub's
        # SessionState, so caller-scope variables are visible inside the
        # payload regardless of operator. Using `&` (rather than `.`) keeps
        # variables created inside the payload from leaking back into the
        # stub's scope, which is the correct default for stub-style usage.
        & $ScriptBlock

        if ($DoneMessage) { Write-Host $DoneMessage }
    } catch {
        Write-Error -ErrorRecord $_
        $ExitCode = 1
    } finally {
        $global:WhatIfPreference = $previousGlobalWhatIfPreference
        $global:ConfirmPreference = $previousGlobalConfirmPreference
        $global:VerbosePreference = $previousGlobalVerbosePreference
        $global:DebugPreference = $previousGlobalDebugPreference
        $global:WarningPreference = $previousGlobalWarningPreference
        $global:InformationPreference = $previousGlobalInformationPreference
        $global:ProgressPreference = $previousGlobalProgressPreference
    }

    Exit-AndWaitOnUI -ExitCode $ExitCode -PSScriptID $PSScriptID
}

# SIG # Begin signature block
# MIIn8gYJKoZIhvcNAQcCoIIn4zCCJ98CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCATJnvI7W20GgfK
# Il2z/wfRKNZpmje2HCM87E3nFvvyoKCCIP4wggWNMIIEdaADAgECAhAOmxiO+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
# twGpn1eqXijiuZQwggahMIIEiaADAgECAhAHhD2tAcEVwnTuQacoIkZ5MA0GCSqG
# SIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy
# dXN0ZWQgUm9vdCBHNDAeFw0yMjA2MjMwMDAwMDBaFw0zMjA2MjIyMzU5NTlaMFox
# CzAJBgNVBAYTAkxWMRkwFwYDVQQKExBFblZlcnMgR3JvdXAgU0lBMTAwLgYDVQQD
# EydHb0dldFNTTCBHNCBDUyBSU0E0MDk2IFNIQTI1NiAyMDIyIENBLTEwggIiMA0G
# CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCtHvQHskNmiqJndyWVCqX4FtYp5FfJ
# LO9Sh0BuwXuvBeNYt21xf8h/pLJ/7YzeKcNq9z4zEhecqtD0xhbvSB8ksBAfWBMZ
# O0NLfOT0j7WyNuD7rv+ZFza+mxIQ79s1dCiwUMwGonaoDK7mqZfDpKEExR6UyKBh
# 3aatT73U2Imx/x+fYTmQFq+N8FrLs6Fh6YEGWJTgsxyw1fAChCfgtEcZkdtcgK7q
# uqskHtW6PJ9l5VNJ7T3WXpznsOOxrz3qx0CzWjwK8+3Kv2X6piWvd8YRfAOycSrT
# 4/PM0cHLFc5xs/4m/ek4FCnYSem43doFftBxZBQkHKoPW3Bt6VIrhVIwvO7hrUjh
# chJJZYdSld3bANDviJ5/ToP7ENv97U9MtKFvmC5dzd1p4HxFR0p5wWmYQbW+y3RF
# m0np6H9m57MUMNp0ysmdJjb0f7+dVLX3OEBUb6H+r1LRLZT/xEOTuwOxGg2S4w25
# KGL9SCBUW4nkBljPHeJToU+THt0P8ZQf4B9IFlGxtLK0g3uOAnwSFgKtmNjhkTl8
# caLAQwbgEINCqrhc0b6k2Z8+QwgVAL0nIuzM9ckKP8xtIcWg85L3/l0cTkHQde+j
# KGDG2CdxBHtflLIUtwqD7JA2uCxWlIzRNgwT0kH2en0+QV8KziSGaqO2r06kwboq
# 2/xy4e98CEfSYwIDAQABo4IBWTCCAVUwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNV
# HQ4EFgQUyfwQ71DIy2t/vQhE7zpik+1bXpowHwYDVR0jBBgwFoAU7NfjgtJxXWRM
# 3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMD
# MHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl
# cnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v
# RGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRw
# Oi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAc
# BgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATANBgkqhkiG9w0BAQsFAAOCAgEA
# C9sK17IdmKTCUatEs7+yewhJnJ4tyrLwNEnfl6HrG8Pm7HZ0b+5Jc+GGqJT8kRc7
# mihuVrdsYNHdicueDL9imhtCusI/rUmjwhtflp+XgLkmgLGrmsEho1b+lGiRp7LC
# /10di8SAOilDkHj5Zx142xRvBrrWj9eOdSGHwYubAsEd6CDojwcaVz9pfXMzYO3k
# c0O6PXg1TkcgkYlCUAuDHuk/sZx68W0FVj1P2iMh+VUq9lL1puroAydoeWVUh/+c
# MXeqfgpBqlAW+r8ma5F6yKL0stVQH8vYb1ES0mJSIPyIfkIjC1V0pbZS3p0QWsKa
# afEor8fLfLNfSxntVI/ugut0+6ekluPWRpEXH+JAiNdRjbLbZchCREe3/Xl0Ylwk
# A+eQVJfM0A7XiuFtY/mOpK2AN+E25t5mQYFhpdxZX5LTDKWgDnb+A6QnEt4iNyuk
# cLaJuS8IPgPz0E2ALZLt3Rqs+lXifK/GwnNIWQNbf7FmLDB9ph8i8dvsR1hsjc2K
# PEW4bAsbvLcz8hN1zE1/QbOV92vDGoFjwZOi2koQ+UyEh0e8jDFHAKJeTI+p8EPE
# /mqvojLFAnt31yXIA2tjt0ERtsjkhBNmZY6SEOfnIoOwvyqavLPya1Ut3/2cOFLu
# NQ8Ql6HaZsNQErnnzn+ZEAaUTkPZaeVyoHIkODECLzkwgga0MIIEnKADAgECAhAN
# x6xXBf8hmS5AQyIMOkmGMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUw
# EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x
# ITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yNTA1MDcwMDAw
# MDBaFw0zODAxMTQyMzU5NTlaMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdp
# Q2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3Rh
# bXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEwggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQC0eDHTCphBcr48RsAcrHXbo0ZodLRRF51NrY0NlLWZloMs
# VO1DahGPNRcybEKq+RuwOnPhof6pvF4uGjwjqNjfEvUi6wuim5bap+0lgloM2zX4
# kftn5B1IpYzTqpyFQ/4Bt0mAxAHeHYNnQxqXmRinvuNgxVBdJkf77S2uPoCj7GH8
# BLuxBG5AvftBdsOECS1UkxBvMgEdgkFiDNYiOTx4OtiFcMSkqTtF2hfQz3zQSku2
# Ws3IfDReb6e3mmdglTcaarps0wjUjsZvkgFkriK9tUKJm/s80FiocSk1VYLZlDwF
# t+cVFBURJg6zMUjZa/zbCclF83bRVFLeGkuAhHiGPMvSGmhgaTzVyhYn4p0+8y9o
# HRaQT/aofEnS5xLrfxnGpTXiUOeSLsJygoLPp66bkDX1ZlAeSpQl92QOMeRxykvq
# 6gbylsXQskBBBnGy3tW/AMOMCZIVNSaz7BX8VtYGqLt9MmeOreGPRdtBx3yGOP+r
# x3rKWDEJlIqLXvJWnY0v5ydPpOjL6s36czwzsucuoKs7Yk/ehb//Wx+5kMqIMRvU
# BDx6z1ev+7psNOdgJMoiwOrUG2ZdSoQbU2rMkpLiQ6bGRinZbI4OLu9BMIFm1UUl
# 9VnePs6BaaeEWvjJSjNm2qA+sdFUeEY0qVjPKOWug/G6X5uAiynM7Bu2ayBjUwID
# AQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU729TSunk
# Bnx6yuKQVvYv1Ensy04wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08w
# DgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEB
# BGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsG
# AQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz
# dGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgG
# BmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBABfO+xaAHP4H
# PRF2cTC9vgvItTSmf83Qh8WIGjB/T8ObXAZz8OjuhUxjaaFdleMM0lBryPTQM2qE
# JPe36zwbSI/mS83afsl3YTj+IQhQE7jU/kXjjytJgnn0hvrV6hqWGd3rLAUt6vJy
# 9lMDPjTLxLgXf9r5nWMQwr8Myb9rEVKChHyfpzee5kH0F8HABBgr0UdqirZ7bowe
# 9Vj2AIMD8liyrukZ2iA/wdG2th9y1IsA0QF8dTXqvcnTmpfeQh35k5zOCPmSNq1U
# H410ANVko43+Cdmu4y81hjajV/gxdEkMx1NKU4uHQcKfZxAvBAKqMVuqte69M9J6
# A47OvgRaPs+2ykgcGV00TYr2Lr3ty9qIijanrUR3anzEwlvzZiiyfTPjLbnFRsjs
# Yg39OlV8cipDoq7+qNNjqFzeGxcytL5TTLL4ZaoBdqbhOhZ3ZRDUphPvSRmMThi0
# vw9vODRzW6AxnJll38F0cuJG7uEBYTptMSbhdhGQDpOXgpIUsWTjd6xpR6oaQf/D
# Jbg3s6KCLPAlZ66RzIg9sC+NJpud/v4+7RWsWCiKi9EOLLHfMR2ZyJ/+xhCx9yHb
# xtl5TPau1j/1MIDpMPx0LckTetiSuEtQvLsNz3Qbp7wGWqbIiOWCnb5WqxL3/BAP
# vIXKUjPSxyZsq8WhbaM2tszWkPZPubdcMIIG7TCCBNWgAwIBAgIQCoDvGEuN8QWC
# 0cR2p5V0aDANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMO
# RGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgVGlt
# ZVN0YW1waW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUgQ0ExMB4XDTI1MDYwNDAwMDAw
# MFoXDTM2MDkwMzIzNTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBTSEEyNTYgUlNBNDA5NiBUaW1l
# c3RhbXAgUmVzcG9uZGVyIDIwMjUgMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
# AgoCggIBANBGrC0Sxp7Q6q5gVrMrV7pvUf+GcAoB38o3zBlCMGMyqJnfFNZx+wvA
# 69HFTBdwbHwBSOeLpvPnZ8ZN+vo8dE2/pPvOx/Vj8TchTySA2R4QKpVD7dvNZh6w
# W2R6kSu9RJt/4QhguSssp3qome7MrxVyfQO9sMx6ZAWjFDYOzDi8SOhPUWlLnh00
# Cll8pjrUcCV3K3E0zz09ldQ//nBZZREr4h/GI6Dxb2UoyrN0ijtUDVHRXdmncOOM
# A3CoB/iUSROUINDT98oksouTMYFOnHoRh6+86Ltc5zjPKHW5KqCvpSduSwhwUmot
# uQhcg9tw2YD3w6ySSSu+3qU8DD+nigNJFmt6LAHvH3KSuNLoZLc1Hf2JNMVL4Q1O
# pbybpMe46YceNA0LfNsnqcnpJeItK/DhKbPxTTuGoX7wJNdoRORVbPR1VVnDuSeH
# VZlc4seAO+6d2sC26/PQPdP51ho1zBp+xUIZkpSFA8vWdoUoHLWnqWU3dCCyFG1r
# oSrgHjSHlq8xymLnjCbSLZ49kPmk8iyyizNDIXj//cOgrY7rlRyTlaCCfw7aSURO
# wnu7zER6EaJ+AliL7ojTdS5PWPsWeupWs7NpChUk555K096V1hE0yZIXe+giAwW0
# 0aHzrDchIc2bQhpp0IoKRR7YufAkprxMiXAJQ1XCmnCfgPf8+3mnAgMBAAGjggGV
# MIIBkTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTkO/zyMe39/dfzkXFjGVBDz2GM
# 6DAfBgNVHSMEGDAWgBTvb1NK6eQGfHrK4pBW9i/USezLTjAOBgNVHQ8BAf8EBAMC
# B4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwgZUGCCsGAQUFBwEBBIGIMIGFMCQG
# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXQYIKwYBBQUHMAKG
# UWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFRp
# bWVTdGFtcGluZ1JTQTQwOTZTSEEyNTYyMDI1Q0ExLmNydDBfBgNVHR8EWDBWMFSg
# UqBQhk5odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRU
# aW1lU3RhbXBpbmdSU0E0MDk2U0hBMjU2MjAyNUNBMS5jcmwwIAYDVR0gBBkwFzAI
# BgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQBlKq3xHCcE
# ua5gQezRCESeY0ByIfjk9iJP2zWLpQq1b4URGnwWBdEZD9gBq9fNaNmFj6Eh8/Ym
# RDfxT7C0k8FUFqNh+tshgb4O6Lgjg8K8elC4+oWCqnU/ML9lFfim8/9yJmZSe2F8
# AQ/UdKFOtj7YMTmqPO9mzskgiC3QYIUP2S3HQvHG1FDu+WUqW4daIqToXFE/JQ/E
# ABgfZXLWU0ziTN6R3ygQBHMUBaB5bdrPbF6MRYs03h4obEMnxYOX8VBRKe1uNnzQ
# VTeLni2nHkX/QqvXnNb+YkDFkxUGtMTaiLR9wjxUxu2hECZpqyU1d0IbX6Wq8/gV
# utDojBIFeRlqAcuEVT0cKsb+zJNEsuEB7O7/cuvTQasnM9AWcIQfVjnzrvwiCZ85
# EE8LUkqRhoS3Y50OHgaY7T/lwd6UArb+BOVAkg2oOvol/DJgddJ35XTxfUlQ+8Hg
# gt8l2Yv7roancJIFcbojBcxlRcGG0LIhp6GvReQGgMgYxQbV1S3CrWqZzBt1R9xJ
# gKf47CdxVRd/ndUlQ05oxYy2zRWVFjF7mcr4C34Mj3ocCVccAvlKV9jEnstrniLv
# UxxVZE/rptb7IRE2lskKPIJgbaP5t2nGj/ULLi49xTcBZU8atufk+EMF/cWuiC7P
# OGT75qaL6vdCvHlshtjdNXOCIUjsarfNZzCCBxswggUDoAMCAQICEAYFIuuX3jJX
# cLz8AeYyZmYwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UEBhMCTFYxGTAXBgNVBAoT
# EEVuVmVycyBHcm91cCBTSUExMDAuBgNVBAMTJ0dvR2V0U1NMIEc0IENTIFJTQTQw
# OTYgU0hBMjU2IDIwMjIgQ0EtMTAeFw0yNTEyMjkwMDAwMDBaFw0yNjEyMjgyMzU5
# NTlaMGExCzAJBgNVBAYTAkRFMQ8wDQYDVQQIEwZCYXllcm4xETAPBgNVBAcTCEZh
# cmNoYW50MRYwFAYDVQQKEw1NYW51ZWwgVGFuemVyMRYwFAYDVQQDEw1NYW51ZWwg
# VGFuemVyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuxv/b0+nhaEE
# 94ewtCEHJFhCdgrwn6iF1lZ41Y40alX0LnSGBniZxw480yw+F9o6qhhprIpKvEoW
# WW1q+KNMxjdbLgKPlMcudU8DTfUVxKRxlVuqSMdizawGlQgwtwrj5RDsWEE2k0nq
# guNejIRWrWDn6JblBaj/WIx8DZ9YuYl6egi/KtJF5lYC6StOgymjFdZQ6WKs5v9Q
# IZMCvMw0BqpthVkFUXEQ07J49/h50bkl1dfNAnT2K8V2QeVGuJ5YOSs3GFMXiAxZ
# g6LJTHT0poTL5I5XD62X9xQ56LMRa9IDmFx6x4sMDrkn5oNVSGw7Vmx7cnI/5u1C
# 1pEymuN8vTa3DeMcVOngt7k9wRLyA+OTzdlobO+o+Hfr2S9zFVh+7PVDC0wUzrTd
# W7u1c63IK89HPHnTc37lnGFQUGPXYKXIooN6UhIbHRDTUFVO/sH2LysKLRyFWEs8
# qdOsszmx2zL+WNKmjrBmQBvPzrV7IimT6rpmpCYxEnoY/bFvXsmK5avv/Osj+efn
# GkCc9hm7oLxy494MFy3u5S1XqkB0jweBCJo750sGG3N8QapKAicVYobSXBdeLxsm
# H82yzsczRNPRAdyAGbFIe5vVpl43OSwclH9gRpREbdE63Bb8Uvyn+8FqECSb1f7N
# uLXsMwue6nHTf7Lc/u48srSd6yRnY70CAwEAAaOCAdQwggHQMB8GA1UdIwQYMBaA
# FMn8EO9QyMtrf70IRO86YpPtW16aMB0GA1UdDgQWBBREAaQBaa2AQTUPKUUkMbf9
# S5f5sDA+BgNVHSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRwOi8v
# d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoG
# CCsGAQUFBwMDMIGXBgNVHR8EgY8wgYwwRKBCoECGPmh0dHA6Ly9jcmwzLmRpZ2lj
# ZXJ0LmNvbS9Hb0dldFNTTEc0Q1NSU0E0MDk2U0hBMjU2MjAyMkNBLTEuY3JsMESg
# QqBAhj5odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vR29HZXRTU0xHNENTUlNBNDA5
# NlNIQTI1NjIwMjJDQS0xLmNybDCBgwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzAB
# hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly9j
# YWNlcnRzLmRpZ2ljZXJ0LmNvbS9Hb0dldFNTTEc0Q1NSU0E0MDk2U0hBMjU2MjAy
# MkNBLTEuY3J0MAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggIBABqpRiImY/Bk
# WSYmqNEZsmjcqPNRBEp9bYFMylajgTFR7dOLqF9sCStkO+cs/X64RKHkZtlaCrQa
# ecZp6j/Dpq1fnkp2NfwuAaX2Osp/h6tMKnjaun3gvTdmI/j2iyJv6YJRxZ7daee7
# q9bkPzCTINwNc7AqWHbZ3Whlg+MmhLXaFHpoasR5JO9Vh+A8z8Y593G1bc7/Wjp2
# JWBNrCLjSUuz83YcDtf7yaURHuoJ96NDrHpbggaYU1s75sMhfBwAMTuYN2HmQe6/
# ShmDwGPtNzja0OtUdL0siHUp3Gouuqiux+ii7sjK0BDqvW3cbIQhCY41HkrJt4YF
# e+KAUzZXeger2mTb9StmWLTLuqTKvUnCpzsVlqEO1I6NwsXgV2u46/0EYqk42vBT
# e1vwXXj2Eawp5g4sSHQqbcxTAr/H5wZDIMp32tSyDMqV7zXhT6UWvxy5xua9Zkvf
# j7bn7CzUWgX/+GMyP8KlgXEDG5rCY1uSxleFBvn0ACZjWRp+kWl17WbdrIL22Kkl
# 2s1px/g9dfRbhHwNVAX1Mx2S7uCreeRs5qAA1cEjct2ulpNIbuPAEDDKenUHDTfN
# k0osUL9uaOjSx6HiVkcwPHgFzyMyu+SCVOUTpBhwBXIH+/r77s6XVnQWmjUHcZUY
# Y6KuwZiQ+E7joeAaZ0JGyW+LWx8WPfg7MYIGSjCCBkYCAQEwbjBaMQswCQYDVQQG
# EwJMVjEZMBcGA1UEChMQRW5WZXJzIEdyb3VwIFNJQTEwMC4GA1UEAxMnR29HZXRT
# U0wgRzQgQ1MgUlNBNDA5NiBTSEEyNTYgMjAyMiBDQS0xAhAGBSLrl94yV3C8/AHm
# MmZmMA0GCWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAw
# GQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisG
# AQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIJWvEBJRI/ox/bNd9mdTtedbuAzs612n
# CQM7ETab6WqEMA0GCSqGSIb3DQEBAQUABIICAKA27SVGKakXuIZzcpr5vuaYs4pq
# s7BRYrJvaYkmxQS1qAAt9ThaYFaG2pFYGGd1AV4lSP5r5zse2nKxLCaBiGgKdtle
# Lg6kQygHO2/kKi7/IkqIPmv+/oMmzdMt3dHsbrMKgrXtRIkFD49qiBpBEBgVLmDa
# /jZW/4COi8MqEXkD4dAcNrEFlYAdm+ewEjNBBsJb4TfO+ULQJBUaikyU1tLDo79L
# WAs/aKXcDej74RsyLaOIo7j8G1wjmyt0+CSmuROUPLBW60NIvs1CM0PfeX8TtgmT
# 355YIynpXuAAzrZMfnYFz4GoJbc+MKiHQ1AUY+Nh80dM3TKTKLvrGnhiKo47vXzV
# x1RGmPvYXkLRKECI6rvWLfQftz/gi3EbyibXsf0VHzfthkj4+wYKqX67CCSw8Oxt
# H7+hf0O3jcq8lTnB0PzbzmvPLZWKjICUViHTvjxbB6hJYnbKBgKa2f3RcT9OuCOd
# EBF/UjR3mxCQ/Pc5s4z8g1QemhN9vIBBzT9fkt6xIYKd95zxieZ8WGZe/viSP+ba
# +QXYaKrQ7CMk9o00Fr1WFINQIMNA/4//sQVGxLetCjog2wbBwx+1eT3WAfct+9l/
# md4XE6xt56cWLxVliRpMeWolx2mQ5WWdrFPv2mji4Xs2smiYaiv2UKrkZy1LWipr
# jlQ9yRXiGvAGjeWooYIDJjCCAyIGCSqGSIb3DQEJBjGCAxMwggMPAgEBMH0waTEL
# MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhE
# aWdpQ2VydCBUcnVzdGVkIEc0IFRpbWVTdGFtcGluZyBSU0E0MDk2IFNIQTI1NiAy
# MDI1IENBMQIQCoDvGEuN8QWC0cR2p5V0aDANBglghkgBZQMEAgEFAKBpMBgGCSqG
# SIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI2MDUwNDE5NDMy
# MFowLwYJKoZIhvcNAQkEMSIEIBzIF55mDN3K288B2JT+GKIh2K3LqsTvintz7P1r
# u/LeMA0GCSqGSIb3DQEBAQUABIICAH6NQhBh6aGFlH20G5G+uYPVXHF6V2syOSPk
# dWfaPwMubsm7zZmzNbpXbtXUs1iM61Z18Jv31T6sguUBBZGI1cYoQ5amWrmI0sOd
# f1+Fp332RwM8eMhyEsjJP5X/sSl6z+q/gH8hlou59W9DxRthtqHgyPhB5B3+XO2l
# KftvfOnKLiJ1U5C/zjcraV9jj+Bb1i8y19eORjh43Ea1EKrzLLZ8Rj4Iy2ttbXfS
# g+c8Tcos5qf9pS1aHyFMjhM73fUwJ1EY8h31QsRzRxZVy+eCjPmg+dxSveFp/uu/
# JI/BQ1K2dm3fo8XMs1q9OtCQtT9Arn4sLfOlW2vKdcry701yGX+/UNBzwpZMfpYF
# KNOSY628Au1YOtuTwz+mrks8PACU3+BWoXDEKlRyAbJ7zBTU/jQLH6WcA5gbs2D9
# SGkqr/WREhSwn3SeKCSOzavesW5iv8o1N+rq/8YBULYkGjoSYmdv7QvWOBEFF2HM
# xz1Mq4+0L3pAYIU3rXrkTn6WHPi1WDokeTf50J7z2/4TmNIp0rmjIoIJGXba/owp
# 0KroK5s5XYpKsXm/De2E9c1wVHddKvfn6b/hbYbGLM3gWJOQkqKIwkbR94VQ+SVS
# CYrjDFKxBTm5HZsmKqBs1/hm+69io0mD2Mm+Aa/fAcEDh9tmpG/8ueqmgYiaVJ+B
# VkO395RN
# SIG # End signature block