#
.SYNOPSIS
Sets the recommended TrioFox performance tuning values
.DESCRIPTION
Sets the recommended TrioFox performance tuning values.
1. Adds/Updates these sections in "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\machine.config":
2. Adds/Updates this section in "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Aspnet.config":
3. Calls appcmd.exe to set appConcurrentRequestLimit = 50000
4. Updates these settings in TrioFox's web.config:
.EXAMPLE
> .\Set-TFPerfTuning.ps1
.NOTES
Author: Jeff Reed
Name: Set-TFPerfTuning.ps1
Created: 2019-07-22
Version History
2019-07-24 1.0.0 Initial version
2019-08-29 1.0.1 Backup applicationHost.config
2019-08-29 1.0.2 Fix comment based help
#>
#Requires -Version 5.1
#Requires -RunAsAdministrator
#region functions
#region New-XMLNode function
function New-XMLNode {
<#
.SYNOPSIS
Adds nodes if they don't exist in XML document
.DESCRIPTION
Adds nodes if they don't exist in XML document
.PARAMETER XML
An XML document passed in ByRef. The XML will be updated with new nodes.
.PARAMETER Parent
The parent node of the child node that will be added. This is passed ByRef
.PARAMETER Child
The child node that will be added
#>
Param
(
[Parameter(
Mandatory=$true,
ValueFromPipelineByPropertyName=$false
)]
[ref]
$XML,
[Parameter(
Mandatory=$true,
ValueFromPipelineByPropertyName=$false
)]
[ref]
$Parent,
[Parameter(
Mandatory=$true,
ValueFromPipelineByPropertyName=$false
)]
[String]
$Child
)
$node = $Parent.Value.SelectNodes($Child)
if ($node.Count -eq 0) {
$node = $XML.Value.CreateElement($Child)
[void] $Parent.Value.AppendChild($node)
}
$node
}
#endregion New-XMLNode function
#region New-WebConfigAppSetting
function New-WebConfigAppSetting {
<#
.SYNOPSIS
Adds (or updates) keys under the appSettings node of web.config
.DESCRIPTION
Adds (or updates) keys under the appSettings node of web.config
.PARAMETER XML
An XML document passed in ByRef. The XML will be updated with new keys.
.PARAMETER Key
The Key to be added, for example "HighDirWorkerCount".
.PARAMETER Value
The Value to be added (or updated), for example "20"
#>
Param(
[parameter(Mandatory=$true)]
[ref]
$XML,
[parameter(Mandatory=$true)]
[string]
$Key,
[parameter(Mandatory=$true)]
[string]
$Value
)
$node = $xml.Value.configuration.appSettings.add | Where-Object {$_.key -eq $Key}
if ($node) {
# node exists, update
$node.value = $Value
}
else {
# node does not exist, create
$newAppSetting = $xml.Value.CreateElement("add")
[void] $xml.Value.configuration.appSettings.AppendChild($newAppSetting)
$newAppSetting.SetAttribute("key", $Key)
$newAppSetting.SetAttribute("value", $Value)
}
}
#endregion New-WebConfigAppSetting function
#region Update-MachineCfg function
function Update-MachineCfg () {
# Defines the path to the .NET 4 machine.config file
$machineCfg = Join-Path $env:SystemRoot "\Microsoft.NET\Framework64\v4.0.30319\Config\machine.config"
if (-not (Test-Path $machineCfg)) {
Throw ("'{0}' does not exist!" -f $machineCfg)
}
# Read the file as an XML document
$xmlMachineCfg = [xml](Get-Content -Path $machineCfg)
# Make sure there is a top level configuration node, else throw an error
$configuration = $xmlMachineCfg.SelectNodes('/configuration')
if ($configuration.Count -eq 0) {
Throw ("'configuration' node does not exist in {0}" -f $machineCfg)
}
# Determine if /configuration/system.web node exists, if not create it
$systemWeb = New-XMLNode -XML ([ref] $xmlMachineCfg) -Parent ([ref] $configuration) -Child 'system.web'
# Determine if /configuration/system.web/processModel node exists, if not create it
$processModel = New-XMLNode -XML ([ref] $xmlMachineCfg) -Parent ([ref] $systemWeb) -Child 'processModel'
# Updated processModel attributes
$processModel.setattribute("autoConfig", $autoConfig)
$processModel.setattribute("maxWorkerThreads", $maxWorkerThreads)
$processModel.setattribute("maxIoThreads", $maxIoThreads)
$processModel.setattribute("minWorkerThreads", $minWorkerThreads)
# Determine if /configuration/system.net node exists, if not create it
$systemNet = New-XMLNode -XML ([ref] $xmlMachineCfg) -Parent ([ref] $configuration) -Child 'system.net'
# Determine if /configuration/system.net/connectionManagement node exists, if not create it
$connectionManagement = New-XMLNode -XML ([ref] $xmlMachineCfg) -Parent ([ref] $systemNet) -Child 'connectionManagement'
# Determine if /configuration/system.net/connectionManagement node exists, if not create it
$add = New-XMLNode -XML ([ref] $xmlMachineCfg) -Parent ([ref] $connectionManagement) -Child 'add'
$add.setattribute("address", "*")
$add.setattribute("maxconnection", $maxconnection)
# Backup the machine.config file, appending a timestamp to show when the change occurred
$now = Get-Date -f "yyyy-MM-dd_hh-mm-ss"
Copy-Item -Path $machineCfg -Destination ("{0}_{1}" -f $machineCfg, $now)
# Save the updated machine.config file
Write-Output ("`nSaving {0}`n" -f $machineCfg)
$xmlMachineCfg.Save($machineCfg)
}
#endregion Update-MachineCfg function
#region Update-AspNetCfg function
function Update-AspNetCfg () {
# Defines the path to the .NET 4 Aspnet.config file
$aspNetCfg = Join-Path $env:SystemRoot "\Microsoft.NET\Framework64\v4.0.30319\Aspnet.config"
if (-not (Test-Path $aspNetCfg)) {
Throw ("'{0}' does not exist!" -f $aspNetCfg)
}
# Read the file as an XML document
$xmlASPNetCfg = [xml](Get-Content -Path $aspNetCfg)
# Make sure there is a top level configuration node, else throw an error
$configuration = $xmlASPNetCfg.SelectNodes('/configuration')
if ($configuration.Count -eq 0) {
Throw ("'configuration' node does not exist in {0}" -f $machineCfg)
}
# Determine if /configuration/system.web node exists, if not create it
$systemWeb = New-XMLNode -XML ([ref] $xmlASPNetCfg) -Parent ([ref] $configuration) -Child 'system.web'
# Determine if /configuration/system.web/applicationPool node exists, if not create it
$applicationPool = New-XMLNode -XML ([ref] $xmlASPNetCfg) -Parent ([ref] $systemWeb) -Child 'applicationPool'
# Updated applicationPool attributes
$applicationPool.setattribute("maxConcurrentRequestsPerCPU", $maxConcurrentRequestsPerCPU)
$applicationPool.setattribute("requestQueueLimit", $requestQueueLimit)
# Backup the Aspnet.config file, appending a timestamp to show when the change occurred
$now = Get-Date -f "yyyy-MM-dd_hh-mm-ss"
Copy-Item -Path $aspNetCfg -Destination ("{0}_{1}" -f $aspNetCfg, $now)
# Save the updated Aspnet.config file
Write-Output ("Saving {0}`n" -f $aspNetCfg)
$xmlASPNetCfg.Save($aspNetCfg)
}
#endregion Update-AspNetCfg function
#region Start-Appcmd function
function Start-Appcmd () {
# Backup applicationHost.config
$appHostConfig = Join-Path $env:SystemRoot "\System32\inetsrv\config\applicationHost.config"
$now = Get-Date -f "yyyy-MM-dd_hh-mm-ss"
Copy-Item -Path $appHostConfig -Destination ("{0}_{1}" -f $appHostConfig, $now)
# Check appcmd.exe path
$appcmdExe = Join-Path $env:SystemRoot "\System32\inetsrv\appcmd.exe"
if (-not (Test-Path $appcmdExe)) {
Throw ("'{0}' does not exist!" -f $appcmdExe)
}
# "C:\Windows\System32\inetsrv\appcmd.exe" set config -section:system.webServer/serverRuntime /appConcurrentRequestLimit:50000
$argString = "set config `"Default Web Site`" -section:system.webServer/serverRuntime /enabled:`"True`" /appConcurrentRequestLimit:{0} /commit:apphost" -f $appConcurrentRequestLimit
$ArgList = $argString.Split(" ")
# Write install logs to %temp%
$fileErrLog = Join-Path $env:Temp "appcmd_StdErr.log"
$fileOutputLog = Join-Path $env:Temp "appcmd_StdOut.log"
Write-Output ("Executing: {0} {1}" -f $appCmdExe, $argString)
$process = Start-Process -FilePath $appcmdExe -ArgumentList $ArgList -Wait -PassThru -RedirectStandardOutput $fileOutputLog -RedirectStandardError $fileErrLog
if ($process.ExitCode -ne 0) {
Get-Content $fileErrLog
Throw $process.ExitCode
} else {
Get-Content $fileOutputLog
}
Write-Output ""
}
#endregion Start-Appcmd function
#region Update-WebCfg function
function Update-WebCfg () {
# Determine InstallDir
# This will return '32-bit' or '64-bit'
$osArchitecture = (Get-WmiObject -Class Win32_OperatingSystem).OSArchitecture
if ($osArchitecture -eq '64-bit') {
# 64 bit
$regKey = 'HKLM:\SOFTWARE\Wow6432Node\Gladinet\Enterprise'
} else {
# 32 bit
$regKey = 'HKLM:\SOFTWARE\Gladinet\Enterprise'
}
$installDir = (Get-ItemProperty -path $regKey -name "InstallDir").InstallDir
if ($Null -eq $installDir) {
Throw "Failed to retrieve InstallDir registry value. Perhaps TrioFox is not installed."
}
$webCfg = Join-Path $installDir "\root\web.config"
if (-not (Test-Path $webCfg)) {
Throw ("'{0}' does not exist!" -f $webCfg)
}
# Read the file as an XML document
$xmlWebCfg = [xml](Get-Content -Path $webCfg)
New-WebConfigAppSetting ([ref] $xmlWebCfg ) -Key 'HighDirWorkerCount' -Value $HighDirWorkerCount
New-WebConfigAppSetting ([ref] $xmlWebCfg ) -Key 'HighDirMaxQueueLength' -Value $HighDirMaxQueueLength
New-WebConfigAppSetting ([ref] $xmlWebCfg ) -Key 'LowDirWorkerCount' -Value $LowDirWorkerCount
New-WebConfigAppSetting ([ref] $xmlWebCfg ) -Key 'LowDirMaxQueueLength' -Value $LowDirMaxQueueLength
# Backup the web.config file, appending a timestamp to show when the change occurred
$now = Get-Date -f "yyyy-MM-dd_hh-mm-ss"
Copy-Item -Path $webCfg -Destination ("{0}_{1}" -f $webCfg, $now)
Write-Output ("Saving {0}`n" -f $webCfg)
$xmlWebCfg.Save($webCfg)
}
#endregion Update-WebCfg function
#endregion functions
#region script body
#region recommended variable values
# These variables are defined at the beginning of the script body in the event they need to be changed in the future.
# These are the recommended values for TrioFox tuning in .NET 4 machine.config (strings)
$autoConfig = "false"
$maxWorkerThreads = "500"
$maxIoThreads = "500"
$minWorkerThreads = "2"
$maxconnection = "20000"
# These are recommended values for TrioFox tuning in .NET 4 Aspnet.config (strings)
$maxConcurrentRequestsPerCPU = "5000"
$requestQueueLimit="20000"
# This is the recommended value for the appConcurrentRequestLimit (string value used in appcmd.exe command line)
$appConcurrentRequestLimit = "50000"
# These are the recommended web.config values (strings)
$HighDirWorkerCount = '20'
$HighDirMaxQueueLength ='35'
$LowDirWorkerCount = '10'
$LowDirMaxQueueLength = '15'
#endregion recommended variable values
# Each change is implemented as separate functions.
Update-MachineCfg
Update-AspNetCfg
Start-Appcmd
Update-WebCfg
# Reset IIS
iisreset
#endregion script body
# SIG # Begin signature block
# MIIkTgYJKoZIhvcNAQcCoIIkPzCCJDsCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUWsGZ+StwOg+T8ik1JQn0AU6P
# 5lCggh90MIID7jCCA1egAwIBAgIQfpPr+3zGTlnqS5p31Ab8OzANBgkqhkiG9w0B
# AQUFADCBizELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIG
# A1UEBxMLRHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhh
# d3RlIENlcnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcg
# Q0EwHhcNMTIxMjIxMDAwMDAwWhcNMjAxMjMwMjM1OTU5WjBeMQswCQYDVQQGEwJV
# UzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xMDAuBgNVBAMTJ1N5bWFu
# dGVjIFRpbWUgU3RhbXBpbmcgU2VydmljZXMgQ0EgLSBHMjCCASIwDQYJKoZIhvcN
# AQEBBQADggEPADCCAQoCggEBALGss0lUS5ccEgrYJXmRIlcqb9y4JsRDc2vCvy5Q
# WvsUwnaOQwElQ7Sh4kX06Ld7w3TMIte0lAAC903tv7S3RCRrzV9FO9FEzkMScxeC
# i2m0K8uZHqxyGyZNcR+xMd37UWECU6aq9UksBXhFpS+JzueZ5/6M4lc/PcaS3Er4
# ezPkeQr78HWIQZz/xQNRmarXbJ+TaYdlKYOFwmAUxMjJOxTawIHwHw103pIiq8r3
# +3R8J+b3Sht/p8OeLa6K6qbmqicWfWH3mHERvOJQoUvlXfrlDqcsn6plINPYlujI
# fKVOSET/GeJEB5IL12iEgF1qeGRFzWBGflTBE3zFefHJwXECAwEAAaOB+jCB9zAd
# BgNVHQ4EFgQUX5r1blzMzHSa1N197z/b7EyALt0wMgYIKwYBBQUHAQEEJjAkMCIG
# CCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMBIGA1UdEwEB/wQIMAYB
# Af8CAQAwPwYDVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NybC50aGF3dGUuY29tL1Ro
# YXd0ZVRpbWVzdGFtcGluZ0NBLmNybDATBgNVHSUEDDAKBggrBgEFBQcDCDAOBgNV
# HQ8BAf8EBAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFRpbWVTdGFtcC0y
# MDQ4LTEwDQYJKoZIhvcNAQEFBQADgYEAAwmbj3nvf1kwqu9otfrjCR27T4IGXTdf
# plKfFo3qHJIJRG71betYfDDo+WmNI3MLEm9Hqa45EfgqsZuwGsOO61mWAK3ODE2y
# 0DGmCFwqevzieh1XTKhlGOl5QGIllm7HxzdqgyEIjkHq3dlXPx13SYcqFgZepjhq
# IhKjURmDfrYwggSjMIIDi6ADAgECAhAOz/Q4yP6/NW4E2GqYGxpQMA0GCSqGSIb3
# DQEBBQUAMF4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3Jh
# dGlvbjEwMC4GA1UEAxMnU3ltYW50ZWMgVGltZSBTdGFtcGluZyBTZXJ2aWNlcyBD
# QSAtIEcyMB4XDTEyMTAxODAwMDAwMFoXDTIwMTIyOTIzNTk1OVowYjELMAkGA1UE
# BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMTQwMgYDVQQDEytT
# eW1hbnRlYyBUaW1lIFN0YW1waW5nIFNlcnZpY2VzIFNpZ25lciAtIEc0MIIBIjAN
# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomMLOUS4uyOnREm7Dv+h8GEKU5Ow
# mNutLA9KxW7/hjxTVQ8VzgQ/K/2plpbZvmF5C1vJTIZ25eBDSyKV7sIrQ8Gf2Gi0
# jkBP7oU4uRHFI/JkWPAVMm9OV6GuiKQC1yoezUvh3WPVF4kyW7BemVqonShQDhfu
# ltthO0VRHc8SVguSR/yrrvZmPUescHLnkudfzRC5xINklBm9JYDh6NIipdC6Anqh
# d5NbZcPuF3S8QYYq3AhMjJKMkS2ed0QfaNaodHfbDlsyi1aLM73ZY8hJnTrFxeoz
# C9Lxoxv0i77Zs1eLO94Ep3oisiSuLsdwxb5OgyYI+wu9qU+ZCOEQKHKqzQIDAQAB
# o4IBVzCCAVMwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAO
# BgNVHQ8BAf8EBAMCB4AwcwYIKwYBBQUHAQEEZzBlMCoGCCsGAQUFBzABhh5odHRw
# Oi8vdHMtb2NzcC53cy5zeW1hbnRlYy5jb20wNwYIKwYBBQUHMAKGK2h0dHA6Ly90
# cy1haWEud3Muc3ltYW50ZWMuY29tL3Rzcy1jYS1nMi5jZXIwPAYDVR0fBDUwMzAx
# oC+gLYYraHR0cDovL3RzLWNybC53cy5zeW1hbnRlYy5jb20vdHNzLWNhLWcyLmNy
# bDAoBgNVHREEITAfpB0wGzEZMBcGA1UEAxMQVGltZVN0YW1wLTIwNDgtMjAdBgNV
# HQ4EFgQURsZpow5KFB7VTNpSYxc/Xja8DeYwHwYDVR0jBBgwFoAUX5r1blzMzHSa
# 1N197z/b7EyALt0wDQYJKoZIhvcNAQEFBQADggEBAHg7tJEqAEzwj2IwN3ijhCcH
# bxiy3iXcoNSUA6qGTiWfmkADHN3O43nLIWgG2rYytG2/9CwmYzPkSWRtDebDZw73
# BaQ1bHyJFsbpst+y6d0gxnEPzZV03LZc3r03H0N45ni1zSgEIKOq8UvEiCmRDoDR
# EfzdXHZuT14ORUZBbg2w6jiasTraCXEQ/Bx5tIB7rGn0/Zy2DBYr8X9bCT2bW+IW
# yhOBbQAuOA2oKY8s4bL0WqkBrxWcLC9JG9siu8P+eJRRw4axgohd8D20UaF5Mysu
# e7ncIAkTcetqGVvP6KUwVyyJST+5z3/Jvz4iaGNTmr1pdKzFHTx/kuDDvBzYBHUw
# ggTTMIIDu6ADAgECAhAqkZhtgcjc5II2bCEKD4GAMA0GCSqGSIb3DQEBCwUAMH8x
# CzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0G
# A1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEwMC4GA1UEAxMnU3ltYW50ZWMg
# Q2xhc3MgMyBTSEEyNTYgQ29kZSBTaWduaW5nIENBMB4XDTE4MDUyMjAwMDAwMFoX
# DTIxMDYyMzIzNTk1OVowazELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB0Zsb3JpZGEx
# GDAWBgNVBAcMD0ZvcnQgTGF1ZGVyZGFsZTEXMBUGA1UECgwOR2xhZGluZXQsIElu
# Yy4xFzAVBgNVBAMMDkdsYWRpbmV0LCBJbmMuMIIBIjANBgkqhkiG9w0BAQEFAAOC
# AQ8AMIIBCgKCAQEAwO6vB0l/6YHY+R9DNqPYiA+x6IWGL6TQEdumm4G42obsmCpm
# tyo037j4dIdBrQT42PPicQB8j5/jX+y/L+RrgE5jYOcQTTGCW71ZboqGudjmJbkK
# iXKpDIF+QmNWPfWBIPx58l9pm5JNeZYApHJSN0tv7XtTzFgXj6rgNpIHGogy8oa8
# BL2Q/f4DMgQ3/cEiYpTZsLlq2ZZJBChKQasswU29VA5xmhuhhOXIfNnJgFITijDa
# /R7ebrD4Z4NJzjI2Fi2z6jQ7AMtnZ21xnAywU79wMuKVCmhRqXRhAkiCLtsWitVX
# GtxEu6OG9dvTW/2ipqVm1923aAh2qs6a18QK9QIDAQABo4IBXTCCAVkwCQYDVR0T
# BAIwADAOBgNVHQ8BAf8EBAMCB4AwKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3N2
# LnN5bWNiLmNvbS9zdi5jcmwwYQYDVR0gBFowWDBWBgZngQwBBAEwTDAjBggrBgEF
# BQcCARYXaHR0cHM6Ly9kLnN5bWNiLmNvbS9jcHMwJQYIKwYBBQUHAgIwGQwXaHR0
# cHM6Ly9kLnN5bWNiLmNvbS9ycGEwEwYDVR0lBAwwCgYIKwYBBQUHAwMwVwYIKwYB
# BQUHAQEESzBJMB8GCCsGAQUFBzABhhNodHRwOi8vc3Yuc3ltY2QuY29tMCYGCCsG
# AQUFBzAChhpodHRwOi8vc3Yuc3ltY2IuY29tL3N2LmNydDAfBgNVHSMEGDAWgBSW
# O1PweTOXr32D7y4rzMq3hh5yZjAdBgNVHQ4EFgQUK00KOFsRE4eer7ziBf8iSGJ3
# myYwDQYJKoZIhvcNAQELBQADggEBAHdBodwEeCdWcr0CULLPuBznC+rtOIKO5QQv
# IBfsISNLsXvr26jHuKm/hyon3FzzxceINxYeIEf6NxXV59mg4Wcm6UTYmLAPhEog
# +YNFWN2/GY42A713bG4b7rp9tqfAWYeMB8tFRip9kvzgJQmPKR3y0s9K/22jNN3v
# m6WA5vKJQouzmFJA7IQH17vU3N6gCljOYu0vknMm+0N43E3wUhvwfy5b8KQJLaFz
# 6TOQDZZPTnEkOs7l+E+AgX3wB6U7DwjKWWtp58s9ec8av/+/puaVU4LQoJEla8pb
# EIT7A1Hlc2n6LTKCRn0bNZ6K+F5kaBifAXNxc8NCovP2tjLAz94wggVZMIIEQaAD
# AgECAhA9eNf5dklgsmF99PAeyoYqMA0GCSqGSIb3DQEBCwUAMIHKMQswCQYDVQQG
# EwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWdu
# IFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA2IFZlcmlTaWduLCBJbmMu
# IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENs
# YXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH
# NTAeFw0xMzEyMTAwMDAwMDBaFw0yMzEyMDkyMzU5NTlaMH8xCzAJBgNVBAYTAlVT
# MR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50
# ZWMgVHJ1c3QgTmV0d29yazEwMC4GA1UEAxMnU3ltYW50ZWMgQ2xhc3MgMyBTSEEy
# NTYgQ29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
# AQEAl4MeABavLLHSCMTXaJNRYB5x9uJHtNtYTSNiarS/WhtR96MNGHdou9g2qy8h
# UNqe8+dfJ04LwpfICXCTqdpcDU6kDZGgtOwUzpFyVC7Oo9tE6VIbP0E8ykrkqsDo
# OatTzCHQzM9/m+bCzFhqghXuPTbPHMWXBySO8Xu+MS09bty1mUKfS2GVXxxw7hd9
# 24vlYYl4x2gbrxF4GpiuxFVHU9mzMtahDkZAxZeSitFTp5lbhTVX0+qTYmEgCscw
# dyQRTWKDtrp7aIIx7mXK3/nVjbI13Iwrb2pyXGCEnPIMlF7AVlIASMzT+KV93i/X
# E+Q4qITVRrgThsIbnepaON2b2wIDAQABo4IBgzCCAX8wLwYIKwYBBQUHAQEEIzAh
# MB8GCCsGAQUFBzABhhNodHRwOi8vczIuc3ltY2IuY29tMBIGA1UdEwEB/wQIMAYB
# Af8CAQAwbAYDVR0gBGUwYzBhBgtghkgBhvhFAQcXAzBSMCYGCCsGAQUFBwIBFhpo
# dHRwOi8vd3d3LnN5bWF1dGguY29tL2NwczAoBggrBgEFBQcCAjAcGhpodHRwOi8v
# d3d3LnN5bWF1dGguY29tL3JwYTAwBgNVHR8EKTAnMCWgI6Ahhh9odHRwOi8vczEu
# c3ltY2IuY29tL3BjYTMtZzUuY3JsMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEF
# BQcDAzAOBgNVHQ8BAf8EBAMCAQYwKQYDVR0RBCIwIKQeMBwxGjAYBgNVBAMTEVN5
# bWFudGVjUEtJLTEtNTY3MB0GA1UdDgQWBBSWO1PweTOXr32D7y4rzMq3hh5yZjAf
# BgNVHSMEGDAWgBR/02Wnwt3su/AwCfNDOfoCrzMxMzANBgkqhkiG9w0BAQsFAAOC
# AQEAE4UaHmmpN/egvaSvfh1hU/6djF4MpnUeeBcj3f3sGgNVOftxlcdlWqeOMNJE
# WmHbcG/aIQXCLnO6SfHRk/5dyc1eA+CJnj90Htf3OIup1s+7NS8zWKiSVtHITTuC
# 5nmEFvwosLFH8x2iPu6H2aZ/pFalP62ELinefLyoqqM9BAHqupOiDlAiKRdMh+Q6
# EV/WpCWJmwVrL7TJAUwnewusGQUioGAVP9rJ+01Mj/tyZ3f9J5THujUOiEn+jf0o
# r0oSvQ2zlwXeRAwV+jYrA9zBUAHxoRFdFOXivSdLVL4rhF4PpsN0BQrvl8OJIrEf
# d/O9zUPU8UypP7WLhK9k8tAUITCCBZowggOCoAMCAQICCmEZk+QAAAAAABwwDQYJ
# KoZIhvcNAQEFBQAwfzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEpMCcGA1UEAxMgTWljcm9zb2Z0IENvZGUgVmVyaWZpY2F0aW9uIFJvb3QwHhcN
# MTEwMjIyMTkyNTE3WhcNMjEwMjIyMTkzNTE3WjCByjELMAkGA1UEBhMCVVMxFzAV
# BgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO
# ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh
# dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1
# YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0G
# CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7
# K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70PbZmIV
# Yc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBRTdGJ
# aXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/Arr0
# PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxp
# g8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGj
# gcswgcgwEQYDVR0gBAowCDAGBgRVHSAAMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0P
# BAQDAgGGMB0GA1UdDgQWBBR/02Wnwt3su/AwCfNDOfoCrzMxMzAfBgNVHSMEGDAW
# gBRi+wohW39DbhHaCVRQa/XSlnHxnjBVBgNVHR8ETjBMMEqgSKBGhkRodHRwOi8v
# Y3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNyb3NvZnRDb2Rl
# VmVyaWZSb290LmNybDANBgkqhkiG9w0BAQUFAAOCAgEAgSqCFow0ZyvlA+s0e4yi
# o1CK9FWG8R6Mjq597gMZznKVGEitYhH9IP0/RwYBWuLgb4wVLE48alBsCzajz3oN
# nEK8XPgZ1WDjaebiI0FnjGiDdiuPk6MqtX++WfupybImj8qi84IbmD6RlSeXhmHu
# W10Ha82GqOJlgKjiFeKyviMFaroM80eTTaykjAd5OcBhEjoFDYmj7J9XiYT77Mp8
# R2YUkdi2Dxld5rhKrLxHyHFDluYyIKXcd4b9POOLcdt7mwP8tx0yZOsWUqBDo/ou
# rVmSTnzH8jNCSDhROnw4xxskIihAHhpGHxfbGPfwJzVsuGPZzblkXSulXu/GKbTy
# x/ghzAS6V/0BtqvGZ/nn05l/9PUi+nL1/f86HEI6ofmAGKXujRzUZp5FAf6q7v/7
# F48w9/HNKcWd7LXVSQA9hbjLu5M6J2pJwDCuZsn3Iygydvmkg1bISM5alqqgzAzE
# f7SOl69t41Qnw5+GwNbkcwiXBdvQVGJeA0jC1Z9/p2aM0J2wT9TTmF9Lesl/silS
# 0BKAxw9Uth5nzcagbBEDhNNIdecq/rA7bgo6pmt2mQWj8XdoYTMURwb8U39SvZIU
# XEokameMr42QqtD2eSEbkyZ8w84evYg4kq5FxhlqSVCzBfiuWTeKaiUDlLFZgVDo
# uoOAtyM19Ha5Zx1ZGK0gjZQwggcFMIIE7aADAgECAhBylAQQHz4Mo0eDf8oXWoQ4
# MA0GCSqGSIb3DQEBBQUAMH8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n
# dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y
# YXRpb24xKTAnBgNVBAMTIE1pY3Jvc29mdCBDb2RlIFZlcmlmaWNhdGlvbiBSb290
# MB4XDTA1MTEwMTEzNDY0NloXDTI1MTEwMTEzNTQwM1owfzELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UEAxMgTWljcm9zb2Z0IENvZGUg
# VmVyaWZpY2F0aW9uIFJvb3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
# AQC9d8kcfxV4OMUHQyFa++TMO8ZVMfwhibG85wGc+5C+IBFVdqdNAuey9C6N77KH
# RlbKR87Iw2PjCANLlga5cCJE5kt7RD91t7imK5EIQe9LB1nWpBmd9suku44CZU3K
# 3g+0kCLxtWtcIvbK+TiqKAsGLTwZjbc1X4Pt3WVzhEaSn0TiiUqM1ZinbT3oGctE
# rRgL6lxffAvDmpNoRPO2v5eZMHI/KFnQcMgFV3j1SoI0CiTBerBkpTpuEtUDYTi7
# Di39hZzWSHVqHLKi6JH6t+T1PF/9yUCsx6BC9XTYudvX/nN3GuDEtwmxBZpt416A
# OHV4UrYS03muQ/dlp9EWZGmFj3g6uJS/RRJiWk2HSNb4GbxZAQb1GttgKZ8BP25z
# +f2ARc6V14r2kgzBc0AsbaoypvF/MPiQ8a5FJ7m0DjACvcYO7DyMW7Y0hc8UCwxQ
# DaniWZEuqAE59CwVYwSAuEDfYvf+t0wTqCypZhM4YvxAcGJ7dXfVK44bpZnlubfH
# reoBoCV7WEZSVlSiyZIrWB1IUcAf/jcA0eKrEMKpWelCmW6PtR5HZnQemHZXVwRe
# vS+Fk9UOC58ueyZkp4YSCVBj59HHjn4OOwfnu+TNGkDUeroFWUrW0O7cll4iSicc
# RePe2rLp00P96W/AyX0f/Z+QnIYgCMx03ECnKbOrWGVrsQIDAQABo4IBezCCAXcw
# CwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFGL7CiFbf0Nu
# EdoJVFBr9dKWcfGeMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly9jcmwubWljcm9z
# b2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY3Jvc29mdENvZGVWZXJpZlJvb3Qu
# Y3JsMBAGCSsGAQQBgjcVAQQDAgEAMHQGA1UdIARtMGswaQYJKwYBBAGCNxUvMFww
# WgYIKwYBBQUHAgIwTh5MAEMAbwBwAHkAcgBpAGcAaAB0ACAAqQAgADIAMAAwADUA
# IABNAGkAYwByAG8AcwBvAGYAdAAgAEMAbwByAHAAbwByAGEAdABpAG8AbjBZBggr
# BgEFBQcBAQRNMEswSQYIKwYBBQUHMAKGPWh0dHA6Ly93d3cubWljcm9zb2Z0LmNv
# bS9wa2kvY2VydHMvTWljcm9zb2Z0Q29kZVZlcmlmUm9vdC5jcnQwDQYJKoZIhvcN
# AQEFBQADggIBAGFC6OsYyHHcRhlpbeoOzThnrD6nB/ZNFjyeV/u4uKfHhwnmY1fQ
# PUaQUPUFmcnKdDr7Aa16TWqrhOBAU1fInNUENoK4tx7Gh5nC2McgnsbyPlX5j3RE
# Ud0rttAU3cbe0sX/ha8XuaB12sYN4kV2lhrLPlP76TRbMgMjbL/Yfpo9hQ/IFTud
# AFZPr92xJRygzClmG0fOe2fvZO9IR5LenRRQabhfgqnyrZMrU++rYjI36r8EC0br
# GYSmqkmgbcGrLYNBarTWQ3vSO3oM0m3QDSDcW8Ipp/iCJCIWATXYGtZEItxHZ1am
# aC7/yWaf+3+0ZMYeZ3bgMS462XMNZ5nI9WEeXcGnuI8xHzjf47OHTE2zxPZgXKys
# DaAsArnv8KNdJ72WfQ3Q1EAG0aTWy26z1Tb+SMjyOCZWOiBqpMMASOcYKeero6t8
# B6R/pWtKugI/hpdduW5ZJOB8j+3vw8+qrxVRP9/3KHhd600fXTQImzQ7KqHIkQl9
# U8H/fYAf4Gso1LndWHQhuJ8tvRU6TkVKXMOsfAey0CEVuM9p2hTkLCT7ZuUBnhpm
# tMStTSutYGLk5I/qNw3K2Dbcf9sgejPAStYfLeCFziuhktdUUyBhhnw3AwOM/ENH
# 5PhIi6waQuqYgl1iU4FrU0YwsueFq7wYnIaWWYa2Gq/6/UgxJFwBHLscMYIERDCC
# BEACAQEwgZMwfzELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBv
# cmF0aW9uMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMTAwLgYDVQQD
# EydTeW1hbnRlYyBDbGFzcyAzIFNIQTI1NiBDb2RlIFNpZ25pbmcgQ0ECECqRmG2B
# yNzkgjZsIQoPgYAwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAIoAKAAKEC
# gAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwG
# CisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFEo+GjJq/FI6nP8MVBHtnzAZetk6
# MA0GCSqGSIb3DQEBAQUABIIBAAYf/sH4lIBzVVhcxP+uYA6Gb/m+ulrIXuDB3FS9
# xDwRevr9771LcWp2WVqJ0Yhj5BgYI0hBWZESCrfOPuWdMSiZ6XgsSuwoCHGGEpmF
# qBP1RkEooCHOD+3s3G+8UDpF11Tb2dWRp+XXy/ossOcwG/gQyMpUra9Gc4qoz+Mk
# NlhE4bQhyfQVdxLQz5BwzCDkDCv3a4A1PFqy80dSgssD1pdEomjQJkAY+NqfuEzO
# rpv7gE3RBawV89bmp4MsrLzQYP0v0vWzt3gecbheW3s1zzSZuwZr3H4sQyTePtjf
# Y56gqd9FOyMErOEcm2M0qCeMqcB44uDCnW7sP+DDyN0fbbehggILMIICBwYJKoZI
# hvcNAQkGMYIB+DCCAfQCAQEwcjBeMQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3lt
# YW50ZWMgQ29ycG9yYXRpb24xMDAuBgNVBAMTJ1N5bWFudGVjIFRpbWUgU3RhbXBp
# bmcgU2VydmljZXMgQ0EgLSBHMgIQDs/0OMj+vzVuBNhqmBsaUDAJBgUrDgMCGgUA
# oF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTkx
# MDI0MTgxODI0WjAjBgkqhkiG9w0BCQQxFgQUAQsW5S3qolIlcfn3r4+NqWCDwcww
# DQYJKoZIhvcNAQEBBQAEggEAGkCwd8MNtUY9D0i3wNY1e3SKjr/CHeqfA7ryNt7T
# 404uvy5eJdjnjtq2qflk/OEDLo2rTs5xfpcVrlUipYhp3Z2DEUXUZdAzTcx0VOeU
# G9uCXeazUMIHDhndAozEvBmja0o+iAXhzH5VPeWB/g6+5+iLOPDdPP7Djh4x+I7e
# wMeZc3O/knftdL4Y+1q9pUIvhJXuaH80KGbRSXwergvhAZBHVFosasG/X0B6dbmw
# oqC3caRDAhEsU3cxPyKfNffJ4F9t7rqph+FUOw5FEyGGwks/VQAYqstYAfW09GpC
# BQ6UcV3tx5NkVAggFdxIIHY3hobwvDDZpXcdTU99tmosMQ==
# SIG # End signature block