AzStackHciSoftware/AzStackHci.Software.Helpers.psm1

Import-LocalizedData -BindingVariable lswTxt -FileName AzStackHci.Software.Strings.psd1

class HealthModel
{
    # Attributes for Azure Monitor schema
    [string]$Name #Name of the individual test/rule/alert that was executed. Unique, not exposed to the customer.
    [string]$Title #User-facing name; one or more sentences indicating the direct issue.
    [string]$Severity #Severity of the result (Critical, Warning, Informational, Hidden) - this answers how important the result is. Critical is the only update-blocking severity.
    [string]$Description #Detailed overview of the issue and what impact the issue has on the stamp.
    [psobject]$Tags #Key-value pairs that allow grouping/filtering individual tests. For example, "Group": "ReadinessChecks", "UpdateType": "ClusterAware"
    [string]$Status #The status of the check running (i.e. Failed, Succeeded, In Progress) - this answers whether the check ran, and passed or failed.
    [string]$Remediation #Set of steps that can be taken to resolve the issue found.
    [string]$TargetResourceID #The unique identifier for the affected resource (such as a node or drive).
    [string]$TargetResourceName #The name of the affected resource.
    [string]$TargetResourceType #The type of resource being referred to (well-known set of nouns in infrastructure, aligning with Monitoring).
    [datetime]$Timestamp #The Time in which the HealthCheck was called.
    [psobject]$AdditionalData #Property bag of key value pairs for additional information.
    [string]$HealthCheckSource #The name of the services called for the HealthCheck (I.E. Test-AzureStack, Test-Cluster).
}

class AzStackHciSoftwareTarget : HealthModel {}

function Test-OSVersion
{
    <#
    .SYNOPSIS
        Test OS Version is correct
    .DESCRIPTION
        Get OS Version from local machine to use as minimum core count
        Expecting data from PsSessions to include local machine indicating ECE.
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        [System.Management.Automation.Runspaces.PSSession[]]
        $PsSession
    )
    try
    {
        # Get All OS versions
        $OsVersion = @()
        $OsVersion = GetOSVersion -PsSession $PsSession

        # Get OS Version from local machine to use as desired OS version
        # Expecting data from PsSessions to include local machine indicating ECE.
        $localOs = $OsVersion | Where-Object ComputerName -EQ $ENV:COMPUTERNAME
        if ($localOs)
        {
            # Check local machine for minimum OS
            $instanceResults = @()
            $instanceResults += TestOsMinimumVersion -Version $localOs
            # Check other nodes for consistency against local node
            $localOsVersion = $localOs | Select-Object -ExpandProperty OSVersion

            foreach ($version in ($OsVersion | Where-Object ComputerName -NE $ENV:COMPUTERNAME))
            {
                $detail = $lswTxt.OSVersion -f $version.OSVersion, $localOsVersion
                if ($version.OSVersion -eq $localOsVersion)
                {
                    $status = 'Succeeded'
                    Log-Info ("Checking {0}. {1}" -f $version.ComputerName, $detail)
                }
                else
                {
                    $status = 'Failed'
                    Log-Info ("Checking {0}. {1}" -f $version.ComputerName, $detail) -type Warning
                }

                $instanceResult = New-Object AzStackHciSoftwareTarget
                $instanceResult.Name = 'AzStackHci_Software_OperatingSystem_Version'
                $instanceResult.Title = 'Test Operating System Version'
                $instanceResult.Severity = 'Warning'
                $instanceResult.Description = "Checking Operating System Version matches existing cluster"
                $instanceResult.Remediation = 'https://aka.ms/hci-connect-chk'
                $instanceResult.TargetResourceType = 'OperatingSystem'
                $instanceResult.Timestamp = [datetime]::UtcNow
                $instanceResult.HealthCheckSource = 'Environment Checker'
                $instanceResult.TargetResourceID = $version.ComputerName
                $instanceResult.TargetResourceName = $version.ComputerName
                $AdditionalData = @()
                $AdditionalData += New-Object -TypeName PSObject -Property @{
                    Source    = $version.ComputerName
                    Resource  = 'OS Version'
                    Detail    = $detail
                    Status    = $status
                    TimeStamp = [datetime]::UtcNow
                }
                $instanceResult.AdditionalData = $AdditionalData
                $instanceResult.Status = $AdditionalData.Status
                $instanceResults += $instanceResult
            }
            return $instanceResults
        }
        else
        {
            Log-Info $lswTxt.OSVersionSkip
        }
    }
    catch
    {
        throw $_
    }
}

function GetOSVersion
{
    <#
    .SYNOPSIS
        Get OS Version from local or remote
    #>


    [CmdletBinding()]
    param (
        [System.Management.Automation.Runspaces.PSSession[]]
        $PsSession
    )
    $sb = {
        $osVersion = (Get-CimInstance -ClassName Win32_OperatingSystem -Property Version).Version
        $ntoskrnl = (Get-Item -Path (Join-Path -Path ([System.Environment]::SystemDirectory) -ChildPath 'ntoskrnl.exe')).VersionInfo.ProductVersion
        return New-Object PSObject -Property @{
            ComputerName = $ENV:COMPUTERNAME
            OSVersion    = [System.Version]"$osVersion.$($ntoskrnl.Split('.')[-1])"
        }
    }
    $osVersion = if ($PsSession)
    {
        Invoke-Command -Session $PsSession -ScriptBlock $sb
    }
    else
    {
        Invoke-Command -ScriptBlock $sb
    }
    return $osVersion
}

function TestOsMinimumVersion
{
    <#
    .SYNOPSIS
        A short one-line action-based description, e.g. 'Tests if a function is valid'
    #>


    [CmdletBinding()]
    param (
        [Parameter()]
        [psobject]
        $Version,

        [Parameter()]
        [system.version]
        $MinVersion = '10.0.19045'

    )
    try {
        if ($version.OSVersion -ge $MinVersion)
        {
            $Status = 'Succeeded'
            $detail = $lswTxt.MinOSCheck -f $Version.OsVersion, $MinVersion
            Log-Info $detail -Type Info
        }
        else
        {
            $Status = 'Failed'
            $detail = $lswTxt.MinOSCheck -f $Version.OsVersion, $MinVersion
            Log-Info $detail -Type Warning
        }

        $instanceResult = New-Object AzStackHciSoftwareTarget
        $instanceResult.Name = 'AzStackHci_Software_OperatingSystem_Version'
        $instanceResult.Title = 'Test Operating System Version'
        $instanceResult.Severity = 'Critical'
        $instanceResult.Description = "Checking Operating System Version meets minimum version"
        $instanceResult.Remediation = 'https://aka.ms/hci-connect-chk'
        $instanceResult.TargetResourceType = 'OperatingSystem'
        $instanceResult.Timestamp = [datetime]::UtcNow
        $instanceResult.HealthCheckSource = 'Environment Checker'
        $instanceResult.TargetResourceID = $version.ComputerName
        $instanceResult.TargetResourceName = $version.ComputerName
        $AdditionalData = @()
        $AdditionalData += New-Object -TypeName PSObject -Property @{
            Source    = $version.ComputerName
            Resource  = 'OS Version'
            Detail    = $detail
            Status    = $status
            TimeStamp = [datetime]::UtcNow
        }
        $instanceResult.AdditionalData = $AdditionalData
        $instanceResult.Status = $AdditionalData.Status
        return $instanceResult
    }
    catch {
        throw $_
    }
}

Export-ModuleMember -Function Test-*
# SIG # Begin signature block
# MIInoQYJKoZIhvcNAQcCoIInkjCCJ44CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC8V8wyzzhHMFWl
# zMSeQZO7kP7OOo7NGwhTbBxHiPH47qCCDXYwggX0MIID3KADAgECAhMzAAACy7d1
# OfsCcUI2AAAAAALLMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjIwNTEyMjA0NTU5WhcNMjMwNTExMjA0NTU5WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQC3sN0WcdGpGXPZIb5iNfFB0xZ8rnJvYnxD6Uf2BHXglpbTEfoe+mO//oLWkRxA
# wppditsSVOD0oglKbtnh9Wp2DARLcxbGaW4YanOWSB1LyLRpHnnQ5POlh2U5trg4
# 3gQjvlNZlQB3lL+zrPtbNvMA7E0Wkmo+Z6YFnsf7aek+KGzaGboAeFO4uKZjQXY5
# RmMzE70Bwaz7hvA05jDURdRKH0i/1yK96TDuP7JyRFLOvA3UXNWz00R9w7ppMDcN
# lXtrmbPigv3xE9FfpfmJRtiOZQKd73K72Wujmj6/Su3+DBTpOq7NgdntW2lJfX3X
# a6oe4F9Pk9xRhkwHsk7Ju9E/AgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUrg/nt/gj+BBLd1jZWYhok7v5/w4w
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzQ3MDUyODAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAJL5t6pVjIRlQ8j4dAFJ
# ZnMke3rRHeQDOPFxswM47HRvgQa2E1jea2aYiMk1WmdqWnYw1bal4IzRlSVf4czf
# zx2vjOIOiaGllW2ByHkfKApngOzJmAQ8F15xSHPRvNMmvpC3PFLvKMf3y5SyPJxh
# 922TTq0q5epJv1SgZDWlUlHL/Ex1nX8kzBRhHvc6D6F5la+oAO4A3o/ZC05OOgm4
# EJxZP9MqUi5iid2dw4Jg/HvtDpCcLj1GLIhCDaebKegajCJlMhhxnDXrGFLJfX8j
# 7k7LUvrZDsQniJZ3D66K+3SZTLhvwK7dMGVFuUUJUfDifrlCTjKG9mxsPDllfyck
# 4zGnRZv8Jw9RgE1zAghnU14L0vVUNOzi/4bE7wIsiRyIcCcVoXRneBA3n/frLXvd
# jDsbb2lpGu78+s1zbO5N0bhHWq4j5WMutrspBxEhqG2PSBjC5Ypi+jhtfu3+x76N
# mBvsyKuxx9+Hm/ALnlzKxr4KyMR3/z4IRMzA1QyppNk65Ui+jB14g+w4vole33M1
# pVqVckrmSebUkmjnCshCiH12IFgHZF7gRwE4YZrJ7QjxZeoZqHaKsQLRMp653beB
# fHfeva9zJPhBSdVcCW7x9q0c2HVPLJHX9YCUU714I+qtLpDGrdbZxD9mikPqL/To
# /1lDZ0ch8FtePhME7houuoPcMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# /Xmfwb1tbWrJUnMTDXpQzTGCGYEwghl9AgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAALLt3U5+wJxQjYAAAAAAsswDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIPh8Bm0uXtd+QCjO9s3COlV2
# UsKOfVbqFLAa9C/CMSqXMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEANOw++3IyFCwLT+vQwkgr0weNe0defN7eZduCOqZ48smzt9/DymQUMezH
# nzMbnyFVlyZVX7tkvidyzZfb2GiULA0jRKon60JvkXiCWFxxJTLa0XKeJ/zrxDis
# /J70Zja1R2FII2JuzD852gzDcxcGQvHxDRgus3+H/zWR46/3RaEVW6VJWRbMjPt1
# pI8PJeCaocr/tcgZPHH3BiPWzagstDD1QcGwcigCYDBLJPHXJ8E+SKUv3WT74BzT
# pC4nnOeoCMTArfmrdI58DHIoOQlEnGKXsSpDVRPxV2RIxbI7871mhWBIz1M/p6sU
# m0fQ9ujYvJ3hjuaU9GR0Rk/y3JFOIqGCFwswghcHBgorBgEEAYI3AwMBMYIW9zCC
# FvMGCSqGSIb3DQEHAqCCFuQwghbgAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFUBgsq
# hkiG9w0BCRABBKCCAUMEggE/MIIBOwIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCB3JJS92qyiRBjiwhfhIcgKjUHLvqtmW22VBY632cBnXAIGY3Os9LIo
# GBIyMDIyMTIwNzA3MTIwMS4zN1owBIACAfSggdSkgdEwgc4xCzAJBgNVBAYTAlVT
# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy
# YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo3ODgw
# LUUzOTAtODAxNDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj
# ZaCCEV8wggcQMIIE+KADAgECAhMzAAABqFXwYanMMBhcAAEAAAGoMA0GCSqGSIb3
# DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD
# VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAk
# BgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTIyMDMwMjE4
# NTEyM1oXDTIzMDUxMTE4NTEyM1owgc4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX
# YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg
# Q29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0
# byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo3ODgwLUUzOTAtODAxNDEl
# MCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCCAiIwDQYJKoZI
# hvcNAQEBBQADggIPADCCAgoCggIBAKPabcrALiXX8pjyXpcMN89KTvcmlAiDw4pU
# +HejZhibUeo/HUy+P9VxWhCX7ogeeKPJ677+LeVdPdG5hTvGDgSuo3w+AcmzcXZ2
# QCGUgUReLUKbmrr06bB0xhvtZwoelhxtPkjJFsbTGtSt+V7E4VCjPdYqQZ/iN0Ar
# XXmgbEfVyCwS+h2uooBhM5UcbPogtr5VpgdzbUM4/rWupmFVjPB1asn3+wv7aBCK
# 8j9QUJroY4y1pmZSf0SuGMWY7cm2cvrbdm7XldljqRdHW+CQAB4EqiOqgumfR+aS
# po5T75KG0+nsBkjlGSsU1Bi15p4rP88pZnSop73Gem9GWO2GRLwP15YEnKsczxhG
# Y+Z8NEa0QwMMiVlksdPU7J5qK9gxAQjOJzqISJzhIwQWtELqgJoHwkqTxem3grY7
# B7DOzQTnQpKWoL0HWR9KqIvaC7i9XlPv+ue89j9e7fmB4nh1hulzEJzX6RMU9THJ
# MlbO6OrP3NNEKJW8jipCny8H1fuvSuFfuB7t++KK9g2c2NKu5EzSs1nKNqtl4KO3
# UzyXLWvTRDO4D5PVQOda0tqjS/AWoUrxKC5ZPlkLE+YPsS5G+E/VCgCaghPyBZsH
# NK7wHlSf/26uhLnKp6XRAIroiEYl/5yW0mShjvnARPr0GIlSm0KrqSwCjR5ckWT1
# sKaEb8w3AgMBAAGjggE2MIIBMjAdBgNVHQ4EFgQUNsfb4+L4UutlNh/MxjGkj0kL
# ItUwHwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYDVR0fBFgwVjBU
# oFKgUIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9z
# b2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwGCCsGAQUFBwEB
# BGAwXjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9w
# cy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5j
# cnQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG9w0B
# AQsFAAOCAgEAcTuCS2Rqqmf2mPr6OUydhmUx+m6vpEPszWioJXbnsRbny62nF9YX
# TKuSNWH1QFfyc/2N3YTEp4hE8YthYKgDM/HUhUREX3WTwGseYuuDeSxWRJWCorAH
# F1kwQzIKgrUc3G+uVwAmG/EI1ELRExA4ftx0Ehrf59aJm7Ongn0lTSSiKUeuGA+M
# y6oCi/V8ETxz+eblvQANaltJgGfppuWXYT4jisQKETvoJjBv5x+BA0oEFu7gGaeM
# DkZjnO5vdf6HeKneILs9ZvwIWkgYQi2ZeozbxglG5YwExoixekxrRTDZwMokIYxX
# mccscQ0xXmh+I3vo7hV9ZMKTa9Paz5ne4cc8Odw1T+624mB0WaW9HAE1hojB6Cbf
# undtV/jwxmdKh15plJXnN1yM7OL924HqAiJisHanpOEJ4Um9b3hFUXE2uEJL9aYu
# IgksVYIq1P29rR4X7lz3uEJH6COkoE6+UcauN6JYFghN9I8JRBWAhHX4GQHlngsd
# ftWLLiDZMynlgRCZzkYI24N9cx+D367YwclqNY6CZuAgzwy12uRYFQasYHYK1hpz
# yTtuI/A2B8cG+HM6X1jf2d9uARwH6+hLkPtt3/5NBlLXpOl5iZyRlBi7iDXkWNa3
# juGfLAJ3ISDyNh7yu+H4yQYyRs/MVrCkWUJs9EivLKsNJ2B/IjNrStYwggdxMIIF
# WaADAgECAhMzAAAAFcXna54Cm0mZAAAAAAAVMA0GCSqGSIb3DQEBCwUAMIGIMQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNy
# b3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAx
# ODIyMjVaFw0zMDA5MzAxODMyMjVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX
# YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg
# Q29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAy
# MDEwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL
# 1yR5vQ7VgtP97pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/XE/HZveVU3Fa4n5K
# Wv64NmeFRiMMtY0Tz3cywBAY6GB9alKDRLemjkZrBxTzxXb1hlDcwUTIcVxRMTeg
# Cjhuje3XD9gmU3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7M62AW36MEBydUv62
# 6GIl3GoPz130/o5Tz9bshVZN7928jaTjkY+yOSxRnOlwaQ3KNi1wjjHINSi947SH
# JMPgyY9+tVSP3PoFVZhtaDuaRr3tpK56KTesy+uDRedGbsoy1cCGMFxPLOJiss25
# 4o2I5JasAUq7vnGpF1tnYN74kpEeHT39IM9zfUGaRnXNxF803RKJ1v2lIH1+/Nme
# Rd+2ci/bfV+AutuqfjbsNkz2K26oElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afo
# mXw/TNuvXsLz1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9ahhaYQFzymeiXtcodgLi
# Mxhy16cg8ML6EgrXY28MyTZki1ugpoMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb
# 0f2y1BzFa/ZcUlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV2xo3xwgVGD94q0W2
# 9R6HXtqPnhZyacaue7e3PmriLq0CAwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQF
# AgMBAAEwIwYJKwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUpzxD/LwTuMB0GA1Ud
# DgQWBBSfpxVdAF5iXYP05dJlpxtTNRnpcjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdM
# g30BATBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp
# b3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJ
# KwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF
# MAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8w
# TTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVj
# dHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBK
# BggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9N
# aWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcNAQELBQADggIBAJ1V
# ffwqreEsH2cBMSRb4Z5yS/ypb+pcFLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1
# OW27DzHkwo/7bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYAA7AFvonoaeC6Ce57
# 32pvvinLbtg/SHUB2RjebYIM9W0jVOR4U3UkV7ndn/OOPcbzaN9l9qRWqveVtihV
# J9AkvUCgvxm2EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6LGYnn8AtqgcKBGUIZ
# UnWKNsIdw2FzLixre24/LAl4FOmRsqlb30mjdAy87JGA0j3mSj5mO0+7hvoyGtmW
# 9I/2kQH2zsZ0/fZMcm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0SCyxTkctwRQEcb9k
# +SS+c23Kjgm9swFXSVRk2XPXfx5bRAGOWhmRaw2fpCjcZxkoJLo4S5pu+yFUa2pF
# EUep8beuyOiJXk+d0tBMdrVXVAmxaQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L
# +DvktxW/tM4+pTFRhLy/AsGConsXHRWJjXD+57XQKBqJC4822rpM+Zv/Cuk0+CQ1
# ZyvgDbjmjJnW4SLq8CdCPSWU5nR0W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6
# CGJ/2XBjU02N7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIC0jCCAjsCAQEwgfyhgdSk
# gdEwgc4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH
# EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNV
# BAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1U
# aGFsZXMgVFNTIEVTTjo3ODgwLUUzOTAtODAxNDElMCMGA1UEAxMcTWljcm9zb2Z0
# IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUAbLr8xJ9BB4rL4Yg5
# 8X1LZ5iQdyyggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAN
# BgkqhkiG9w0BAQUFAAIFAOc6gzIwIhgPMjAyMjEyMDcwNzEyNTBaGA8yMDIyMTIw
# ODA3MTI1MFowdzA9BgorBgEEAYRZCgQBMS8wLTAKAgUA5zqDMgIBADAKAgEAAgIW
# rQIB/zAHAgEAAgITkjAKAgUA5zvUsgIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgor
# BgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUA
# A4GBABaIPcLWel69wZ7E1YRQ8brqoT2mnEdNXDr38YhEtdUSkPClPUb0GOxupKv9
# KV/voDLrTCM8RHBUOJAmT5/hMaIaTAQul7rNm8Fa+xani1wbSoV8eddy4NCuQVmU
# mQlFOlCbTHqVNRvq75NtmTX7HzE7QYM1ccAgXGqgeQNVg/67MYIEDTCCBAkCAQEw
# gZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT
# B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE
# AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAGoVfBhqcwwGFwA
# AQAAAagwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0B
# CRABBDAvBgkqhkiG9w0BCQQxIgQg9reUjXkinv5WPxEU8Jy2M2evooKNuOn3XFB6
# OeRz6ZwwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCB0/ssdAMsHwnNwhfFB
# XPlFnRvWhHqSX9YLUxBDl1xlpjCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwAhMzAAABqFXwYanMMBhcAAEAAAGoMCIEIDVBgdaDpd5aXZmJx7EE
# 4VgtLDK64S08oJC5wyjgnxC7MA0GCSqGSIb3DQEBCwUABIICAJGh8rOIoOCid7bR
# iV0yti3dxt5qDKpddwKQ4zGi+u+0kYgXYwxENQ60MuWKfWAKi7zUa+ZZVzylms1u
# mrWOAj3Sr1IJv30jMdTlNrdJ2agdxCIZkgmAXTecLBFTqXdnj68XNCMY/uwhEoWB
# M3oRqDrmX1zRSkQmPKRs76KLiRKYFrjTYQZlTKUKnlFwvPdY37SaiqMfPsbgjI//
# +MYNMA78LdVSAfukzXgNTM+njR2+j+qY5eE5GjQm2VsCIq3LxFm5bmE/GOYAMhoD
# pmxnzamEaXkM5QKdqc7gwVG7v90qjV/XK8dhkekAU9qflK1ShaZ0ejN77oj+7Dzf
# xKOetq8simsF9gtR1qPwEFalzF5Mn4jPgjeneN2EEmN32RpgtFc3NB66zKww4StZ
# OVdylnWLFNSj2JH7qfSob5uCjyDo/PAwNaRWh1WPJAsnjLroVGD9ViRhFsqubC2z
# eRnDOmECz7iXdrerGsRFwreaxF4f5KUGOHcto9eJnt5JgDrZPrczJaOB9xI5+Dhg
# C8nsBjgJPcbIRsq6XHxMxdOd91coroUSzErYPiMmD24P2o1cntBJ/mTbwbcs+jOC
# AlDyGFV++nkHTIQ71bd8eQ4OKqSKT1FJZ/nqidCqdBrRFtcKxeb/lLaBOn7hCbap
# u1LMLz62kpedV+dok1G34AmLZcwR
# SIG # End signature block