AzStackHciSBEHealth/AzStackHciSBEHealth.psm1

<#############################################################
 # #
 # Copyright (C) Microsoft Corporation. All rights reserved. #
 # #
 #############################################################>

Import-Module $PSScriptRoot\AzStackHci.SBEHealth.Helpers.psm1 -DisableNameChecking -Global
 $MetaData = @{
    "OperationType" =  @("PreUpdate", "PreUpdateJIT") # PreUpdateJIT should be removed after 2508
    "UIName" = 'Azure Stack HCI SBE Health'
    "UIDescription" = 'Check SBE Health requirements'
}

function Test-AzStackHciSBEHealth
{
    param (
        [parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        $Parameters,

        [parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        $OperationType,

        [parameter(Mandatory = $false)]
        [ValidateSet('Small','Medium','Large')]
        [String]
        $HardwareClass = "Medium",

        [parameter(Mandatory = $false)]
        [ValidateSet('Standard','Stretch','RackAware')]
        [String]
        $ClusterPattern = "Standard",

        [parameter(Mandatory = $false)]
        [switch]
        $FailFast
    )
    try
    {
        $backupPSModuleAutoLoadingPreference = $PSModuleAutoLoadingPreference
        # Disable module auto-loading and explicitly import modules needed.
        $PSModuleAutoLoadingPreference = [System.Management.Automation.PSModuleAutoLoadingPreference]::None
        Import-Module Microsoft.PowerShell.Utility -Verbose:$false
        Import-Module Microsoft.PowerShell.Management -Verbose:$false
        Import-Module NetTCPIP -Verbose:$false
        Import-Module DnsClient -Verbose:$false
        Import-Module PackageManagement -Verbose:$false

        # Import Module via Helper in case this is update
        [EnvironmentValidator]::EnvironmentValidatorImport($Parameters)
        $ENV:EnvChkrOp = $OperationType
        Trace-Execution "Starting SBE Health validation for $OperationType"
        $allResult = @()
        $exitEarly = $false
        # Start with default of running SBE from local SBE Cache paths (needed for Deployment and AddNode since CSV doesn't exist or isn't accessible yet)
        $runFrom = "LOCAL"

        $runtimeParameters = $Parameters.RunInformation['RuntimeParameter']
        $extendedTests = $runtimeParameters["ExtendedTests"]
        if ($null -ne $extendedTests)
        {
            [bool]$hasExtendedTests = [System.Boolean]::Parse($extendedTests)
        }
        else
        {
            [bool]$hasExtendedTests = $false
        }

        # For deployments, use the C:\SBE directory to signal to skip SBE checks for "just" this deployment
        $deploymentExcludeAllFileDirectory = "C:\SBE"

        # If there is an SBE installed, used the installed CSV path to skip all future system and pre-update checks
        $updateExcludeAllFileDirectory = [System.Environment]::GetEnvironmentVariable("SBEInstalledMetadata", "Machine")
        if ([System.String]::IsNullOrWhiteSpace($updateExcludeAllFileDirectory))
        {
            # if there is no SBE installed yet, fall back to the deployment exclude path
            $updateExcludeAllFileDirectory = $deploymentExcludeAllFileDirectory
        }
        $sbeExcludeAllFilePath = Join-Path -Path $updateExcludeAllFileDirectory -ChildPath "ExcludeAllSBEHealthChecks.txt"

        Trace-Execution "Confirming we intend to run SBE Health checks during Deployment"
        if (Test-Path -Path $sbeExcludeAllFilePath)
        {
            Trace-Execution "Exclusion file found at $sbeExcludeAllFilePath. Skipping SBE Health checks."
            $allResult += (New-Object -TypeName PsObject -Property @{Result = 'INFORMATIONAL'; FailedResult = $null; ExecutionDetail = "SBE Health validation has been skipped as exclusion file was found at $sbeExcludeAllFilePath."})
            $exitEarly = $true
        }
        elseif ($OperationType -eq 'Deployment')
        {
            Trace-Execution "Checking SBE Configuration"
            $sbeConfigNuget = Get-ASArtifactPath -NugetName "Microsoft.AzureStack.SBEConfiguration"
            # Only run tests if the SBE Package is 4.1.x.x or later
            if ($sbeConfigNuget -match "SBEConfiguration.([\d\.]+)")
            {
                $sbeVersion = $Matches[1]
                $sbeVersionFormatted = "1.0"
                if ([System.Version]::TryParse($sbeVersion,[ref]$sbeVersionFormatted))
                {
                    if($sbeVersionFormatted -lt [System.Version]"4.1")
                    {
                        Trace-Execution "SBE Configuration nuget '$($sbeConfigNuget)' must be version 4.1.x.x or later to support SBE health checks."
                        
                        #try one more time the older way
                        $sbeVerMatch = "SBEConfiguration.(4\.[1-9])|([5-9])\."
                        if ($sbeConfigNuget -notmatch $sbeVerMatch)
                        {
                            $detailedMessage = "SBE Configuration nuget '$($sbeConfigNuget)' does not support this test. Must be version 4.1.x.x or later."
                            Trace-Execution $detailedMessage
                            $allResult += (New-Object -TypeName PsObject -Property @{Result = 'INFORMATIONAL'; FailedResult = $null; ExecutionDetail = $detailedMessage})
                            $exitEarly = $true
                        }
                        else
                        {
                            Trace-Execution "Matched old way. SBE version of '$sbeVersion' is new enough to that we should check to see if HealthChecks are implemented."
                        }
                    }
                    else
                    {
                        Trace-Execution "SBE version of '$sbeVersion' is new enough to that we should check to see if HealthChecks are implemented."
                    }
                }
                else
                {
                    $detailedMessage = "SBE Configuration nuget '$($sbeConfigNuget)' version of '$sbeVersion' is malformed. Must be version 4.1.x.x or later."
                    Trace-Execution $detailedMessage
                    $allResult += (New-Object -TypeName PsObject -Property @{Result = 'INFORMATIONAL'; FailedResult = $null; ExecutionDetail = $detailedMessage})
                    $exitEarly = $true
                }
            }
            $sbeMetadataPath = Join-Path -Path $sbeConfigNuget -ChildPath "content"
            $oemMetadataXmlPath = Join-Path -Path $sbeMetadataPath -ChildPath "oemMetadata.xml"
            if ($false -eq (Test-Path -Path $oemMetadataXmlPath))
            {
                throw "Unable to locate oemMetadata.xml file at $oemMetadataXmlPath"
            }
            [xml]$oemMetadata = Get-Content -Path $oemMetadataXmlPath
            $sbeVersion = $oemMetadata.UpdatePackageManifest.UpdateInfo.Version

            Trace-Execution "Getting SBE Package source local path"
            $sbeSourcePath = $Parameters.Roles["SBE"].PublicConfiguration.PublicInfo.SBEContentPaths.SBESeedNodePath
            if ($null -eq $sbeSourcePath)
            {
                $detailedMessage = "SBE Seed Node Path property did not get populated. Please retry the deployment."
                Trace-Execution $detailedMessage
                throw $detailedMessage
            }
        }
        elseif ($OperationType -eq 'PreUpdate')
        {
            $aldoSupport = [System.Environment]::GetEnvironmentVariable("DISCONNECTED_OPS_SUPPORT", "Machine")
            # note: order matters here - $true -eq $aldoSupport won't work because $aldoSupport is a string
            if ($null -ne $aldoSupport -and $aldoSupport -eq "True")
            {
                Trace-Execution "ALDO Disconnected Operations support detected. Skipping SBE endpoint check."
            }
            else
            {
                $manifestFilePath = (New-TemporaryFile).FullName
                if (Test-Path -Path $manifestFilePath)
                {
                    Remove-Item -Path $manifestFilePath -Force -ErrorAction SilentlyContinue
                }

                Trace-Execution "Validating SBE manifest endpoint connectivity"
                # First, make sure firewall rules are not blocking access to the manifest endpoint
                try
                {
                    Import-Module Microsoft.AzureStack.Lcm.PowerShell -Verbose:$false
                    $diagnosticInfo = Get-SolutionDiscoveryDiagnosticInfo 3>$null 4>$null
                    $sbeEndpoint = $diagnosticInfo.Configuration.ComponentUris["SBE"]
                    Trace-Execution "SBE manifest endpoint: $sbeEndpoint"
                }
                catch
                {
                    Trace-Execution "Failed to get SBE manifest endpoint from Get-SolutionDiscoveryDiagnosticInfo. Error: $($PSItem.Exception.Message)"
                }

                $endpointAccessResult = New-SBEHealthResultObject -TestName 'Test-Endpoint-Connectivity' -TargetName $env:ComputerName -Severity 'INFORMATIONAL' -Status 'SUCCESS' -Description "Validate SBE manifest reachable: $sbeEndpoint"
                $endpointAccessResult.TargetResourceID = $sbeEndpoint
                $endpointAccessResult.TargetResourceName = 'Hardware vendor Solution Builder Extension manifest'

                # Test connectivity to SBE manifest endpoint
                $endpointAccessResult = Test-SBEEndpointConnectivity -SbeEndpointUri $sbeEndpoint -EndpointAccessResult $endpointAccessResult -ManifestFilePath $manifestFilePath

                # continue with other tests - having bad firewall rules doesn't mean we can skip the rest of the tests
                $allResult += $endpointAccessResult

                if (Test-Path -Path $manifestFilePath)
                {
                    $endpointContentsResult  = New-SBEHealthResultObject -TestName 'Test-Endpoint-Matches-ModelSKU' -TargetName $env:ComputerName -Severity 'INFORMATIONAL' -Status 'SUCCESS' -Description "Validate SBE manifest matches hardware model:$modelValue, sku:$skuValue"
                    try
                    {
                        Trace-Execution "Attempting to get the path to the UpdateService.Validation module."
                        $validationModuleConsolidatedPath = Join-Path -Path (Get-ASArtifactPath -NugetName "Microsoft.AzureStack.UpdateService.Validation") -ChildPath "lib\net472\UpdateService.Validation.dll"
                        Trace-Execution "Will use: $validationModuleConsolidatedPath"
                    }
                    catch
                    {
                        Trace-Execution "Get-ASArtifactPath failed to locate Microsoft.AzureStack.UpdateService.Validation nuget."
                    }
                    if ($null -eq $validationModuleConsolidatedPath -or (Test-Path -Path $validationModuleConsolidatedPath) -eq $false)
                    {
                        $msg = "Unable to locate UpdateService.Validation module at expected path: $validationModuleConsolidatedPath"
                        Trace-Execution $msg
                        $endpointContentsResult.Description = $msg
                        $endpointContentsResult.Remediation = "Ensure the LCM Extension is properly installed and that C:\NugetStore\Microsoft.AzureStack.UpdateService.Validation nuget is available."
                    }
                    else
                    {
                        Trace-Execution "Loading UpdateService.Validation module from: $validationModuleConsolidatedPath"
                        $job = Start-Job -ScriptBlock {
                            try {
                                # Load DLL for running SBE validation
                                Import-Module "$($using:validationModuleConsolidatedPath)" -Force
                            }
                            catch {
                                return [PSCustomObject]@{
                                    Code = "ErrorUnableToLoadDll"
                                    Message = "Error loading $($using:validationModuleConsolidatedPath): $($_.Exception.Message)"
                                }
                            }
                            try {
                                $validator = [UpdateService.Validation.DeploymentSbeValidator]::new()
                                $defaultEndpoint = $validator.GetSbeDiscoveryUri()
                                return [PSCustomObject]@{
                                    Code = "Ok"
                                    Message = $defaultEndpoint
                                }
                            }
                            catch {

                                return [PSCustomObject]@{
                                    Code = "ErrorUnableToGetEndpoint"
                                    Message = "Error running SBE validation: $($_.Exception.Message)"
                                }
                            }
                        }
                        $result = $job | Receive-Job -Wait -AutoRemoveJob

                        if ($result.Code -ne "Ok")
                        {
                            $msg = $result.Message
                            Trace-Execution $msg
                            $endpointContentsResult.Status = 'FAILURE'
                            $endpointContentsResult.Description = $msg
                            $endpointContentsResult.Remediation = "Ensure the LCM Extension is properly installed and that C:\NugetStore\Microsoft.AzureStack.UpdateService.Validation nuget is available."
                        }
                        elseif($result.Message -eq $sbeEndpoint)
                        {
                            Trace-Execution "SBE manifest endpoint $sbeEndpoint matches the default endpoint will skip model/SKU check as we don't intend to advise users to change from default."
                            $endpointContentsResult.Description = "SBE manifest endpoint $sbeEndpoint matches the default endpoint. No model/SKU check needed."
                        }
                        else {
                            Trace-Execution "SBE manifest endpoint $sbeEndpoint does not match the default endpoint."

                            Trace-Execution "Proceeding with model/SKU check against manifest at $manifestFilePath to see if the override endpoint is valid for this hardware."
                            $endpointContentsResult = Get-ManifestMatchesModelandSKUResult -SBEManifestFilePath $manifestFilePath -EndpointContentsResult $endpointContentsResult
                        }

                    }
                    $allResult += $endpointContentsResult
                }
            }

            # Next, get the installed SBE info - we might need to fall back to this SBE
            $sbeVersion = '1.0'
            $sbeSourcePath = [System.Environment]::GetEnvironmentVariable("SBEInstalledContent", "Machine")
            $sbeMetadataPath = [System.Environment]::GetEnvironmentVariable("SBEInstalledMetadata", "Machine")
            if ($null -ne $sbeMetadataPath)
            {
                $oemMetadataXmlPath = Join-Path -Path $sbeMetadataPath -ChildPath "oemMetadata.xml"
                if ($true -eq (Test-Path -Path $oemMetadataXmlPath))
                {
                    [xml]$oemMetadata = Get-Content -Path $oemMetadataXmlPath
                    $sbeVersion = $oemMetadata.UpdatePackageManifest.UpdateInfo.Version
                }
            }
            $sbeInstalled = $false

            $installSBEResult = New-SBEHealthResultObject -TestName 'Test-Installed-SBE-Env-Vars' -TargetName $env:ComputerName -Status 'SUCCESS' -Description "Validate Installed SBE Env Vars"

            if (($null -ne $sbeSourcePath) -and ($null -ne $sbeMetadataPath) -and ($null -ne $sbeVersion))
            {
                $detailedMessage = "Detected SBE $sbeVersion is installed. TBD if we will use this or a newer version for checks."
                $sbeInstalled = $true
            }
            elseif ((($null -eq $sbeSourcePath) -and ($null -eq $sbeMetadataPath) -and ($null -eq $sbeVersion)) -or ($sbeVersion -eq '1.0'))
            {
                Trace-Execution "SBE ENV vars - content: [$sbeSourcePath], metadata: [$sbeMetadataPath], sbeVersion [$sbeVersion]"
                $detailedMessage = "No SBE installed. May be skipping checks if this isn't an update including an SBE."
            }
            else
            {
                $detailedMessage = "Inconsistent SBE ENV vars!! content: [$sbeSourcePath], metadata: [$sbeMetadataPath], sbeVersion [$sbeVersion]"
                $installSBEResult.Severity = 'WARNING'
                $installSBEResult.Remediation = "Update to latest available Solution Builder Extension to restore consistent SBE state."
            }
            Trace-Execution $detailedMessage
            $installSBEResult.AdditionalData.Detail = $detailedMessage
            $allResult += $installSBEResult

            # Figure out if this is a period "system" healthcheck or "preUpdate" HealthCheck
            if ($null -eq $runtimeParameters)
            {
                $runtimeParameters = $Parameters.RunInformation['RuntimeParameter']
            }
            $packagePath = $runtimeParameters["UpdatePackagePath"]
            $updateVersion = $runtimeParameters["UpdateVersion"]
            if ($null -eq $packagePath)
            {
                Trace-Execution "System HealthCheck detected."
                if ($sbeInstalled)
                {
                    Trace-Execution -Message "System check scenario - Using installed SBE - content: [$sbeSourcePath], metadata: [$sbeMetadataPath], sbeVersion [$sbeVersion]"
                    # For the "installed" system check scenario we want to just run from the installed CSV dir as it is already properly organzied (with split content and metadata)
                    # Note: We can't run from the update CSV as it is messy - has combined content and metadata as well as the zip files
                    $runFrom = "CSV"
                }
                else
                {
                    # Early exit, there is no SBE to test with
                    $exitEarly = $true
                }
            }
            else
            {
                Trace-Execution "Update package @: [$packagePath]"
                Trace-Execution "Update version @: [$updateVersion]"
                $updateEnvVarsForSBE = $false

                # The package path is expected to be a full path to an update package file (e.g. a self-
                # extracting .exe, metadata.xml, or oemMetadata.xml file). The content is in the same dir.

                # Also paths from runtime params often have extra \\ in them like: C:\\ClusterStorage\\Infrastructure_1\\Shares\\SU1_Infrastructure_1\\Updates\\Packages\Solution99.9999.9.11\metadata.xml
                $updatePackageBaseDir = (Split-Path $packagePath -Parent).Replace( '\\', '\')
                if ($updateVersion -match '^\d{2}\.\d{4}\..*')
                {
                    # Version is something like 10.2405.1.13 and indicates this is a solution update
                    $potentialSBEPath = "$updatePackageBaseDir\SBE"
                    if (Test-Path -Path $potentialSBEPath)
                    {
                        $sbeSourcePath = $potentialSBEPath
                        $sbeMetadataPath = $potentialSBEPath
                        $oemMetadataXmlPath = Join-Path -Path $sbeMetadataPath -ChildPath "oemMetadata.xml"
                        if ($false -eq (Test-Path -Path $oemMetadataXmlPath))
                        {
                            throw "Unable to locate oemMetadata.xml file at $oemMetadataXmlPath"
                        }
                        [xml]$oemMetadata = Get-Content -Path $oemMetadataXmlPath
                        $sbeVersion = $oemMetadata.UpdatePackageManifest.UpdateInfo.Version
                        Trace-Execution "Using SBE from Solution Update - content: [$sbeSourcePath], metadata: [$sbeMetadataPath], sbeVersion [$sbeVersion]"
                        $updateEnvVarsForSBE = $true
                    }
                    elseif ($sbeInstalled)
                    {
                        Trace-Execution -Message "Non-SBE Solution update scenario - Using installed SBE - content: [$sbeSourcePath], metadata: [$sbeMetadataPath], sbeVersion [$sbeVersion]"
                        # run from the installed CSV dir as it is already properly organzied (with split content and metadata)
                        $runFrom = "CSV"
                    }
                    else
                    {
                        # Early exit, this is a Solution update w/o an SBE and there is no SBE installed
                        Trace-Execution "Skipping tests as no SBE is installed and there is no SBE from this Solution Update"
                        $exitEarly = $true
                    }
                }
                elseif ($updateVersion -match '\d\.\d\.\d{4}\..*')
                {
                    $sbeSourcePath = $updatePackageBaseDir
                    $sbeMetadataPath = $updatePackageBaseDir
                    $sbeVersion = $updateVersion
                    $updateEnvVarsForSBE = $true
                    Trace-Execution "Using SBE from SBE-only Update - content: [$sbeSourcePath], metadata: [$sbeMetadataPath], sbeVersion [$sbeVersion]"
                }
            }
            $sbeVersionFormatted = "1.0"
            if ([System.Version]::TryParse($sbeVersion,[ref]$sbeVersionFormatted))
            {
                if($sbeVersionFormatted -lt [System.Version]"4.1")
                {
                    $versionResult = New-SBEHealthResultObject -TestName 'Test-Version-Supports-$OperationType-Tests' -TargetName $env:ComputerName -Status 'SUCCESS' -Description "Validate SBE Version supports $OperationType type tests."
                    $detailedMessage = "Skipping tests. SBE '$OperationType' type Health Checks are only supported with version 4.1.x.x or later (SBE version was $sbeVersion)."
                    Trace-Execution $detailedMessage
                    $versionResult.AdditionalData.Detail = $detailedMessage
                    $allResult += $versionResult
                    $exitEarly = $true
                }
            }
        }
        elseif ($OperationType -eq 'PreAddNode')
        {
            # TODO : Future placeholder for when AddNode tests are implemented
            Trace-Execution "SBE Health validation for $OperationType has not been implemented yet"
            $allResult += (New-Object -TypeName PsObject -Property @{Result = 'INFORMATIONAL'; FailedResult = $null; ExecutionDetail = 'SBE Health validation has been skipped as this OperationType is not yet supported.'})
            $exitEarly = $true
        }
        else
        {
            Trace-Execution "OperationType $OperationType is not implemented"
            $allResult += (New-Object -TypeName PsObject -Property @{Result = 'INFORMATIONAL'; FailedResult = $null; ExecutionDetail = 'SBE Health validation has been skipped as this OperationType is not yet supported.'})
            $exitEarly = $true
        }

        # Done with built-in checks - send result to telemetry channel
        foreach ($res in $allResult)
        {
            Write-ETWResult -Result $res
        }

        if ($exitEarly)
        {
            Trace-Execution "Either decided to skip SBE checks (on request or due to lack of SBE support for health check) or identified a setup issue. Exit early before partner tests."
            # Check if the ParseResult method supports the Parameters
            if ([EnvironmentValidator]::ParseResult.OverloadDefinitions -match 'EceInterfaceParameters')
            {
                Trace-Execution "ParseResult method supports EceInterfaceParameters argument"
                return [EnvironmentValidator]::ParseResult($allResult, 'SBEHealth', $FailFast, $Parameters)
            }
            else
            {
                Trace-Execution "ParseResult method does not support EceInterfaceParameters argument"
                return [EnvironmentValidator]::ParseResult($allResult, 'SBEHealth', $FailFast)
            }
        }

        $priorStageVersion = $null
        $priorStageRootPath = $null
        $priorSBEMeatadataPath = $null
        if ($true -eq $updateEnvVarsForSBE)
        {
            # While we haven't technically staged the SBE yet, set the env vars so some of the helper functions know where to find the metadata files or SBE content
            # IMPORTANT: We MUST put these values back to their prior values when we are done (success or fail). If we don't we will get new + old mismatches:
            # - scenario 1: system checks will fail (old install dir + new metadata path)
            # - scenario 2: after scenario 1 fails we have pre-update checks will fail due to both new and old files being at the cache path (since we do pssession copy-item copy first instead of a robocopy /MIR type copy)
            $priorStageVersion = [System.Environment]::GetEnvironmentVariable("SBEStageVersion", "Machine")
            Trace-Execution "Temporarily changing SBEStageVersion from '$priorStageVersion' to '$sbeVersion'"
            [System.Environment]::SetEnvironmentVariable("SBEStageVersion", $sbeVersion, "Machine")
            $priorStageRootPath = [System.Environment]::GetEnvironmentVariable("SBEStageRootPath", "Machine")
            Trace-Execution "Temporarily changing SBEStageRootPath from '$priorStageRootPath' to '$sbeSourcePath'"
            [System.Environment]::SetEnvironmentVariable("SBEStageRootPath", $sbeSourcePath, "Machine")
            $priorSBEMeatadataPath = [System.Environment]::GetEnvironmentVariable("SBEStagedMetadata", "Machine")
            Trace-Execution "Temporarily changing SBEStagedMetadata from '$priorSBEMeatadataPath' to '$sbeMetadataPath'"
            [System.Environment]::SetEnvironmentVariable("SBEStagedMetadata", $sbeMetadataPath, "Machine")
        }

        Trace-Execution "Getting PSSessions for all hosts"

        # No prep for the partner validators
        $psSession = [EnvironmentValidator]::NewPsSessionAllHosts($Parameters)

        # Update mapping as needed each time envChecker adds a new OperationType that we want to translate to an SBE healthCheck "tag"
        $envCheckerToSBEPrecheckTagMap = @{
            PreUpdate = "Update"
            PostUpdate = "Update"
            PreAddNode = "AddNode"
        }
        $tag = $OperationType
        if ($null -ne $envCheckerToSBEPrecheckTagMap.$OperationType)
        {
            # EnvChecker has invented some new test types that SBE doesn't support - use mapping table to translate to SBE equivalent
            $tag = $envCheckerToSBEPrecheckTagMap.$OperationType
        }
        $params = @{
            PsSession = $psSession
            PassThru = $true
            OutputPath = "$($env:LocalRootFolderPath)\MASLogs\"
            HardwareClass = $HardwareClass
            ClusterPattern = $ClusterPattern
            Tag = $tag
            SBESourcePath = $sbeSourcePath
            SBEMetadataPath = $sbeMetadataPath
            SBEVersion = $sbeVersion
            ECEParameters = $Parameters
            RunFrom = $runFrom
        }
        if ($true -eq $hasExtendedTests)
        {
            $params += @{ExtendedTests = [switch]::Present}
        }

        # Run Partner SBE Health Checks
        [array]$partnerResults = AzStackHci.EnvironmentChecker\Invoke-AzStackHciSBEHealthValidation @params
        if ($null -ne $partnerResults -and $partnerResults.Count -gt 0)
        {
            $allResult += $partnerResults
        }
        # Check if the ParseResult method supports the Parameters
        if ([EnvironmentValidator]::ParseResult.OverloadDefinitions -match 'EceInterfaceParameters')
        {
            Trace-Execution "ParseResult method supports EceInterfaceParameters argument"
            return [EnvironmentValidator]::ParseResult($allResult, 'SBEHealth', $FailFast, $Parameters)
        }
        else
        {
            Trace-Execution "ParseResult method does not support EceInterfaceParameters argument"
            return [EnvironmentValidator]::ParseResult($allResult, 'SBEHealth', $FailFast)
        }
    }
    catch
    {
        Trace-Execution "Validator failed. $PSItem"
        Trace-Execution "$($PSItem.ScriptStackTrace)"
        throw $PSItem
    }
    finally
    {
        #return the EnvVars to default values
        if ($OperationType -eq 'PreUpdate')
        {
            try {
                Trace-Execution "Reverting SBE env vars to default values"
                $templatedSBEPath = $Parameters.Roles["SBE"].PublicConfiguration.PublicInfo.SBEContentPaths.SBESharePath
                $sbeCSVDir = $templatedSBEPath.Replace('{DefaultClusterShare}', $env:InfraCSVRootFolderPath)
                if ($false -eq (Test-Path -Path $sbeCSVDir))
                {
                    Trace-Execution "Unable to locate SBE CSV path at $sbeCSVDir"
                }
                else
                {
                    Trace-Execution "Resolved SBE CSV path to '$sbeCSVDir'"
                    $defaultStagedMetadata  = Join-Path -Path $sbeCSVDir -ChildPath $Parameters.Roles["SBE"].PublicConfiguration.PublicInfo.SBEContentPaths.RelativePaths.CSVStagedMetadataPath
                    $defaultStagedRoot = Join-Path -Path $sbeCSVDir -ChildPath $Parameters.Roles["SBE"].PublicConfiguration.PublicInfo.SBEContentPaths.RelativePaths.CSVStagedContentPath
                    if ($false -eq [System.String]::IsNullOrWhiteSpace($defaultStagedMetadata) -and (Test-Path -Path $defaultStagedMetadata))
                    {
                        $newSBEMeatadataPath = [System.Environment]::GetEnvironmentVariable("SBEStagedMetadata", "Machine")
                        Trace-Execution "Reverting changing SBEStagedMetadata from '$newSBEMeatadataPath' to default '$defaultStagedMetadata'"
                        [System.Environment]::SetEnvironmentVariable("SBEStagedMetadata", $defaultStagedMetadata, "Machine")
                    }
                    if ($false -eq [System.String]::IsNullOrWhiteSpace($defaultStagedRoot) -and (Test-Path -Path $defaultStagedRoot))
                    {
                        $newStageRootPath = [System.Environment]::GetEnvironmentVariable("SBEStageRootPath", "Machine")
                        Trace-Execution "Reverting changing SBEStageRootPath from '$newStageRootPath' to default '$defaultStagedRoot'"
                        [System.Environment]::SetEnvironmentVariable("SBEStageRootPath", $defaultStagedRoot, "Machine")
                    }
                    if ($true -eq $updateEnvVarsForSBE)
                    {
                        # While it is always safe to return the "staged" paths to their defaults, we should only go back to the installed version in the case of a true pre-update check.
                        # Avoid doing this in the daily health check case because it will change from the staged version to the installed version
                        $installedSBEVersion = [System.Environment]::GetEnvironmentVariable("SBEInstallVersion", "Machine")
                        if ([System.String]::IsNullOrWhiteSpace($installedSBEVersion) -or $false -eq $installedSBEVersion.StartsWith("4"))
                        {
                            $installedSBEVersion = "2.1.0.0"
                            Trace-Execution "No SBEis installed, will revert staged SBE back to '$installedSBEVersion'"
                        }
                        $sbeVersion = [System.Environment]::GetEnvironmentVariable("SBEStageVersion", "Machine")
                        Trace-Execution "Reverting changing SBEStageVersion from '$sbeVersion' to the actual installed value '$installedSBEVersion'"
                        [System.Environment]::SetEnvironmentVariable("SBEStageVersion", $installedSBEVersion, "Machine")
                    }
                }
            }
            catch
            {
                Trace-Execution "Failed to revert SBE env vars. $($PSItem.Exception.Message)"
                Trace-Execution "$($PSItem.ScriptStackTrace)"
            }
        }
        if ($psSession)
        {
            if ($backupPSModuleAutoLoadingPreference)
            {
                $PSModuleAutoLoadingPreference = $backupPSModuleAutoLoadingPreference
            }
            $psSession | Microsoft.PowerShell.Core\Remove-PSSession
        }
    }
}

Export-ModuleMember -Function Test-AzStackHciSBEHealth -Variable MetaData

# SIG # Begin signature block
# MIInbgYJKoZIhvcNAQcCoIInXzCCJ1sCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA3kJDqXMQFnbGy
# 1MzPbWO8LkkkrPb5sOwFKsJjMUgnVKCCDMkwggYEMIID7KADAgECAhMzAAACHPrN
# xZvoL37EAAAAAAIcMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAlVTMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBD
# b2RlIFNpZ25pbmcgUENBIDIwMjQwHhcNMjYwNDE2MTg1OTQxWhcNMjcwNDE1MTg1
# OTQxWjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYD
# VQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IB
# DwAwggEKAoIBAQDVsZfgOKmM31HPfoWOoNEiw0SlCiIxUMC0I9NMWbucKOw/e9lP
# oAoehQVu6SG65V4EPzrYsnBnFPNoi4/HoOdjhz1qkrEt4I6tEcxXU6oOeY9zGveC
# /3iBeuhLYxM3M/PkcUoebF+Nednm8OkdSPoDu8imViHPQq/8CQUu0WRR4rE+dMRf
# rpVqfmNi2qWCX94T4MsepijGVkwE//tJg0ryAiYdHT34LSnlG/RSBZmQRGWZ5g8j
# qnKjRParSqMft1gvjuUTVgtWNZfgcLFSK5Wa0myrq8OPcgTGGsRgun+tnSS+IxDT
# xVsAPH1OzvPjwomguByhUe/OcvUN0D5Wmp7xAgMBAAGjggGqMIIBpjAOBgNVHQ8B
# Af8EBAMCB4AwHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0O
# BBYEFNoH7a2YDjOSwpkp6DHcmUS7J+0yMFQGA1UdEQRNMEukSTBHMS0wKwYDVQQL
# EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxFjAUBgNVBAUT
# DTIzMDAxMis1MDc1NjkwHwYDVR0jBBgwFoAUf1k/VCHarU/vBeXmo9ctBpQSCDEw
# YAYDVR0fBFkwVzBVoFOgUYZPaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9w
# cy9jcmwvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNy
# bDBtBggrBgEFBQcBAQRhMF8wXQYIKwYBBQUHMAKGUWh0dHA6Ly93d3cubWljcm9z
# b2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmcl
# MjBQQ0ElMjAyMDI0LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IC
# AQAUnEqhaRXe0T3hIJjvdQErEkrA/7bByjn6t5IArODkkRjzkYwtKMc2yYj2quaN
# rLutWw2YZcngKPy1b71YyDJQTy4NDRwaSh9Tw5thrk3NmcPrAHia5vtcBJ1CgtKK
# 7mQbIcQ22d/N3813ayCDDFewu1+jsZmX+r/aTEqaOM4TVxVtRSkuCy8nAXKuChOK
# Li/zA4XuH8iEYqIsj2YoNaeSxVmeGiERXpKdo3dDmYi0kO5w2D8VS4c3+9h6gElY
# BaAAg/dYErBg27qT3vv0zRDJhJufvCNylA8S7/+8H5E/PV5cng6na9VV/w9OV3qu
# uND6zdGa2EX38Glp50F9AIQk3p2xXmcvorDeM4XJ7UlWYBi6g80J1SSOQnInCYFE
# msfUNn3+1AaTJKSJL83quKArTac2pKhu0Yzzzrzo6HrsRiQKzpnRBb1/dMa6P3hz
# 75XbMRBctNsFhZC07WCmjExdLg2eHW5uV0TY8D5+6wozJf7vF3+WHkYPO85Z+BC6
# U4FkNbYNycZ9cE4j1tXRdyDCfml6c0HWPHjNVDObrv9lKt3qUqFpX38VCqVCyNOO
# 1UcXfQiVjJw32U2WUKZjt/neJKHEBsm9kFsLuWzkQ53+qcaSaytmsCnk2gOglrlD
# 5d3kKyvvAw+rzm0lT8K38P6PLxfZQHhu4W8dV7Av8N2ZmDCCBr0wggSloAMCAQIC
# EzMAAAA5O7Y3Gb8GHWcAAAAAADkwDQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYT
# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBS
# b290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTI0MDgwODIwNTQxOFoX
# DTM2MDMyMjIyMTMwNFowVzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQ
# Q0EgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANgBnB7jOMeq
# lRYHNa265v4IY9fH8TKhemHfPINe1gpLaV3dhg324WwH06LcHbpnsBukCDNitryo
# 0dtS/EW6I/yEL/bLSY8hKpbfQuWusBPr9qazYcDxCW/qnjb5JsI1s8bNOg3bVATv
# QVL4tcf03aTycsz8QeCdM0l/yHRObJ9QqazM1r6VPEOJ7LL+uEEb73w6QCuhs89a
# 1uv1zerOYMnsneRRwCbpyW11IcggU0cRKDDq1pjVJzIbIF6+oiXXbReOsgeI8zu1
# FyQfK0fVkaya8SmVHQ/tOf23mZ4W9k0Ri22QW9p3UgSC5OUDktKxxcCmGL6tXLfO
# GSWHIIV4YrTJTT6PNty5REojHJuZHArkF9VnHTERWoTjAzfI3kP+5b4alUdhgAZ7
# ttOu1bVnXfHaqPYl2rPs20ji03LOVWsh/radgE17es5hL+t6lV0eVHrVhsssROWJ
# uz2MXMCt7iw7lFPG9LXKGjsmonn2gotGdHIuEg5JnJMJVmixd5LRlkmgYRZKzhxS
# CwyoGIq0PhaA7Y+VPct5pCHkijcIIDm0nlkK+0KyepolcqGm0T/GYQRMhHJlGOOm
# VQop36wUVUYklUy++vDWeEgEo4s7hxN6mIbf2MSIQ/iIfMZgJxC69oukMUXCrOC3
# SkE/xIkgpfl22MM1itkZ35nNXkMolU1lAgMBAAGjggFOMIIBSjAOBgNVHQ8BAf8E
# BAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFH9ZP1Qh2q1P7wXl5qPX
# LQaUEggxMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMA8GA1UdEwEB/wQFMAMB
# Af8wHwYDVR0jBBgwFoAUci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBP
# oE2gS4ZJaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv
# TWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRSMFAw
# TgYIKwYBBQUHMAKGQmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMv
# TWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNydDANBgkqhkiG9w0BAQwFAAOC
# AgEAFJQfOChP7onn6fLIMKrSlN1WYKwDFgAddymOUO3FrM8d7B/W/iQ6DxXsDn7D
# 5W4wMwYeLystcEqfkjz4NURRgazyMu5yRzQh4LqjA4tStTcJh1opExo7nn5PuPBY
# nbu0+THSuVHTe0VTTPVhily/piFrDo3axQ9P4C+Ol5yet+2gTfekICS5xS+cYfSI
# vgn0JksVBVMYVI5QFu/qhnLhsEFEUzG8fvv0hjgkO+lkpV9ty6GkN4vdnd7ya6Q6
# aR9y34aiM1qmxaxBi6OUnyNl6fkuun/diTFnYDLTppOkr/mg5WSfCiDVMNCxtj4w
# PKC5OmHm1DQIt/MNokbbH3UGsFP1QbzsLocuSqLCvH09Io3fDPTmscR9Y75G4qX7
# RTX8AdBPo0I6OEojf39zuFZt0qOHm65YWQE69cZM2ueE1MB05dNNgHK9gTE7zKvK
# /fg8B2qjW88MT/WF5V5uvZGtqa9FSL2RazArA+rDPuf6JGYz4HpgMZHB4S6szWSK
# YBv0VisCzfxgeU+dquXW9bd0auYlOB58DPcOYKdc3Se94g+xL4pcEhbB54JOgAkw
# YTu/9dLeH2pDqeJZAABVDWRQCaXfO5LgyKwKCLYXpigrZYCjUSBcr+Ve8PFWMhVT
# Ql0v4q8J/AUmQN5W4n101cY2L4A7GTQG1h32HHAvfQESWP0xghn7MIIZ9wIBATBu
# MFcxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# KDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIc
# +s3Fm+gvfsQAAAAAAhwwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwG
# CisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZI
# hvcNAQkEMSIEIAjohyO4y0aScXtYUARvHFLnT3yaQwCk0SPlxyDkJDCJMEIGCisG
# AQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3
# Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEBBQAEggEAq15+U6LGYXbcq8lbCrHv
# j2TEow32eZYKPlGH9DmcSikIN1znuzjvMDgRLcWV5LW8aZkWL7659y7y19h03Z4E
# O/q2CJRDHQ4sTee2x6BbcmOXp+c92ak4wOu4GA/xrbQjuFe4PR8tqxPT11NiLC1u
# byBuAtLiiHI1wTxx2snGeJd5/4EEt2NBSsdtwn2JiUFt7yQvn2kDo4cyGi39Vz7d
# 1u8liyn/LiwlhYyLVZsaFZYzlk//tV6sDk1xMSp9uk1K0lXmZPwFQyGX6PWvOWEd
# EmSQ+nIF844DdcdW0EzRxoFB8m5PU853b29h2Am+7mzP9j4nRxGEMQax3LrLwN9J
# zKGCF60wghepBgorBgEEAYI3AwMBMYIXmTCCF5UGCSqGSIb3DQEHAqCCF4YwgheC
# AgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFaBgsqhkiG9w0BCRABBKCCAUkEggFFMIIB
# QQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQCAQUABCDhcCzuajjpJqGJUc2q
# D6GmBlj9tqfU+4CSHZ4cq+9OEAIGaewquMvBGBMyMDI2MDUwMzE0MzExMS4xMzRa
# MASAAgH0oIHZpIHWMIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
# aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0
# ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo1OTFBLTA1RTAtRDk0NzElMCMG
# A1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCEfswggcoMIIFEKAD
# AgECAhMzAAACFI3NI0TuBt9yAAEAAAIUMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNV
# BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m
# dCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI1MDgxNDE4NDgxOFoXDTI2MTExMzE4
# NDgxOFowgdMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD
# VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTAr
# BgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEnMCUG
# A1UECxMeblNoaWVsZCBUU1MgRVNOOjU5MUEtMDVFMC1EOTQ3MSUwIwYDVQQDExxN
# aWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEFAAOC
# Ag8AMIICCgKCAgEAyU+nWgCUyvfyGP1zTFkLkdgOutXcVteP/0CeXfrF/66chKl4
# /MZDCQ6E8Ur4kqgCxQvef7Lg1gfso1EWWKG6vix1VxtvO1kPGK4PZKmOeoeL68F6
# +Mw2ERPy4BL2vJKf6Lo5Z7X0xkRjtcvfM9T0HDgfHUW6z1CbgQiqrExs2NH27rWp
# UkyTYrMG6TXy39+GdMOTgXyUDiRGVHAy3EqYNw3zSWusn0zedl6a/1DbnXIcvn9F
# aHzd/96EPNBOCd2vOpS0Ck7kgkjVxwOptsWa8I+m+DA43cwlErPaId84GbdGzo3V
# oO7YhCmQIoRab0d8or5Pmyg+VMl8jeoN9SeUxVZpBI/cQ4TXXKlLDkfbzzSQriVi
# QGJGJLtKS3DTVNuBqpjXLdu2p2Yq9ODPqZCoiNBh4CB6X2iLYUSO8tmbUVLMMEeg
# bvHSLXQR88QNICjFoBBDCDydoTo9/TNkq80mO77wDM04tPdvbMmxT01GTod60JJx
# UGmMTgseghdBGjkN+D6GsUpY7ta7hP9PzLrs+Alxu46XT217bBn6EwJsAYAc9C28
# mKRUcoIZWQRb+McoZaSu2EcSzuIlAaNIQNtGlz2PF3foSeGmc/V7gCGs8AHkiKwX
# zJSPftnsH8O/R3pJw2D/2hHE3JzxH2SrLX1FdI7Drw145PkL0hbFL6MVCCkCAwEA
# AaOCAUkwggFFMB0GA1UdDgQWBBTbX/bs1cSpyTYnYuf/Mt9CPNhwGzAfBgNVHSME
# GDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5odHRw
# Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBUaW1l
# LVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwGCCsG
# AQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01p
# Y3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNVHRMB
# Af8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDAN
# BgkqhkiG9w0BAQsFAAOCAgEAP3xp9D4Gu0SH9B+1JH0hswFquINaTT+RjpfEr8Um
# UOeDl4U5uV+i28/eSYXMxgem3yBZywYDyvf4qMXUvbDcllNqRyL2Rv8jSu8wclt/
# VS1+c5cVCJfM+WHvkUr+dCfUlOy9n4exCPX1L6uWwFH5eoFfqPEp3Fw30irMN2So
# nHBK3mB8vDj3D80oJKqe2tatO38yMTiREdC2HD7eVIUWL7d54UtoYxzwkJN1t7gE
# EGosgBpdmwKVYYDO1USWSNmZELglYA4LoVoGDuWbN7mD8VozYBsfkZarOyrJYlF/
# UCDZLB8XaLfrMfMyZTMCOuEuPD4zj8jy/Jt40clrIW04cvLhkhkydBzcrmC2HxeE
# 36gJsh+jzmivS9YvyiPhLkom1FP0DIFr4VlqyXHKagrtnqSF8QyEpqtQS7wS7ZzZ
# F0eZe0fsYD0J1RarbVuDxmWsq45n1vjRdontuGUdmrG2OGeKd8AtiNghfnabVBbg
# pYgcx/eLyW/n40eTbKIlsm0cseyuWvYFyOqQXjoWtL4/sUHxlWIsrjnNarNr+POk
# L8C1jGBCJuvm0UYgjhIaL+XBXavrbOtX9mrZ3y8GQDxWXn3mhqM21ZcGk83xSRqB
# 9ecfGYNRG6g65v635gSzUmBKZWWcDNzwAoxsgEjTFXz6ahfyrBLqshrjJXPKfO+9
# Ar8wggdxMIIFWaADAgECAhMzAAAAFcXna54Cm0mZAAAAAAAVMA0GCSqGSIb3DQEB
# CwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYD
# VQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAe
# Fw0yMTA5MzAxODIyMjVaFw0zMDA5MzAxODMyMjVaMHwxCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0
# YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5OGm
# TOe0ciELeaLL1yR5vQ7VgtP97pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/XE/H
# ZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY6GB9alKDRLemjkZrBxTzxXb1hlDc
# wUTIcVxRMTegCjhuje3XD9gmU3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7M62A
# W36MEBydUv626GIl3GoPz130/o5Tz9bshVZN7928jaTjkY+yOSxRnOlwaQ3KNi1w
# jjHINSi947SHJMPgyY9+tVSP3PoFVZhtaDuaRr3tpK56KTesy+uDRedGbsoy1cCG
# MFxPLOJiss254o2I5JasAUq7vnGpF1tnYN74kpEeHT39IM9zfUGaRnXNxF803RKJ
# 1v2lIH1+/NmeRd+2ci/bfV+AutuqfjbsNkz2K26oElHovwUDo9Fzpk03dJQcNIIP
# 8BDyt0cY7afomXw/TNuvXsLz1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9ahhaYQFz
# ymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZki1ugpoMhXV8wdJGUlNi5UPkLiWHz
# NgY1GIRH29wb0f2y1BzFa/ZcUlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV2xo3
# xwgVGD94q0W29R6HXtqPnhZyacaue7e3PmriLq0CAwEAAaOCAd0wggHZMBIGCSsG
# AQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUpzxD/
# LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJlpxtTNRnpcjBcBgNVHSAEVTBTMFEG
# DCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29m
# dC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYIKwYB
# BQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8G
# A1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQw
# VgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9j
# cmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUF
# BwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3Br
# aS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcNAQEL
# BQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/ypb+pcFLY+TkdkeLEGk5c9MTO1OdfC
# cTY/2mRsfNB1OW27DzHkwo/7bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYAA7AF
# vonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM9W0jVOR4U3UkV7ndn/OOPcbzaN9l
# 9qRWqveVtihVJ9AkvUCgvxm2EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6LGYnn
# 8AtqgcKBGUIZUnWKNsIdw2FzLixre24/LAl4FOmRsqlb30mjdAy87JGA0j3mSj5m
# O0+7hvoyGtmW9I/2kQH2zsZ0/fZMcm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0SCyx
# TkctwRQEcb9k+SS+c23Kjgm9swFXSVRk2XPXfx5bRAGOWhmRaw2fpCjcZxkoJLo4
# S5pu+yFUa2pFEUep8beuyOiJXk+d0tBMdrVXVAmxaQFEfnyhYWxz/gq77EFmPWn9
# y8FBSX5+k77L+DvktxW/tM4+pTFRhLy/AsGConsXHRWJjXD+57XQKBqJC4822rpM
# +Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU5nR0W2rRnj7tfqAxM328y+l7vzhw
# RNGQ8cirOoo6CGJ/2XBjU02N7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIDVjCCAj4C
# AQEwggEBoYHZpIHWMIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
# aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0
# ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo1OTFBLTA1RTAtRDk0NzElMCMG
# A1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIa
# AxUA2RysX196RXLTwA/P8RFWdUTpUsaggYMwgYCkfjB8MQswCQYDVQQGEwJVUzET
# MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV
# TWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1T
# dGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQsFAAIFAO2hNKgwIhgPMjAyNjA1MDMw
# MjQyNDhaGA8yMDI2MDUwNDAyNDI0OFowdDA6BgorBgEEAYRZCgQBMSwwKjAKAgUA
# 7aE0qAIBADAHAgEAAgIA/DAHAgEAAgISUTAKAgUA7aKGKAIBADA2BgorBgEEAYRZ
# CgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0G
# CSqGSIb3DQEBCwUAA4IBAQAc2Yzc77kwZuH46ShXvStPqvvycwk+yq9klOHFkRvR
# PgLHSofJciKLjwGzsi0wfb5jadJcoJxDENCEmkR/WpPliL61hemoHY/2cMGp7s5i
# 5jdqptQRrhwNBAZuRVAqB/EaG1AC6FNJhSM2xyaLEaRI6HWU6gDfuj1L0aCm3++b
# yPA6EH8tn5NVw4H4WdeLe99pW6k6PAMqgO+gj2QNX+TGgrABvYmUDbHunEyabbrZ
# rhZjJ2n0QwoDUeST9eIvD+nKNs6FxbnjCZ08rL61Z2AZM+k3BFBREhhVtdN/7Xvk
# k6ipnYxlAiF9uqOd3Cyjwb7P34igT8weR0PntEH6Rx+DMYIEDTCCBAkCAQEwgZMw
# fDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
# ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMd
# TWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAIUjc0jRO4G33IAAQAA
# AhQwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRAB
# BDAvBgkqhkiG9w0BCQQxIgQgv2Wzz0fXQmDXE0s/vgz3gSStXHKF/ccmHytY0M2R
# LdowgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCA2eKvvWx5bcoi43bRO3+Et
# tQUCvyeD2dbXy/6+0xK+xzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
# ZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBD
# QSAyMDEwAhMzAAACFI3NI0TuBt9yAAEAAAIUMCIEIIuYD1reuH7VxpqskBn7uksa
# ptLeODyj6FjPBUD6JIKNMA0GCSqGSIb3DQEBCwUABIICACWTnvjnjs8Oq/Mg3QcV
# QBlABH9ZBiRPv9NyfEI7UrwW5NzEu3Rw2b0vUc3FYbqs3eWt9Km7QROTfCpCgg+c
# U8XpObiT06OklZJbMYfPE35M1t2ODFXf9vK8bnl+jwNSw+X/iCNSK+udU2mhfvg2
# MbvVyoLPwXjW77ydI+YYKcXl+0KxBU/ejSXUzO7g+omBw988wv74G03iXQ/slY68
# jm0yWIMjdWHb+kD+uMte9Wv7PWTPGR7Dm6lxf2ElBkED0RSrO6QZVvbgVBRJT92k
# ERvIFe6gLkdaxgq6TgEHLD+HuyffhqMWv5DF3I7q8ffnOhVJvq7IDxW9HmscfM3e
# GzyzyDlhnPEaFgc1Kk+OaZcYe5pXRK3tsg7z/2YDMsOglfl727asZHgnxxPe8cAD
# /bG5YbdXVq5X0xPOoQOtUQKNrj1qQ3pvSqOYEPv3gkKEsuOtPYf6vIZFU75QnRsk
# LIAVZFQT6Jm6MkURDtg7jCJZysHfV6fq+dBUcoH/lR65XPZHyMrvG992KCxAfkgq
# sHxn4bJmFG30uwUAiRtTMSfvVdzvgZ3uINrWBw5lUlMqV7sJxt62//qBLSEjYpvC
# TALYfojHwj3crOZR7HdJY4FN+CZUQaItdyXAym5wYKUp+KPuBa0B8XZwVrB1Ce9C
# x/mBwS6v+1lEGsqkrBmpHbEr
# SIG # End signature block