Private/System/Utilities/ReusableHelpers/RemoteWorkHelpers/Invoke-RemoteWorker.ps1
|
function Invoke-RemoteWorker { <# .SYNOPSIS Invokes a remote worker script on a specified PSSession. .DESCRIPTION This function stages helper scripts and worker scripts to a remote session, verifies their integrity, and executes the specified entry point with provided parameters. .PARAMETER Session The PSSession to use for remote execution. .PARAMETER HelpersZip Path to the helpers zip file to stage on the remote session. .PARAMETER HelpersZipHash SHA256 hash of the helpers zip file for integrity verification. .PARAMETER WorkerRemotePath Remote path for the worker scripts (currently unused). .PARAMETER WorkerLocalPath Local path to the worker scripts. .PARAMETER EntryPoint The entry point script to execute on the remote session. .PARAMETER EntryParameters Hashtable of parameters to pass to the entry point script. .PARAMETER ForceUpdate Switch to force update of the staged scripts (currently unused). #> [CmdletBinding()] param( [Parameter(Mandatory)] [System.Management.Automation.Runspaces.PSSession]$Session, [string]$HelpersZip, [string]$HelpersZipHash, # NOTE: Stages into $env:TEMP\TT_Worker_{guid}\workers. # WorkerRemotePath is kept for signature compatibility but isn't used. [Parameter(Mandatory)] [string]$WorkerRemotePath, [Parameter(Mandatory)] [string]$WorkerLocalPath, [Parameter(Mandatory)] [string]$EntryPoint, [Parameter()] [hashtable]$EntryParameters, # NOTE: currently unused (kept for compatibility) [Parameter()] [switch]$ForceUpdate = $true ) # StrictMode-friendly predefs (prevents "not set" reads during refactors) $remoteTmp = $null $remoteZip = $null $remoteHelpers = $null $remoteWorkers = $null $remoteWorkerPath = $null $expandedOk = $null $result = $null # 1) Create remote temp root $remoteTmp = Invoke-Command -Session $Session -ScriptBlock { $base = Join-Path $env:TEMP ("TT_Worker_{0}" -f ([guid]::NewGuid())) New-Item -ItemType Directory -Path $base -Force | Out-Null $base } -ErrorAction Stop try { # Canonical paths $remoteZip = Join-Path $remoteTmp 'helpers.zip' $remoteHelpers = Join-Path $remoteTmp 'helpers' $remoteWorkers = Join-Path $remoteTmp 'workers' if (-not $EntryParameters) { $EntryParameters = @{} } # Decide if helpers zip should be staged/expanded (worker-only packages supported) $stageHelpersZip = $false if (-not [string]::IsNullOrWhiteSpace($HelpersZip) -and (Test-Path -LiteralPath $HelpersZip)) { $zipInfo = Get-Item -LiteralPath $HelpersZip -ErrorAction Stop if ($zipInfo.Length -gt 0) { $stageHelpersZip = $true } } # 2–4) Helpers package stage (optional) if ($stageHelpersZip) { if ([string]::IsNullOrWhiteSpace($HelpersZipHash)) { throw "HelpersZipHash is required when HelpersZip is provided." } # 2) Push package zip Copy-Item -ToSession $Session -Path $HelpersZip -Destination $remoteZip -Force -ErrorAction Stop # 3) Verify + expand on remote into WORK ROOT $expandedOk = Invoke-Command -Session $Session -ScriptBlock { param($zipPath, $expectedHash, $workRoot) if (-not (Test-Path -LiteralPath $zipPath)) { throw "Remote zip not found: $zipPath" } $actual = (Get-FileHash -LiteralPath $zipPath -Algorithm SHA256).Hash if ($actual -ne $expectedHash) { throw "Hash mismatch for package. Expected $expectedHash, got $actual." } New-Item -ItemType Directory -Path $workRoot -Force | Out-Null try { if (Get-Command Expand-Archive -ErrorAction Ignore) { Expand-Archive -Path $zipPath -DestinationPath $workRoot -Force } else { Add-Type -AssemblyName System.IO.Compression.FileSystem [System.IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $workRoot, $true) } } catch { throw "Expand package failed: $($_.Exception.Message)" } $helpersDir = Join-Path $workRoot 'helpers' $workersDir = Join-Path $workRoot 'workers' # Some packages contain helper libs only; ensure standard folders exist. if (-not (Test-Path -LiteralPath $workersDir)) { New-Item -ItemType Directory -Path $workersDir -Force | Out-Null } if (-not (Test-Path -LiteralPath $helpersDir)) { # Create it so downstream logic can assume it exists New-Item -ItemType Directory -Path $helpersDir -Force | Out-Null } $true } -ArgumentList $remoteZip, $HelpersZipHash, $remoteTmp -ErrorAction Stop if (-not $expandedOk) { throw "Failed to expand package on remote." } # 4) Import helper libraries only (in-memory) + inject TT runtime vars Invoke-Command -Session $Session -ScriptBlock { param($helpersDir, $workersDir, $workRoot) # Keep behavior consistent even if zip omitted empty folders. if (-not (Test-Path -LiteralPath $workersDir)) { New-Item -ItemType Directory -Path $workersDir -Force | Out-Null } # helpers optional: create if missing if (-not (Test-Path -LiteralPath $helpersDir)) { New-Item -ItemType Directory -Path $helpersDir -Force | Out-Null } if (-not $script:TT) { $script:TT = [ordered]@{} } $script:TT.IsRemote = $true $script:TT.WorkRoot = $workRoot $script:TT.HelpersRoot = $helpersDir $script:TT.WorkersRoot = $workersDir $helperFiles = Get-ChildItem -LiteralPath $helpersDir -Filter '*.ps1' -File -ErrorAction SilentlyContinue | Sort-Object FullName foreach ($hf in $helperFiles) { $text = Get-Content -LiteralPath $hf.FullName -Raw -Encoding UTF8 . ([ScriptBlock]::Create($text)) } $true } -ArgumentList $remoteHelpers, $remoteWorkers, $remoteTmp -ErrorAction Stop | Out-Null } else { # Worker-only mode: create structure + set TT runtime vars, skip helpers zip entirely Invoke-Command -Session $Session -ScriptBlock { param($helpersDir, $workersDir, $workRoot) New-Item -ItemType Directory -Path $workRoot -Force | Out-Null New-Item -ItemType Directory -Path $helpersDir -Force | Out-Null New-Item -ItemType Directory -Path $workersDir -Force | Out-Null if (-not $script:TT) { $script:TT = [ordered]@{} } $script:TT.IsRemote = $true $script:TT.WorkRoot = $workRoot $script:TT.HelpersRoot = $helpersDir $script:TT.WorkersRoot = $workersDir $true } -ArgumentList $remoteHelpers, $remoteWorkers, $remoteTmp -ErrorAction Stop | Out-Null } # 5) Stage main worker into workroot\workers (Option B) if (-not $WorkerLocalPath) { throw "WorkerLocalPath must be provided when always-copy mode is enabled." } $remoteWorkerPath = Join-Path $remoteWorkers (Split-Path $WorkerLocalPath -Leaf) # Ensure workers dir exists (should, but be defensive) Invoke-Command -Session $Session -ScriptBlock { param($dir) if (-not (Test-Path -LiteralPath $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } } -ArgumentList $remoteWorkers -ErrorAction Stop Copy-Item -ToSession $Session -Path $WorkerLocalPath -Destination $remoteWorkerPath -Force -ErrorAction Stop # 6) Import worker (in-memory) & call entry function (Option B) $result = Invoke-Command -Session $Session -ScriptBlock { param($workerPath, $entry, $entryParams, $helpersDir, $workersDir, $workRoot) if (-not (Test-Path -LiteralPath $workerPath)) { throw "Worker not found: $workerPath" } if (-not $script:TT) { $script:TT = [ordered]@{} } $script:TT.IsRemote = $true $script:TT.WorkRoot = $workRoot $script:TT.HelpersRoot = $helpersDir $script:TT.WorkersRoot = $workersDir $script:TT.WorkerPath = $workerPath # Import worker INTO THIS SCOPE $workerText = Get-Content -LiteralPath $workerPath -Raw -Encoding UTF8 . ([ScriptBlock]::Create($workerText)) $fn = Get-Command -Name $entry -ErrorAction SilentlyContinue if (-not $fn) { throw "Entry function not found in worker: $entry" } # Filter entryParams to only what the entry function actually supports $filtered = @{} if ($entryParams) { foreach ($k in $entryParams.Keys) { if ($fn.Parameters.ContainsKey($k)) { $filtered[$k] = $entryParams[$k] } } $ignored = @($entryParams.Keys | Where-Object { -not $fn.Parameters.ContainsKey($_) }) if ($ignored.Count -gt 0) { Write-Verbose ("IgnoredEntryParams: {0}" -f ($ignored -join ', ')) } } # If the worker expects HelpersPath, default it to the staged helpers folder. if ($fn.Parameters.ContainsKey('HelpersPath')) { if ((-not $filtered.ContainsKey('HelpersPath')) -or [string]::IsNullOrWhiteSpace([string]$filtered.HelpersPath)) { $filtered.HelpersPath = $helpersDir } } & $entry @filtered } -ArgumentList $remoteWorkerPath, $EntryPoint, $EntryParameters, $remoteHelpers, $remoteWorkers, $remoteTmp -ErrorAction Stop -InformationAction Continue return $result } finally { # 7) Cleanup remote temp (best effort) try { Invoke-Command -Session $Session -ScriptBlock { param($root) if (Test-Path -LiteralPath $root) { Remove-Item -LiteralPath $root -Recurse -Force -ErrorAction SilentlyContinue } } -ArgumentList $remoteTmp -ErrorAction SilentlyContinue | Out-Null } catch { } } } # SIG # Begin signature block # MIIfAgYJKoZIhvcNAQcCoIIe8zCCHu8CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBRUnV5LnP/IA4S # wv91JRR0zuPZmH7hO+Oz+rWH+b4zUqCCGEowggUMMIIC9KADAgECAhAR+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 # AQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCA95XN8IcI/ # 7oJ8FB5V04Su3Uu5F2sDMoEHmr646AZWkjANBgkqhkiG9w0BAQEFAASCAgBeub6a # YAc79oh1/19vpUgu4ePr8ABUS705TPx7MnLVCh1KUdV+4e3JdY6gycr9zPTJQs/n # FNoyTAlt0vkN3cLYxdpt9nglKwkT0sZfcyDPPDKKy7eRoleAlfEGzNm38NKnBCZc # o7KZwTcNd/7gAYULbPozj4tfkuFmGHC44crvxEaUYTNJ5K74b5LEaHZYgFp74gIq # TdlCcnWibiF0spF5rlPwPwxdfrV3bboq7KFp5AyzhrnmUwHF3XtVcMtrDPhXQM10 # KaSlOgg35nyujCtDGNPX70Ag9SYPUmraWy1wQqM7n95VoQG/2D4rjhzZ+dOEx8Qf # e6HaUpzfwy7cq11dODmYYuLnl9gDsH/3LCnzfkkNd1Vt3jGJVH3HMprhRQkPG5/V # tax1R/FeKi04GpRWo08GHB3WzEmP8EoFKrDVvj09YJzapYJ7JlVE8vsvAeZe6J5U # j8Uc0OB/WhDDfE4TKvyCvtRpNGHgTaQhO/a65NaD3YgPyS/fqACbbxw2wTc3RE9Q # 8GfFJS1DKzC2SYvRSC99S8XJeP3i5yzaounXYEVoancES56ioN7T468wnvOYou+L # MMIT4wU89CaJLN2w/2LdepynHwlteNrklo+WOlnrBojHX8aZAmyXkl26Lg895x9S # Vra9hovAtODuuW4zzznldN8IhhKKsfhwmlvbXaGCAyYwggMiBgkqhkiG9w0BCQYx # ggMTMIIDDwIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg # SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcg # UlNBNDA5NiBTSEEyNTYgMjAyNSBDQTECEAqA7xhLjfEFgtHEdqeVdGgwDQYJYIZI # AWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJ # BTEPFw0yNjA0MjEyMDU0MTFaMC8GCSqGSIb3DQEJBDEiBCDr5asU+wDs+SpKL2Gw # o4wSfAd64aafcJgAE+83/nwB5TANBgkqhkiG9w0BAQEFAASCAgDEhERQu6LZAu4b # Od21fk4s1qa1hB6XClglyfCDkMRY9bsrtcnPtjKDff1sy8hhsGHUiprC3jVtSQfd # br8Xk4+j4RvvoHOlDkrzMV08G3rCBx2Cf29P73L6WwSwGvAE5Dxuw6PoLTlGNlm0 # 1I8OGSAB6FleaosE2M3NAVUc7ki14lwizglq6KBsb/wtGLQBEoGl7G6GGgZ+0DFo # 9OJ2yzIRROHuoKudxbh7ewQFbE2DWWlfaf5YKZtgtR1jcozsj8ctYULE2E+AAX/2 # Al211QY5bCcPjFckl144Y1QCnxkJBTDGzcrFo+A+6DJzrEnYR/PO0rl+9SNXSULo # gd6ex6GhjH1lwplfEsy9jE9UZb4s5eemzf/2EYvhC0UPTE429zmGULP3hoElPBAc # EIvYwSgbD+tb6hjRsywSmVwAz1LOEVemr1lm2xEAyb9BIUr8wg1AKsS8IAvC9whZ # MB5j/vDPplochbFz+815VJG+NYyW8MN7eDvURhI6H/BLGBB8YoptUJrzYmTXeB9i # NnX9EVnxCik7Gv7Ru8vMgPTR/9CELfZYab+mLoEvp2VGGPUG1LdmH4DY8W4pGP2O # d/9X/uwpP+5Z1XNIMql39X9u5BqEpl6wnayKNX9HISmmelx1NTUZtfvxPJKiEWxB # hgPX8m/1XT4kSxz2h/Wcxm7gfMEUMA== # SIG # End signature block |