AzStackHciValidatedRecipe/AzStackHci.ValidatedRecipe.Helpers.psm1

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

function TestResult {
    <#
    .SYNOPSIS
        Build up a params data structure to pass the
        New-AzStackHciResultObject function.
    .DESCRIPTION
        The New-AzStackHciResultObject function get information about the
        result of a test into the correct result files (log, json, etc.)
    #>

    Param([Parameter(Mandatory=$true,Position=0)] [array]$responses,
          [Parameter(Mandatory=$true,Position=1)] [string]$Name,
          [Parameter(Mandatory=$true,Position=2)] [string]$Title,
          [Parameter(Mandatory=$true,Position=3)] [string]$DisplayName,
          [Parameter(Mandatory=$true,Position=4)] [string]$Severity,
          [Parameter(Mandatory=$true,Position=5)] [string]$Description,
          [Parameter(Mandatory=$false,Position=6)] [string]$Remediation = 'https://learn.microsoft.com/en-us/azure/azure-local/update/update-troubleshooting-23h2',
          [Parameter(Mandatory=$false,Position=7)] [string]$TargetResourceType = 'ValidatedRecipe',
          [Parameter(Mandatory=$false,Position=8)] [string]$Resource = 'Validated Assembly Recipe')
    $instanceResults = @()
    foreach ($response in $responses) {
        $detailString = $($response.details) -join '; '
        foreach ($msg in $($response.logLines)) {
            $msgArray = $msg.split('|')
            Log-Info $msgArray[0] -Type $msgArray[1]
        }
        try {
            $Status = 'SUCCESS'
            if ($($response.rc)) {
                $Status = 'FAILURE'
            }
            $params = @{
                Name               = $Name
                Title              = $Title
                DisplayName        = $DisplayName
                Severity           = $Severity
                Description        = $Description
                Tags               = @{}
                Remediation        = $Remediation
                TargetResourceID   = $($response.computername)
                TargetResourceName = $($response.computername)
                TargetResourceType = $TargetResourceType
                Timestamp          = [datetime]::UtcNow
                Status             = $status
                HealthCheckSource  = $ENV:EnvChkrId
                AdditionalData     = @{Source    = $($response.computername)
                                       Resource  = $Resource
                                       Detail    = $detailString
                                       Status    = $status
                                       TimeStamp = [datetime]::UtcNow}}
            $instanceResults += New-AzStackHciResultObject @params
        }
        catch {
            throw $_
        }
    }
    return $instanceResults
}

function Test-PSModules
{
    <#
    .SYNOPSIS
        Test version of PS Modules installed is correct
    .DESCRIPTION
        Get installed PS module from local machine and validated its expected as per the current recipe.
        Expecting data from PsSessions to include local machine indicating ECE.
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        [System.Management.Automation.Runspaces.PSSession[]]
        $PsSession
    )
    $sb = {
        Param($lswTxt, $resultSeverity)
        $rc = 0
        $localHost = $ENV:ComputerName
        $detailList = New-Object System.Collections.Generic.List[System.Object]
        $logLines = New-Object System.Collections.Generic.List[System.Object]
        $vcNugetPath = Get-ASArtifactPath -NugetName "Microsoft.AzureStack.VersionControl.Operations"
        Import-Module "$vcNugetPath\content\Scripts\VersionControlHelpers.psm1" -Force -DisableNameChecking -Verbose:$false | Out-Null
        $InstalledModules = Get-InstalledPowerShellModules
        foreach ($mod in $InstalledModules) {
            # Get-InstalledPowerShellModules will return 0.0.0 if a required module is not installed
            # it is OK if the module is not yet installed because we are looking to update/install the modules
            # this is true especially if they are not currently installed or they have an older version installed.
            # however, if the customer has a newer version installed we do not want to downgrade the version
            # just because the recipe calls for an older version.
            try {
                $InstalledVersionObj = [Version]::Parse($mod.InstalledVersion)
            }
            catch {
                $InstalledVersionObj = $null
                $mod.InstalledVersion = "N/A"
            }
            try {
                $RequiredVersionObj = [Version]::Parse($mod.RequiredVersion)
            }
            catch {
                $RequiredVersionObj = $null
                $mod.RequiredVersion = "N/A"
            }
            if (-not $InstalledVersionObj -and -not $RequiredVersionObj) {
                $rc += 1
                $res = ${resultSeverity}
                $msg = $lswTxt.PSModulesFailedToParseBothVersions -f $mod.Name, $localHost, $mod.InstalledVersion, $mod.RequiredVersion
                $dtl = $msg
            } elseif (-not $InstalledVersionObj) {
                $rc += 1
                $res = ${resultSeverity}
                $msg = $lswTxt.PSModulesFailedToParseInstalledVersion -f $mod.Name, $localHost, $mod.InstalledVersion
                $dtl = $msg
            } elseif (-not $RequiredVersionObj) {
                $rc += 1
                $res = ${resultSeverity}
                $msg = $lswTxt.PSModulesFailedToParseRequiredVersion -f $mod.Name, $localHost, $mod.RequiredVersion
                $dtl = $msg
            } elseif ($InstalledVersionObj -le $RequiredVersionObj) {
                $res = 'SUCCESS'
                $msg = $lswTxt.PSModulesSuccess -f $mod.Name, $localHost, $mod.InstalledVersion, $mod.RequiredVersion
                $dtl = $lswTxt.PSModulesDetailSuccess -f $mod.Name, $mod.InstalledVersion
            } else {
                $rc += 1
                $res = ${resultSeverity}
                $msg = $lswTxt.PSModulesFailure -f $mod.Name, $localHost, $mod.InstalledVersion, $mod.RequiredVersion
                $dtl = $msg
            }
            $detailList.Add($dtl)
            $logLines.Add("${msg}|${res}")
        }
        $response = "" | Select-Object -Property rc, details, computername, logLines
        $response.rc = $rc
        $response.details = $detailList
        $response.computername = $localHost
        $response.logLines = $logLines
        return $response
    } #endSB
    $resultSeverity = "CRITICAL"
    $responses = if ($psSession) {
        Invoke-Command -Session $PsSession -ScriptBlock $sb -ArgumentList (,$lswTxt, $resultSeverity)
    } else {
        Invoke-Command -ScriptBlock $sb -ArgumentList (,$lswTxt, $resultSeverity)
    }
    $splat = @{responses   = $responses
        Name        = "AzStackHci_ValidatedRecipe_PowerShellModule_Version"
        Title       = "Test PowerShell Module Version"
        DisplayName = "Test PowerShell Module Version"
        Severity    = $resultSeverity
        Description = "Validating that the PS modules installed on the host are the same versions defined in the validated recipe."}
    return (TestResult @splat)
}

function Test-AzCli
{
    <#
    .SYNOPSIS
        Validate that the installed version of Azure.CLI matched the required one
    .DESCRIPTION
        Get installed version using 'az version' and compare it to the required version defined by the VersionControl module
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        [System.Management.Automation.Runspaces.PSSession[]]
        $PsSession
    )
    $sb = {
        Param($lswTxt, $resultSeverity)
        $rc = 0
        $localHost = $ENV:ComputerName
        $detailList = New-Object System.Collections.Generic.List[System.Object]
        $logLines = New-Object System.Collections.Generic.List[System.Object]
        $vcNugetPath = Get-ASArtifactPath -NugetName "Microsoft.AzureStack.VersionControl.Operations"
        Import-Module "$vcNugetPath\content\Scripts\VersionControlHelpers.psm1" -Force -DisableNameChecking -Verbose:$false | Out-Null
        $name = 'AzCli'
        try {
            $installedVersion = (az version -o json |ConvertFrom-Json).'azure-cli'
        }
        catch {
            $installedVersion = "N/A"
        }
        try {
            $requiredVersion = (Get-ValidatedRecipe -TypesToInclude 'AzCli' -TagsToInclude 'Azure').RequiredVersion
        }
        catch {
            $requiredVersion = "N/A"
        }
        try {
            $InstalledVersionObj = [Version]::Parse($installedVersion)
        }
        catch {
            $InstalledVersionObj = $null
            $installedVersion = "N/A"
        }
        try {
            $RequiredVersionObj = [Version]::Parse($requiredVersion)
        }
        catch {
            $RequiredVersionObj = $null
            $requiredVersion = "N/A"
        }
        if (-not $InstalledVersionObj -and -not $RequiredVersionObj) {
            $rc += 1
            $res = ${resultSeverity}
            $msg = $lswTxt.AzCliFailedToParseBothVersions -f $name, $localHost, $installedVersion, $requiredVersion
            $dtl = $msg
        } elseif (-not $InstalledVersionObj) {
            $rc += 1
            $res = ${resultSeverity}
            $msg = $lswTxt.AzCliFailedToParseInstalledVersion -f $name, $localHost, $requiredVersion, $installedVersion
            $dtl = $msg
        } elseif (-not $RequiredVersionObj) {
            $rc += 1
            $res = ${resultSeverity}
            $msg = $lswTxt.AzCliFailedToParseRequiredVersion -f $name, $localHost, $requiredVersion
            $dtl = $msg
        } elseif ($InstalledVersionObj -eq $RequiredVersionObj) {
            $res = 'SUCCESS'
            $msg = $lswTxt.AzCliSuccess -f $name, $localHost, $installedVersion, $requiredVersion
            $dtl = $msg
        } else {
            $rc += 1
            $res = ${resultSeverity}
            $msg = $lswTxt.AzCliFailure -f $name, $localHost, $installedVersion, $requiredVersion
            $dtl = ''
            $dtl += $msg
            $dtl += " The Az-Cli version installed conflicts with updates; "
            $dtl += "Find the [Azure-Cli] msi file path in the C:/NugetStore and run: "
            $dtl += "(Start-Process msiexec.exe -Wait -ArgumentList (/I [fullPathToAzureCliMsiFile] /quiet))"
        }
        $detailList.Add($dtl)
        $logLines.Add("${msg}|${res}")

        $response = "" | Select-Object -Property rc, details, computername, logLines
        $response.rc = $rc
        $response.details = $detailList
        $response.computername = $localHost
        $response.logLines = $logLines
        return $response
    } #endSB
    $resultSeverity = "CRITICAL"
    $responses = if ($psSession) {
        Invoke-Command -Session $PsSession -ScriptBlock $sb -ArgumentList (,$lswTxt, $resultSeverity)
    } else {
        Invoke-Command -ScriptBlock $sb -ArgumentList (,$lswTxt, $resultSeverity)
    }
    $splat = @{responses   = $responses
        Name        = "AzStackHci_ValidatedRecipe_AzureCli_Version"
        Title       = "Test Azure-Cli Module Version"
        DisplayName = "Test Azure-Cli Module Version"
        Severity    = $resultSeverity
        Description = "Validating that the installed version of Az.Cli is the required version."}
    return (TestResult @splat)
}

Export-ModuleMember -Function Test-*
# SIG # Begin signature block
# MIIoVQYJKoZIhvcNAQcCoIIoRjCCKEICAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCD7eVJJvWDpwThN
# x6E53z1Cfrd7GaVJwLC1Gf061fsDKqCCDYUwggYDMIID66ADAgECAhMzAAAEA73V
# lV0POxitAAAAAAQDMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjQwOTEyMjAxMTEzWhcNMjUwOTExMjAxMTEzWjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQCfdGddwIOnbRYUyg03O3iz19XXZPmuhEmW/5uyEN+8mgxl+HJGeLGBR8YButGV
# LVK38RxcVcPYyFGQXcKcxgih4w4y4zJi3GvawLYHlsNExQwz+v0jgY/aejBS2EJY
# oUhLVE+UzRihV8ooxoftsmKLb2xb7BoFS6UAo3Zz4afnOdqI7FGoi7g4vx/0MIdi
# kwTn5N56TdIv3mwfkZCFmrsKpN0zR8HD8WYsvH3xKkG7u/xdqmhPPqMmnI2jOFw/
# /n2aL8W7i1Pasja8PnRXH/QaVH0M1nanL+LI9TsMb/enWfXOW65Gne5cqMN9Uofv
# ENtdwwEmJ3bZrcI9u4LZAkujAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU6m4qAkpz4641iK2irF8eWsSBcBkw
# VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
# dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzUwMjkyNjAfBgNVHSMEGDAW
# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw
# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx
# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB
# AFFo/6E4LX51IqFuoKvUsi80QytGI5ASQ9zsPpBa0z78hutiJd6w154JkcIx/f7r
# EBK4NhD4DIFNfRiVdI7EacEs7OAS6QHF7Nt+eFRNOTtgHb9PExRy4EI/jnMwzQJV
# NokTxu2WgHr/fBsWs6G9AcIgvHjWNN3qRSrhsgEdqHc0bRDUf8UILAdEZOMBvKLC
# rmf+kJPEvPldgK7hFO/L9kmcVe67BnKejDKO73Sa56AJOhM7CkeATrJFxO9GLXos
# oKvrwBvynxAg18W+pagTAkJefzneuWSmniTurPCUE2JnvW7DalvONDOtG01sIVAB
# +ahO2wcUPa2Zm9AiDVBWTMz9XUoKMcvngi2oqbsDLhbK+pYrRUgRpNt0y1sxZsXO
# raGRF8lM2cWvtEkV5UL+TQM1ppv5unDHkW8JS+QnfPbB8dZVRyRmMQ4aY/tx5x5+
# sX6semJ//FbiclSMxSI+zINu1jYerdUwuCi+P6p7SmQmClhDM+6Q+btE2FtpsU0W
# +r6RdYFf/P+nK6j2otl9Nvr3tWLu+WXmz8MGM+18ynJ+lYbSmFWcAj7SYziAfT0s
# IwlQRFkyC71tsIZUhBHtxPliGUu362lIO0Lpe0DOrg8lspnEWOkHnCT5JEnWCbzu
# iVt8RX1IV07uIveNZuOBWLVCzWJjEGa+HhaEtavjy6i7MIIHejCCBWKgAwIBAgIK
# YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm
# aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw
# OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD
# VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG
# 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la
# UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc
# 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D
# dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+
# lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk
# kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6
# A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd
# X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL
# 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd
# sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3
# T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS
# 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI
# bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL
# BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD
# uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv
# c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF
# BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h
# cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA
# YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn
# 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7
# v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b
# pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/
# KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy
# CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp
# mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi
# hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb
# BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS
# oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL
# gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX
# cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGiYwghoiAgEBMIGVMH4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p
# Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAQDvdWVXQ87GK0AAAAA
# BAMwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw
# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIK8n
# ZPSnS/grN75sbrv9H65yeb0OS2onNpcK4OY0fmnOMEIGCisGAQQBgjcCAQwxNDAy
# oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20wDQYJKoZIhvcNAQEBBQAEggEAdePT367LqQBCiryVMGZ4AqfEA99Dc/CFyZqj
# imBvjbGLX6U7GG1UnPfMLBnmIZawEpMgBXj5ukViAKq1V3dn99UMaXvEdUxrH6TB
# FKYe91dMNWQEGG0663Ltx/40ttdS+Wib82GN8Z4pYs2c2csrRYicuKkzV6n4MqIC
# El6cj4YBIH1/zm2YDmbmLe7KxMp0cr0hehFNAm4SdZBVj9CsdyNzSVEt7vrLCr3O
# WTj97KGHOuhumykigb1NFG14HqO4hK+bQ9/He4hCO4UOew8xu9u4QofalRmFnihX
# mcR41uLQXH+9Nrf6eRvPGDusaV1O3oNVf8iGKpTu7Uun30sGYKGCF7AwghesBgor
# BgEEAYI3AwMBMYIXnDCCF5gGCSqGSIb3DQEHAqCCF4kwgheFAgEDMQ8wDQYJYIZI
# AWUDBAIBBQAwggFaBgsqhkiG9w0BCRABBKCCAUkEggFFMIIBQQIBAQYKKwYBBAGE
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCDOkj6ayNw7dwrqSncr0G9xvi53v+7IoWfq
# wNAHOAY7MAIGaC2+S7kgGBMyMDI1MDYxMDE1NDgzMy4yMjNaMASAAgH0oIHZpIHW
# MIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQL
# EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsT
# Hm5TaGllbGQgVFNTIEVTTjo2QjA1LTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9z
# b2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCEf4wggcoMIIFEKADAgECAhMzAAAB9oMv
# JmpUXSLBAAEAAAH2MA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwMB4XDTI0MDcyNTE4MzEwNFoXDTI1MTAyMjE4MzEwNFowgdMxCzAJ
# BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jv
# c29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVs
# ZCBUU1MgRVNOOjZCMDUtMDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGlt
# ZS1TdGFtcCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
# 0UJeLMR/N9WPBZhuKVFF+eWJZ68Wujdj4X6JR05cxO5CepNXo17rVazwWLkm5Aja
# Vh19ZVjDChHzimxsoaXxNu8IDggKwpXvpAAItv4Ux50e9S2uVwfKv57p9JKG+Q7V
# ONShujl1NCMkcgSrPdmd/8zcsmhzcNobLomrCAIORZ8IwhYy4siVQlf1NKhlyAzm
# kWJD0N+60IiogFBzg3yISsvroOx0x1xSi2PiRIQlTXE74MggZDIDKqH/hb9FT2kK
# /nV/aXjuo9LMrrRmn44oYYADe/rO95F+SG3uuuhf+H4IriXr0h9ptA6SwHJPS2Vm
# bNWCjQWq5G4YkrcqbPMax7vNXUwu7T65E8fFPd1IuE9RsG4TMAV7XkXBopmPNfvL
# 0hjxg44kpQn384V46o+zdQqy5K9dDlWm/J6vZtp5yA1PyD3w+HbGubS0niEQ1L6w
# GOrPfzIm0FdOn+xFo48ERl+Fxw/3OvXM5CY1EqnzEznPjzJc7OJwhJVR3VQDHjBc
# EFTOvS9E0diNu1eocw+ZCkz4Pu/oQv+gqU+bfxL8e7PFktfRDlM6FyOzjP4zuI25
# gD8tO9zJg6g6fRpaZc439mAbkl3zCVzTLDgchv6SxQajJtvvoQaZxQf0tRiPcbr2
# HWfMoqqd9uiQ0hTUEhG44FBSTeUPZeEenRCWadCW4G8CAwEAAaOCAUkwggFFMB0G
# A1UdDgQWBBRIwZsJuOcJfScPWcXZuBA4B89K8jAfBgNVHSMEGDAWgBSfpxVdAF5i
# XYP05dJlpxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jv
# c29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENB
# JTIwMjAxMCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRw
# Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRp
# bWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1Ud
# JQEB/wQMMAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsF
# AAOCAgEA13kBirH1cHu1WYR1ysj125omGtQ0PaQkEzwGb70xtqSoI+svQihsgdTY
# xaPfp2IVFdgjaMaBi81wB8/nu866FfFKKdhdp3wnMZ91PpP4Ooe7Ncf6qICkgSuw
# gdIdQvqE0h8VQ5QW5sDV4Q0Jnj4f7KHYx4NiM8C4jTw8SQtsuxWiTH2Hikf3QYB7
# 1a7dB9zgHOkW0hgUEeWO9mh2wWqYS/Q48ASjOqYw/ha54oVOff22WaoH+/Hxd9NT
# EU/4vlvsRIMWT0jsnNI71jVArT4Q9Bt6VShWzyqraE6SKUoZrEwBpVsI0LMg2X3h
# OLblC1vxM3+wMyOh97aFOs7sFnuemtI2Mfj8qg16BZTJxXlpPurWrG+OBj4BoTDk
# C9AxXYB3yEtuwMs7pRWLyxIxw/wV9THKUGm+x+VE0POLwkrSMgjulSXkpfELHWWi
# CVslJbFIIB/4Alv+jQJSKAJuo9CErbm2qeDk/zjJYlYaVGMyKuYZ+uSRVKB2qkEP
# cEzG1dO9zIa1Mp32J+zzW3P7suJfjw62s3hDOLk+6lMQOR04x+2o17G3LceLkkxJ
# m41ErdiTjAmdClen9yl6HgMpGS4okjFCJX+CpOFX7gBA3PVxQWubisAQbL5HgTFB
# tQNEzcCdh1GYw/6nzzNNt+0GQnnobBddfOAiqkzvItqXjvGyK1QwggdxMIIFWaAD
# AgECAhMzAAAAFcXna54Cm0mZAAAAAAAVMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYD
# VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe
# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3Nv
# ZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAxODIy
# MjVaFw0zMDA5MzAxODMyMjVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
# MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5
# vQ7VgtP97pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/XE/HZveVU3Fa4n5KWv64
# NmeFRiMMtY0Tz3cywBAY6GB9alKDRLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhu
# je3XD9gmU3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7M62AW36MEBydUv626GIl
# 3GoPz130/o5Tz9bshVZN7928jaTjkY+yOSxRnOlwaQ3KNi1wjjHINSi947SHJMPg
# yY9+tVSP3PoFVZhtaDuaRr3tpK56KTesy+uDRedGbsoy1cCGMFxPLOJiss254o2I
# 5JasAUq7vnGpF1tnYN74kpEeHT39IM9zfUGaRnXNxF803RKJ1v2lIH1+/NmeRd+2
# ci/bfV+AutuqfjbsNkz2K26oElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afomXw/
# TNuvXsLz1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9ahhaYQFzymeiXtcodgLiMxhy
# 16cg8ML6EgrXY28MyTZki1ugpoMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y
# 1BzFa/ZcUlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV2xo3xwgVGD94q0W29R6H
# XtqPnhZyacaue7e3PmriLq0CAwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMB
# AAEwIwYJKwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUpzxD/LwTuMB0GA1UdDgQW
# BBSfpxVdAF5iXYP05dJlpxtTNRnpcjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30B
# ATBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz
# L0RvY3MvUmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYB
# BAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMB
# Af8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBL
# oEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv
# TWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggr
# BgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNS
# b29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcNAQELBQADggIBAJ1Vffwq
# reEsH2cBMSRb4Z5yS/ypb+pcFLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27
# DzHkwo/7bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYAA7AFvonoaeC6Ce5732pv
# vinLbtg/SHUB2RjebYIM9W0jVOR4U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9Ak
# vUCgvxm2EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6LGYnn8AtqgcKBGUIZUnWK
# NsIdw2FzLixre24/LAl4FOmRsqlb30mjdAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2
# kQH2zsZ0/fZMcm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0SCyxTkctwRQEcb9k+SS+
# c23Kjgm9swFXSVRk2XPXfx5bRAGOWhmRaw2fpCjcZxkoJLo4S5pu+yFUa2pFEUep
# 8beuyOiJXk+d0tBMdrVXVAmxaQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L+Dvk
# txW/tM4+pTFRhLy/AsGConsXHRWJjXD+57XQKBqJC4822rpM+Zv/Cuk0+CQ1Zyvg
# DbjmjJnW4SLq8CdCPSWU5nR0W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/
# 2XBjU02N7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIDWTCCAkECAQEwggEBoYHZpIHW
# MIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQL
# EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsT
# Hm5TaGllbGQgVFNTIEVTTjo2QjA1LTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9z
# b2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUAFU9eSpdxs0a0
# 6JFIuGFHIj/I+36ggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAx
# MDANBgkqhkiG9w0BAQsFAAIFAOvymOcwIhgPMjAyNTA2MTAxMTQzMzVaGA8yMDI1
# MDYxMTExNDMzNVowdzA9BgorBgEEAYRZCgQBMS8wLTAKAgUA6/KY5wIBADAKAgEA
# AgIpmwIB/zAHAgEAAgIToDAKAgUA6/PqZwIBADA2BgorBgEEAYRZCgQCMSgwJjAM
# BgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEB
# CwUAA4IBAQBfjqEcvZXPsAEnK5d74O1BY27W4Of1ynXh3/WHyuYrglZjkbHydNUr
# iUm08Mx4TOAh3+aZBUDXfoQi2kVnuUmXmHNinequihzT/XP66TUVsK/5NupsVbxs
# hjm317O5z3x2Fbp/zWbFyARtI1/e1Uog2soaH2hFSPd/t3fORyKWHsLClmBnS/7m
# FYKpDt0L41f2QCqwTsh27tQB3NDbF1Ovsmd+IuJtzxamxBvnOVR81jQ2lSBvdPEQ
# YUPswiAk0sCrnGKn4YDSiQag/ZOcN2b8v3jHUdtYM69WqJVKZRI1TGth+hqYOhhQ
# C/urPjmhXOAdCEkQ2/6rdKUI+lpqzQRxMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UE
# BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc
# BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0
# IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAH2gy8malRdIsEAAQAAAfYwDQYJYIZI
# AWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG
# 9w0BCQQxIgQgv4EkjT41eC7AEsyi15KMVRiNKuf0nQ1zAVo2PuO3P/YwgfoGCyqG
# SIb3DQEJEAIvMYHqMIHnMIHkMIG9BCArYUzxlF6m5USLS4f8NXL/8aoNEVdsCZRm
# F+LlQjG2ojCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n
# dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y
# YXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMz
# AAAB9oMvJmpUXSLBAAEAAAH2MCIEIAumX33h4Mf6dw83n+ZQQf2Y++iuRXEWjvp+
# ESrUWxiQMA0GCSqGSIb3DQEBCwUABIICACPRxxfv5q4RHoXJLPYvO7IiHOzRzl5G
# XrmvIzLRyJp/n93OCBtn7hvfnw5F/rb/zPLIlvG6xa59EsEw+jzQrxQNO/t6OOyz
# BTXTMaVtYtXEarFYFomUq5EoNVxoAeZv953mgvxtYEEe6dPT7qhnnXH7mWIhUsIC
# iCyVKNTufZP8kTIjD7F6/nVBamIR3ov7SqFwedJcZAKYxbvd6BO5wPG+v7uMmqAa
# Z1u7nWco0wJStI70QfTGqOsEQiIBVuYdFmTBej1NiWBFcBKYF01+fLBRdeMYfvB7
# TTz2z0U4Y+/9uxcY4uXiG5ZvGECbvEtkA9RflrvJdHDknW+6EqovLDRu/g6OtyWX
# zJgbHQmD51zraAc/rRb98m2y+jSM+hsLMjXd9wo9memWFm++cpqmsH/GIBYvNLWH
# PL4nSdinSoCB7MXqm6hr5xaFi6Ay55kkhFdSDafzKKUsxmu1793Qa1eDU2ZrOZf7
# ecEEHSzXakkrC2Pkkrtsyekghl28yUnis9auElFD8OnZFkoKzq1RVKFcNXwbsR7d
# dq5vCe4LfQfqrZ5EuFldIi2MpvaPirsBfSJUWoudkroI6lhNq0ZmH4LKqvWLrIsy
# 1rWk9SL+onE+7JGe9YTP++udFrrmv6w8RAzVZsipZp1d8ipHLXgkWk4SivQNnP0U
# eyvgYnVRIpVZ
# SIG # End signature block