ServerHealth.psm1

<#
    Copyright Microsoft Corporation. All rights reserved.
    Licensed under the MIT License.
#>


Set-StrictMode -Version Latest
Import-LocalizedData -BindingVariable message -ErrorAction Stop

$versionId = '1.0.2.1810'
$controlId = @{ Start = 'start'; Stop = 'stop'; Cancel = 'cancel' }

<#
    .Synopsis
    Captures a performance trace on a Windows Server system using the Windows Performance Recorder.
 
    .Description
    The Trace-Performance cmdlet captures a performance trace on a Windows Server system by utilizing the Windows Performance Recorder. The cmdlet supports using either a time-based capture or can be controlled manually by specifying both start and stop options. The default capture uses a time-based method. The captured trace can be used to understand performance problems.
 
    .Parameter Profile
    Specifies the name of the profile to use to capture a performance trace. If this parameter is omitted, the Trace-Performance cmdlet uses the General profile.
 
    .Parameter Duration
    Specifies the duration in seconds that the performance trace should be captured. If this parameter is omitted, a default period of 30 seconds is used.
 
    .Parameter Detail
    Specifies the level of detail that the performance trace should capture. If this parameter is omitted, the level of detail is set to Low. Use caution when selecting a higher level as it may have a performance impact.
 
    .Parameter Mode
    Specifies the recording mode that is used to capture the performance trace. The cmdlet supports both Memory and File modes. If this parameter is omitted, the default is Memory mode.
 
    .Parameter Start
    Starts a performance trace on a Windows Server system using the Windows Performance Recorder.
 
    .Parameter Stop
    Stops capturing a Windows Server performance trace.
 
    .Inputs
    None
 
    .Outputs
    System.IO.FileInfo
        An object that represents a captured trace is returned if a time-based capture or a manual stop is specified.
 
    .Example
    C:\PS> Trace-Performance
 
    Mode LastWriteTime Length Name
    ---- ------------- ------ ----
    -a---- 6/2/2018 8:00 AM 1070138 TracePerformance{546F937E-923D-57E7-7BE3-135DC4C3EF38}.etl
 
    This example uses the General profile to capture a 30-second performance trace.
 
    .Example
    C:\PS> Trace-Performance -Profile Networking -Duration 10 -Detail High
 
    Mode LastWriteTime Length Name
    ---- ------------- ------ ----
    -a---- 6/2/2018 8:00 AM 1866958 TracePerformance{546F937E-923D-57E7-7BE3-135DC4C3EF38}.etl
 
    This example uses the Networking profile with a High level of detail to capture a 10-second performance trace.
 
    .Example
    C:\PS> Trace-Performance -Profile Storage -Start
 
    This example starts a manual performance trace that uses the Storage profile.
 
    .Example
    C:\PS> Trace-Performance -Stop
 
    Mode LastWriteTime Length Name
    ---- ------------- ------ ----
    -a---- 6/2/2018 8:00 AM 986283 TracePerformance{546F937E-923D-57E7-7BE3-135DC4C3EF38}.etl
 
    This example stops a manually initiated performance trace.
 
    .Link
    https://go.microsoft.com/fwlink/?linkid=2009254
#>

function Trace-Performance
{
    [CmdletBinding(DefaultParameterSetName = 'Trace')]
    [OutputType([System.IO.FileInfo], ParameterSetName = 'Stop')]
    [OutputType([System.IO.FileInfo], ParameterSetName = 'Trace')]
    [OutputType('None', ParameterSetName = 'Start')]
    Param
    (
        [Parameter(ParameterSetName = 'Trace')]
        [Parameter(ParameterSetName = 'Start')]
        [ValidateSet('Custom', 'General', 'Networking', 'Storage')]
        [string]$Profile = 'General',

        [Parameter(ParameterSetName = 'Trace')]
        [ValidateRange(1, 300)]
        [int]$Duration = 30,

        [Parameter(ParameterSetName = 'Trace')]
        [Parameter(ParameterSetName = 'Start')]
        [ValidateSet('Low', 'High')]
        [string]$Detail = 'Low',

        [Parameter(ParameterSetName = 'Trace')]
        [Parameter(ParameterSetName = 'Start')]
        [ValidateSet('File', 'Memory')]
        [string]$Mode = 'Memory',

        [Parameter(ParameterSetName = 'Start', Mandatory = $true)]
        [switch]$Start,

        [Parameter(ParameterSetName ='Stop', Mandatory = $true)]
        [switch]$Stop
    )

    $recorderProcess = Get-Command -Name 'wpr.exe' -ErrorAction SilentlyContinue

    if ($recorderProcess -eq $null)
    {
        throw $message.RecorderNotInstalled
    }

    if ($recorderProcess.FileVersionInfo.FileMajorPart -lt 10)
    {
        throw $message.IncorrectRecorderVersion
    }

    if (-not (Test-IsAdministrator))
    {
        throw $message.InsufficientPrivileges
    }

    $settings = Get-TraceProfileParameters -TraceType 'TracePerformance' -ProfileName $Profile
    $parameters = $settings.RecorderParameters

    try
    {
        if ($PSCmdlet.ParameterSetName -eq 'Start' -or $PSCmdlet.ParameterSetName -eq 'Trace')
        {
            $null = Control-WindowsRecorder -Action $controlId.Cancel -Status $message.ResettingRecorder `
                                            -PercentComplete 0
        }
    }
    catch
    {
        throw $message.UnableToLaunchRecorder
    }

    try
    {
        if ($PSCmdlet.ParameterSetName -eq 'Start' -or $PSCmdlet.ParameterSetName -eq 'Trace')
        {
            $offset = @{ $true = 100 / ($Duration + 1); $false = 100 }[$PSCmdlet.ParameterSetName -eq 'Trace']
            $completedTracing = $false

            $startedTracing = Control-WindowsRecorder -Action $controlId.Start -Status $message.StartingRecorder `
                                                      -PercentComplete $offset -Parameters $parameters

            if ($PSCmdlet.ParameterSetName -eq 'Trace' -and $startedTracing)
            {
                Wait-ForDuration -Duration $Duration -Status $message.TracingPerformance
                $completedTracing = $true
            }
        }
    }
    finally
    {
        if ($PSCmdlet.ParameterSetName -eq 'Stop' -or $PSCmdlet.ParameterSetName -eq 'Trace')
        {
            if ($PSCmdlet.ParameterSetName -eq 'Stop' -or $completedTracing -eq $true)
            {
                $parameters = Get-RecorderParameters $settings.TraceFile
                $null = Control-WindowsRecorder -Action $controlId.Stop -Status $message.StoppingRecorder `
                                                -PercentComplete 100 -Parameters $parameters
            }
            else
            {
                $null = Control-WindowsRecorder -Action $controlId.Cancel -Status $message.CancelingRecorder `
                                                -PercentComplete 100
            }
        }
    }

    if ($PSCmdlet.ParameterSetName -eq 'Stop' -or $PSCmdlet.ParameterSetName -eq 'Trace')
    {
        if (Test-Path -Path $settings.TraceFile)
        {
            return [System.IO.FileInfo]$settings.TraceFile
        }

        throw $message.UnableToRecordCapture
    }
}

function Get-TraceProfileParameters($traceType, $profileName)
{
    $traceFile = Get-TraceName -TraceType $traceType
    $traceProfile = Get-TraceProfile -ProfileName $profileName

    $recorderParameters = '"' + (Get-RecorderProfile -TraceProfile $traceProfile) + '"'

    if ($Mode -eq 'File')
    {
        $recorderParameters += ' -filemode'
    }

    return @{ TraceFile = $traceFile; RecorderParameters = $recorderParameters }
}

function Get-TraceProfile($profileName)
{
    $traceProfile = Get-TraceProfileContent -ProfileName $profileName

    if ($traceProfile.Performance.EventTracing -eq $null)
    {
        throw $message.MalformedProfile
    }

    return $traceProfile
}

function Get-TraceProfileContent($profileName)
{
    $profilePath = Get-TraceProfilePath -ProfileName $profileName
    $content = Get-Content -Path $profilePath -Raw -ErrorAction Stop

    return & ([ScriptBlock]::Create($content))
}

function Get-TraceName($traceType)
{
    $traceFile = "$traceType{$([Guid]::NewGuid().ToString().ToUpper())}.etl"
    return Join-Path -Path (Convert-Path -Path '.') -ChildPath $traceFile
}

function Get-TraceProfilePath($profileName)
{
    return Join-Path -Path $PSScriptRoot -ChildPath "Profile-$profileName.ps1"
}

function Get-RecorderParameters($traceFile)
{
    $marker =  @{ $true = 'M' ; $false = $Duration }[$PSCmdlet.ParameterSetName -eq 'Stop']
    return '"' + $traceFile + '"' + ' ' + ($message.TelemetryRecorderMarker -f $versionId, $marker)
}

function Get-RecorderProfile($traceProfile)
{
    $recorderProfile = Join-Path -Path $PSScriptRoot -ChildPath $traceProfile.Performance.EventTracing
    return $recorderProfile + @{ $true = '.Light' ; $false = '.Verbose' }[$Detail -eq 'Low']
}

function Control-WindowsRecorder($action, $status, $percentComplete, $parameters = $null)
{
    $startInfo = New-Object -TypeName System.Diagnostics.ProcessStartInfo

    $startInfo.FileName = $recorderProcess.Name
    $startInfo.Arguments = '-' + $action + ' ' + $parameters

    $startInfo.RedirectStandardError = $true
    $startInfo.UseShellExecute = $false

    Update-Progress -Status $status -PercentComplete $percentComplete
    return Start-WindowsRecorder -StartInfo $startInfo
}

function Start-WindowsRecorder($startInfo)
{
    $process = New-Object -TypeName System.Diagnostics.Process
    $process.StartInfo = $startInfo

    $process.Start()
    $process.WaitForExit()

    if ($process.ExitCode -eq 0) { return $true }
    return $false
}

function Test-IsAdministrator
{
    return ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()). `
             IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}

function Update-Progress($status, $percentComplete)
{
    Write-Progress -Activity $message.WindowsServerDiagnosticsPerformance -Status $status `
                   -PercentComplete $percentComplete
}

function Wait-ForDuration($duration, $status)
{
    for ($i = 1; $i -ne $duration; $i++)
    {
        $interval = 100 / ($duration + 1)
        Update-Progress -Status $status -PercentComplete ($i * $interval + $interval)
        Start-Sleep -Seconds 1
    }
}

Export-ModuleMember -Function Trace-Performance

# SIG # Begin signature block
# MIIkmgYJKoZIhvcNAQcCoIIkizCCJIcCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC0H0kefQhOisoR
# o76vmbFZFyP9NEMRs7JuQoH5quQEa6CCDYEwggX/MIID56ADAgECAhMzAAABA14l
# HJkfox64AAAAAAEDMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMTgwNzEyMjAwODQ4WhcNMTkwNzI2MjAwODQ4WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDRlHY25oarNv5p+UZ8i4hQy5Bwf7BVqSQdfjnnBZ8PrHuXss5zCvvUmyRcFrU5
# 3Rt+M2wR/Dsm85iqXVNrqsPsE7jS789Xf8xly69NLjKxVitONAeJ/mkhvT5E+94S
# nYW/fHaGfXKxdpth5opkTEbOttU6jHeTd2chnLZaBl5HhvU80QnKDT3NsumhUHjR
# hIjiATwi/K+WCMxdmcDt66VamJL1yEBOanOv3uN0etNfRpe84mcod5mswQ4xFo8A
# DwH+S15UD8rEZT8K46NG2/YsAzoZvmgFFpzmfzS/p4eNZTkmyWPU78XdvSX+/Sj0
# NIZ5rCrVXzCRO+QUauuxygQjAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUR77Ay+GmP/1l1jjyA123r3f3QP8w
# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDM3OTY1MB8GA1UdIwQYMBaAFEhu
# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAn/XJ
# Uw0/DSbsokTYDdGfY5YGSz8eXMUzo6TDbK8fwAG662XsnjMQD6esW9S9kGEX5zHn
# wya0rPUn00iThoj+EjWRZCLRay07qCwVlCnSN5bmNf8MzsgGFhaeJLHiOfluDnjY
# DBu2KWAndjQkm925l3XLATutghIWIoCJFYS7mFAgsBcmhkmvzn1FFUM0ls+BXBgs
# 1JPyZ6vic8g9o838Mh5gHOmwGzD7LLsHLpaEk0UoVFzNlv2g24HYtjDKQ7HzSMCy
# RhxdXnYqWJ/U7vL0+khMtWGLsIxB6aq4nZD0/2pCD7k+6Q7slPyNgLt44yOneFuy
# bR/5WcF9ttE5yXnggxxgCto9sNHtNr9FB+kbNm7lPTsFA6fUpyUSj+Z2oxOzRVpD
# MYLa2ISuubAfdfX2HX1RETcn6LU1hHH3V6qu+olxyZjSnlpkdr6Mw30VapHxFPTy
# 2TUxuNty+rR1yIibar+YRcdmstf/zpKQdeTr5obSyBvbJ8BblW9Jb1hdaSreU0v4
# 6Mp79mwV+QMZDxGFqk+av6pX3WDG9XEg9FGomsrp0es0Rz11+iLsVT9qGTlrEOla
# P470I3gwsvKmOMs1jaqYWSRAuDpnpAdfoP7YO0kT+wzh7Qttg1DO8H8+4NkI6Iwh
# SkHC3uuOW+4Dwx1ubuZUNWZncnwa6lL2IsRyP64wggd6MIIFYqADAgECAgphDpDS
# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIWbzCCFmsCAQEwgZUwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAQNeJRyZH6MeuAAAAAABAzAN
# BglghkgBZQMEAgEFAKCB1jAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQghr039oZd
# KMMScZFZHu0BDBhGOzfyakq2XST7Y5sEwnEwagYKKwYBBAGCNwIBDDFcMFqgPoA8
# AFMAZQByAHYAZQByAEgAZQBhAGwAdABoACAAUABvAHcAZQByAFMAaABlAGwAbAAg
# AE0AbwBkAHUAbABloRiAFmh0dHBzOi8vbWljcm9zb2Z0LmNvbSAwDQYJKoZIhvcN
# AQEBBQAEggEAuxzo8x0UhYn0fZuK0IqDZNAmYquOSZGKB7SBZ13z114n3AinmocQ
# iC7yqWSuRStGFeNhEUZJYrhfTGRsAgqrhpaPPFTCASYTPB3bPJovb4a3bnG188aS
# LRarzQhw6VTMNZa1bsGrhyQvlgfxknpsmdBj7Ys4LlcnO3GYO9FoPbMB7+Xd6xh3
# r87gIi65lQgpi6XjpxENsUbuFfg7Y+WduHerGsSK9lDcXXE+nAZOiQexzBkCxLzq
# gFrHtiz1HZ+nWS4m94NdeufdT+yQoVGbeBeEYmnWnZHu1zIan7KCmDKY8brrFfxm
# f1iJ420GL69PjzXHOc4SsxVOGiphLoMmQ6GCE9EwghPNBgorBgEEAYI3AwMBMYIT
# vTCCE7kGCSqGSIb3DQEHAqCCE6owghOmAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFX
# BgsqhkiG9w0BCRABBKCCAUYEggFCMIIBPgIBAQYKKwYBBAGEWQoDATAxMA0GCWCG
# SAFlAwQCAQUABCC4ljPTM7k+RSYn27DMjgRDux/sL+R9rn6g0Z4rVHszXwIGW60D
# 90mMGBIyMDE4MTAwMzA1MTQ1OS4zMlowBwIBAYACAfSggdSkgdEwgc4xCzAJBgNV
# BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29m
# dCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVT
# Tjo5OEZELUM2MUUtRTY0MTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# U2VydmljZaCCDyIwggT1MIID3aADAgECAhMzAAAAy194yyMOlJfDAAAAAADLMA0G
# CSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTE4
# MDgyMzIwMjYyNFoXDTE5MTEyMzIwMjYyNFowgc4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25z
# IFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo5OEZELUM2MUUt
# RTY0MTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCCASIw
# DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMV4yB3v8B1BcBxtNEo/VALKGniz
# A1WCEIU22DCpyy838O0VlW7D3KUomZPIU3nsx3MxaQXpai0OiVs+DPuHqdoKtsuY
# CaMxeDHhodCgPWdPT9NN0hngnC07R2nDB2NhvtRBpr4V36791Pqi3CssKDdLjBrO
# QUhqEn8S0VP5xldDQPfMIpqRFQdP6Ut4dvaI/Mva5e86HbawJxdGKrTdHp7LOae3
# YHX25khbhuNatqp3dDu3Do6xDE1BIa2GuUGZa4oHVNwWIWk3SZ4xZlarT3eAi712
# yWyeTrjGv56Ryje8yDiBtd+1UCn67t0TwQpTa+a2ZPP2v8HyQxQegc+9ThUCAwEA
# AaOCARswggEXMB0GA1UdDgQWBBQo5PLm9snRTa5uyNsqlr8xw/vZdjAfBgNVHSME
# GDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBHhkVodHRw
# Oi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNUaW1TdGFQ
# Q0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5o
# dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0YVBDQV8y
# MDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMI
# MA0GCSqGSIb3DQEBCwUAA4IBAQCL9GGFgwVibMsUlJfD6SUDbHKxL9pN6ZYMg+aO
# TE8AyCh9oD6HcuinUjkj6afQU63TvgVRWExYJLzrQBysAh2GgbGkKIPtdV6yQQMl
# JxclXpR48t1jS1VvBX0KksR5Bq/4/0e58+jXvUaU2JcUQVw3lHn9I/YtQJeuAvnN
# fLENxJKE3A7FOjOAw+fEH49OGK1IBR9yhXS+r6HslFuFLfjK7DU89+Cu1zAg9JTC
# CrqlWSydWApAYh/ACInONLHHp9OZdilC42zGjB8Ro/07YqMAjPhK7Ze12lWThiZI
# Fqc5fZTxCi3L2T8pQI91/Nxu4CnpIzLXUwSXUxkIpfSNsK7OMIIGcTCCBFmgAwIB
# AgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzAR
# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2Vy
# dGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcNMjUwNzAx
# MjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYw
# JAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIwDQYJKoZI
# hvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0VBDVpQoA
# goX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEwRA/xYIiE
# VEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQedGFnkV+B
# VLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKxXf13Hz3w
# V3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4GkbaICDXo
# eByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEAAaOCAeYw
# ggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7fEYbxTNo
# WoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBW
# BgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUH
# AQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp
# L2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0gAQH/BIGV
# MIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYBBQUHAgIw
# NB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUAbQBlAG4A
# dAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOhIW+z66bM
# 9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS+7lTjMz0
# YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlKkVIArzgP
# F/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon/VWvL/62
# 5Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOiPPp/fZZq
# kHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/fmNZJQ96
# LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCIIYdqwUB5v
# vfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0cs0d9LiF
# AR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7aKLixqduW
# sqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQcdeh0sVV
# 42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+NR4Iuto2
# 29Nfj950iEkSoYIDsDCCApgCAQEwgf6hgdSkgdEwgc4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRp
# b25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo5OEZELUM2
# MUUtRTY0MTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaIl
# CgEBMAkGBSsOAwIaBQADFQC5o5PSQHbRtx8VowRRl644K9uaIaCB3jCB26SB2DCB
# 1TELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
# ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMg
# TWljcm9zb2Z0IE9wZXJhdGlvbnMgUHVlcnRvIFJpY28xJzAlBgNVBAsTHm5DaXBo
# ZXIgTlRTIEVTTjo1N0Y2LUMxRTAtNTU0QzErMCkGA1UEAxMiTWljcm9zb2Z0IFRp
# bWUgU291cmNlIE1hc3RlciBDbG9jazANBgkqhkiG9w0BAQUFAAIFAN9eynAwIhgP
# MjAxODEwMDMxMjU2MTZaGA8yMDE4MTAwNDEyNTYxNlowdzA9BgorBgEEAYRZCgQB
# MS8wLTAKAgUA317KcAIBADAKAgEAAgIgYAIB/zAHAgEAAgIXeDAKAgUA32Ab8AIB
# ADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMBoAowCAIBAAIDFuNgoQow
# CAIBAAIDB6EgMA0GCSqGSIb3DQEBBQUAA4IBAQCWROM4SMFaudr63nyjsrU/QiuX
# r0+kvird8m4QPiw3UoGOXYDzrXl7mWH6PYvK+6V9iXo650Bo6HSInst2FG/kQrCo
# hRHolKXi7yq1QgNCk+U7QRoLMbtdNFH4L8KJSm78z6eX+rAsrjaRcWNS4wJCgv/+
# Iv9aAOMUqYDYmj4njEmAC0huGyL1D+8Mm6mmjsJpBmzP/Ma9Efe+Lt9C2bc0LcnX
# mBMn0xqWwWGinHZixUxFmqkC+tnMfDtfAtfqOZg77RD3541J/I2Vhu5bDLJUyn+N
# Kdpqwk9wVngZPQVTmef9kUa+uilI6BlmV4DOwXIfXfwPBc1bVo+F7sBz2HGIMYID
# DTCCAwkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAADL
# X3jLIw6Ul8MAAAAAAMswDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzEN
# BgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgjdhAlLQ8VB3r7sPRtpp/lHX6
# abuORfXDvhmHURO+et8wgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCA2JyGq
# qWCnXutz0KS9S3wuF/afS9Mu7hRHXqpg3cEdZDCBmDCBgKR+MHwxCzAJBgNVBAYT
# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBU
# aW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAAy194yyMOlJfDAAAAAADLMCIEIH314Hks
# ZDboOtk5s0e/+Bhl/Gcqs7SojFcikG6Nf63uMA0GCSqGSIb3DQEBCwUABIIBAGN/
# fS6Za8ANJe//StYeB+7oPWME1dsGuEs0zeiGRYLTed1QqM0Nq1pwmnHI7DSI2BQS
# hgh5aqu6+kOr3KTbJ3dp0jJfTHFM2PMQBvdomJYSv1FtrFumC3+FazwbFNenzlsw
# vlAqMk6H4YFCQzJKyw+ihyXdnMrb6Fu80Rztp9O/LF4PBkFSQgo4mt1euslt/RTQ
# YQ2c3RQSY7gjOezjKDokBDmcJs8pMtUoIXncBUK8/TT9XhJ4DpIsq3LpN9/l5aFs
# GL7UmwpkBKlP6718J4uTQ+29yNzO4JNzK350fN6tunriqopnE0azu/QsIoRxGJdH
# irytMBFIMYuyzIzvJMk=
# SIG # End signature block