AzStackHciStandaloneObservability/AzStackHCI.StandaloneObservability.psm1

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


Import-Module $PSScriptRoot\AzStackHCI.StandaloneObservability.Helpers.psm1 -Force -DisableNameChecking -Global
Import-Module $PSScriptRoot\..\AzStackHci.EnvironmentChecker.Utilities.psm1 -Force -DisableNameChecking -Global
Import-Module $PSScriptRoot\..\AzStackHci.EnvironmentChecker.Reporting.psm1 -Force -DisableNameChecking -Global
Import-LocalizedData -BindingVariable lvsTxt -FileName AzStackHCI.StandaloneObservability.Strings.psd1

<#
.SYNOPSIS
    Sends daignostics data using standalone observability pipeline.
 
.DESCRIPTION
    Sends diagnostics data using standalone observability pipeline.
 
    PS C:\> Enter-PsSession -ComputerName <NodeName> -Credential $cred
 
    PS C:\> Send-AzStackHciDiagnosticData
 
.PARAMETER ResourceGroupName
    Azure Resource group name where temporary Arc resource will be created. This can be same parameter as used in AzS Deployment.
 
.PARAMETER SubscriptionId
    Azure SubscriptionID where temporary Arc resource will be created. This can be same parameter as used in AzS Deployment.
 
.PARAMETER RegistrationRegion
    Optional. Azure registration region where Arc resource will be created. This can be same parameter as used in AzS Deployment.
 
.PARAMETER DiagnosticLogPath
    Diagnostic Log path which will be parsed and sent to Microsoft.
 
.PARAMETER Cloud
    Optional. Azure Cloud name default: AzureCloud.
 
.PARAMETER CacheFlushWaitTimeInSec
    Optional wait time to Flush the cache folder. default:600
 
.PARAMETER RegistrationCredential
    Azure credentials used for authentication to register ArcAgent. Needed only for DefaultSet
 
.PARAMETER RegistrationWithDeviceCode
    This is RegistrationWithDeviceCode switch to use device code for authentication.
 
.PARAMETER RegistrationWithExistingContext
    This is RegistrationWithExistingContext switch to use existing Azure context on the local machine.
 
.PARAMETER RegistrationSPCredential
    This is SPN crednetials used for authentication to register ArcAgent. Needed only for ServicePrincipal set
 
.EXAMPLE
    The example below .
 
    During Remote Support JEA configuration, WinRM will be restarted twice and that can break the PsSession to node if you are installing Remote Support remotely. In that case, connect to remote node again and execute Enable cmdlet again after 4-5 minutes.
 
    PS C:\> Enter-PsSession -ComputerName <NodeName> -Credential $cred
 
    PS C:\> Send-AzStackHciDiagnosticData
 
    Processing data from remote server v-host1 failed with the following error message: The I/O operation has been aborted because of either a thread exit or an application request. For more information, see the about_Remote_Troubleshooting Help topic.
 
    PS C:\> Enter-PsSession -ComputerName <NodeName> -Credential $cred
 
    PS C:\> Send-AzStackHciDiagnosticData
 
.NOTES
    Requires Support VM to have stable internet connectivity.
#>


function Send-AzStackHciDiagnosticData
{
    [CmdletBinding(PositionalBinding = $false, DefaultParameterSetName = "Interactive")]
    param(
        [Parameter(Mandatory = $true)]
        [System.String] $ResourceGroupName,

        [Parameter(Mandatory = $true)]
        [System.String] $SubscriptionId,

        [Parameter(Mandatory = $true, ParameterSetName = "DefaultSet")]
        [PSCredential] $RegistrationCredential,

        [Parameter(Mandatory = $true, ParameterSetName = "Interactive")]
        [Switch] $RegistrationWithDeviceCode,

        [Parameter(Mandatory = $true, ParameterSetName = "PassThrough")]
        [Switch] $RegistrationWithExistingContext,

        [Parameter(Mandatory = $true, ParameterSetName = "ServicePrincipal")]
        [PSCredential] $RegistrationSPCredential,

        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ Test-Path -Path $_ -PathType Container })]
        [System.String] $DiagnosticLogPath,

        [Parameter(Mandatory=$false)]
        [System.String] $RegistrationRegion = "eastus",

        [Parameter(Mandatory=$false)]
        [System.String] $ObsRootFolderPath = "C:\StdObservability",

        [Parameter(Mandatory=$false)]
        [System.String] $Cloud = "AzureCloud",

        [Parameter(Mandatory=$false)]
        [Int] $CacheFlushWaitTimeInSec = 600
    )

    # Fail if Arc Agent is already connected

    $OperationType = $MyInvocation.MyCommand
    $script:ErrorActionPreference = 'Stop'

    $standaloneScriptsPath = "..\Obs\scripts"
    Import-Module "$PSScriptRoot\$standaloneScriptsPath\StandaloneObservabilityHelper.psm1"

    $TenantId = Get-TenantId -AzureEnvironment $Cloud -SubscriptionId $SubscriptionId

    $skipArcForServer = $false
    if(Test-IsArcAgentConnected)
    {
        $skipArcForServer = $true
    }
    $service = Get-Service "WatchdogAgent" -ErrorAction SilentlyContinue
    if ($null -ne $service)
    {
        throw "Watchdog is present. Use the extension instead of this Cmdlet"
    }

    try
    {
        $LogSource = "AzStackHciEnvironmentChecker/StandaloneObservability"
        $EventID = "19101"
        Set-AzStackHciOutputPath -Path $ObsRootFolderPath -Source $LogSource

        # Ensure we are elevated
        if (Test-Elevation)
        {
            Log-Info -Message ($lvsTxt.ElevationModeInfo) -Type Info
        }
        else
        {
            Log-Info -Message ($lvsTxt.ElevationModeMsg) -Type Error -ConsoleOut
            throw $($lvsTxt.ElevationModeErrMsg)
        }

        Test-ModuleUpdate -PassThru:$PassThru

        # Call/Initialise reporting
        $envcheckerReport = Get-AzStackHciEnvProgress -clean:$CleanReport
        $envcheckerReport = Add-AzStackHciEnvJob -report $envcheckerReport

        Push-Location -Path "$PSScriptRoot\$standaloneScriptsPath"

        if ($PSCmdlet.ParameterSetName -eq "ServicePrincipal") {
            & .\Install-StandaloneObservability.ps1 -ResourceGroupName $ResourceGroupName `
                                                                -SubscriptionId $SubscriptionId `
                                                                -TenantId $TenantId `
                                                                -RegistrationSPCredential $RegistrationSPCredential `
                                                                -FactoryLogShare $DiagnosticLogPath `
                                                                -ObsRootFolderPath $ObsRootFolderPath `
                                                                -Cloud $Cloud `
                                                                -RegistrationRegion $RegistrationRegion `
                                                                -GcsRegion $RegistrationRegion `
                                                                -SkipArcForServer $skipArcForServer -ParseOnce | Out-Null
        }
        elseif ($PSCmdlet.ParameterSetName -eq "Interactive") {
            & .\Install-StandaloneObservability.ps1 -ResourceGroupName $ResourceGroupName `
                                                                -SubscriptionId $SubscriptionId `
                                                                -TenantId $TenantId `
                                                                -Interactive `
                                                                -FactoryLogShare $DiagnosticLogPath `
                                                                -ObsRootFolderPath $ObsRootFolderPath `
                                                                -Cloud $Cloud `
                                                                -RegistrationRegion $RegistrationRegion `
                                                                -GcsRegion $RegistrationRegion `
                                                                -SkipArcForServer $skipArcForServer -ParseOnce | Out-Null
        }
        elseif ($PSCmdlet.ParameterSetName -eq "PassThrough") {
            & .\Install-StandaloneObservability.ps1 -ResourceGroupName $ResourceGroupName `
                                                                -SubscriptionId $SubscriptionId `
                                                                -TenantId $TenantId `
                                                                -PassThrough `
                                                                -FactoryLogShare $DiagnosticLogPath `
                                                                -ObsRootFolderPath $ObsRootFolderPath `
                                                                -Cloud $Cloud `
                                                                -RegistrationRegion $RegistrationRegion `
                                                                -GcsRegion $RegistrationRegion `
                                                                -SkipArcForServer $skipArcForServer -ParseOnce | Out-Null
        }
        else {
            & .\Install-StandaloneObservability.ps1 -ResourceGroupName $ResourceGroupName `
                                                                -SubscriptionId $SubscriptionId `
                                                                -TenantId $TenantId `
                                                                -RegistrationCredential $RegistrationCredential `
                                                                -FactoryLogShare $DiagnosticLogPath `
                                                                -ObsRootFolderPath $ObsRootFolderPath `
                                                                -Cloud $Cloud `
                                                                -RegistrationRegion $RegistrationRegion `
                                                                -GcsRegion $RegistrationRegion `
                                                                -SkipArcForServer $skipArcForServer -ParseOnce | Out-Null
        }

        Write-Host "Going to Wait for MA to flush the Cahce folder: $CacheFlushWaitTimeInSec"
        Start-Sleep -Seconds $CacheFlushWaitTimeInSec
    }
    catch
    {
        $exception = $_
        Log-Info -Message "" -ConsoleOut
        Trace-Execution "$OperationType failed. $exception"
        Trace-Execution "$($exception.ScriptStackTrace)"
        $cmdletException = $_
        throw $exception
    }
    finally
    {
        $Script:ErrorActionPreference = 'SilentlyContinue'
        # Write result to StandaloneObs channel
        Write-ETWLog -Source $LogSource -Message "Standalone Observability Status: $cmdletFailed" -EventId $EventID

        # Write validation result to report object and close out report
        $envcheckerReport | Add-Member -MemberType NoteProperty -Name 'StandaloneObservability' -Value $cmdletFailed -Force
        $envcheckerReport = Close-AzStackHciEnvJob -report $envcheckerReport
        Write-AzStackHciEnvReport -report $envcheckerReport
        if ($PSCmdlet.ParameterSetName -eq "ServicePrincipal") {
            & .\Uninstall-StandaloneObservability.ps1 -RegistrationSPCredential $RegistrationSPCredential `
                                                                -Cloud $Cloud -SkipArcForServer $skipArcForServer | Out-Null
        }
        else {
            $token = Get-AzAccessToken
            if ($null -eq $token)
            {
                 Write-Host "Token was null. Going to try to use Connect-AzAccount to get the token."
                 Connect-AzAccount -Credential $RegistrationCredential -Environment $Cloud -Tenant $TenantId -Subscription $SubscriptionId | Out-Null
                 $token = Get-AzAccessToken
            }
            & .\Uninstall-StandaloneObservability.ps1 -AccessToken $token.Token -SkipArcForServer $skipArcForServer | Out-Null
        }

        Pop-Location
    }
}

 function Get-TenantId
 {
     [CmdletBinding()]
     param (
         [Parameter(Mandatory=$false)]
         [ValidateSet("AzureCloud", "AzureChinaCloud", "AzureGermanCloud", "AzureUSGovernment")]
         [string] $AzureEnvironment = "AzureCloud",

         [Parameter(Mandatory=$true)]
         [string] $SubscriptionId
     )

     $commandName = $MyInvocation.MyCommand
     $endpoints = Get-AzureURIs -AzureEnvironment $AzureEnvironment

     $params = @{
         UseBasicParsing = $true
         ErrorAction     = 'Stop'
         Uri             = $endpoints.ARMUri.TrimEnd('/') + "/subscriptions/${SubscriptionId}?api-version=1.0"
     }
     $response = try { Invoke-WebRequest @params } catch { $_.Exception.Response }

     if ($response.StatusCode -eq [System.Net.HttpStatusCode]::NotFound) {
         throw "[$commandName] SubscriptionId $SubscriptionId not found"
     }

     $header   = $response.GetResponseHeader('WWW-Authenticate')
     Write-Verbose "[$commandName] $header"
     $guidPattern = "[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}"
     $tenantId = $header.Split(' ') | Where-Object { $_ -like '*authorization_uri*' } | Select-Object -First 1 | ForEach-Object { [Regex]::Matches($_, $guidPattern).Value }

     if ([string]::IsNullOrEmpty($tenantId)) {
         Write-Verbose "[$commandName] Response $($response | ConvertTo-Json -depth 5)"
         throw "[$commandName] Unable to get tenantId for SubscriptionId $SubscriptionId"
     }

     Write-Verbose "[$commandName] Retrieved tenantId $tenantId"
     return ,$tenantId

 }

 <#
 .Synopsis
    Builds graph and login endpoints for a given AzureEnvironment
 #>

 function Get-AzureURIs {
     [CmdletBinding()]
     param (
         [Parameter(Mandatory=$false)]
         [ValidateSet("AzureCloud", "AzureChinaCloud", "AzureGermanCloud", "AzureUSGovernment")]
         [string] $AzureEnvironment = "AzureCloud"
     )

     $commandName = $MyInvocation.MyCommand

     $fullUri = "https://management.azure.com/metadata/endpoints?api-version=2023-01-01"
     try {
         $response = Invoke-RestMethod -Uri $fullUri -ErrorAction Stop -UseBasicParsing -TimeoutSec 30 -Verbose
     } catch {
         $message = "[$commandName] Error $($_.Exception)"
         Write-Verbose $message
         throw $message
     }

     $data = $response | Where name -EQ $AzureEnvironment
     if (-not $data) {
         throw New-Object NotImplementedException("Unknown environment type $AzureEnvironment")
     }

     # sample @{ GraphUri = "https://graph.windows.net/"; LoginUri = "https://login.microsoftonline.com/"; ManagementServiceUri = "https://management.core.windows.net/"; ARMUri = "https://management.azure.com/" }
     $endpointProperties = @{
         GraphUri = $data.graph
         LoginUri = $data.authentication.loginEndpoint
         ManagementServiceUri = $data.authentication.audiences[0]
         ARMUri = $data.resourceManager
         MsGraphUri = $data.microsoftGraphResourceId
     }
     Write-Verbose "[$commandName] AzureEnvironment $AzureEnvironment, EndpointProperties $($endpointProperties | ConvertTo-Json -Depth 2)"

     return $endpointProperties
 }

Export-ModuleMember -Function Send-AzStackHciDiagnosticData
# SIG # Begin signature block
# MIIoRgYJKoZIhvcNAQcCoIIoNzCCKDMCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBLBCzButRY72WH
# FxSCao2SHuSkRwEpfvr2RTA6XpZ+E6CCDXYwggX0MIID3KADAgECAhMzAAAEBGx0
# Bv9XKydyAAAAAAQEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjQwOTEyMjAxMTE0WhcNMjUwOTExMjAxMTE0WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQC0KDfaY50MDqsEGdlIzDHBd6CqIMRQWW9Af1LHDDTuFjfDsvna0nEuDSYJmNyz
# NB10jpbg0lhvkT1AzfX2TLITSXwS8D+mBzGCWMM/wTpciWBV/pbjSazbzoKvRrNo
# DV/u9omOM2Eawyo5JJJdNkM2d8qzkQ0bRuRd4HarmGunSouyb9NY7egWN5E5lUc3
# a2AROzAdHdYpObpCOdeAY2P5XqtJkk79aROpzw16wCjdSn8qMzCBzR7rvH2WVkvF
# HLIxZQET1yhPb6lRmpgBQNnzidHV2Ocxjc8wNiIDzgbDkmlx54QPfw7RwQi8p1fy
# 4byhBrTjv568x8NGv3gwb0RbAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU8huhNbETDU+ZWllL4DNMPCijEU4w
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzUwMjkyMzAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAIjmD9IpQVvfB1QehvpC
# Ge7QeTQkKQ7j3bmDMjwSqFL4ri6ae9IFTdpywn5smmtSIyKYDn3/nHtaEn0X1NBj
# L5oP0BjAy1sqxD+uy35B+V8wv5GrxhMDJP8l2QjLtH/UglSTIhLqyt8bUAqVfyfp
# h4COMRvwwjTvChtCnUXXACuCXYHWalOoc0OU2oGN+mPJIJJxaNQc1sjBsMbGIWv3
# cmgSHkCEmrMv7yaidpePt6V+yPMik+eXw3IfZ5eNOiNgL1rZzgSJfTnvUqiaEQ0X
# dG1HbkDv9fv6CTq6m4Ty3IzLiwGSXYxRIXTxT4TYs5VxHy2uFjFXWVSL0J2ARTYL
# E4Oyl1wXDF1PX4bxg1yDMfKPHcE1Ijic5lx1KdK1SkaEJdto4hd++05J9Bf9TAmi
# u6EK6C9Oe5vRadroJCK26uCUI4zIjL/qG7mswW+qT0CW0gnR9JHkXCWNbo8ccMk1
# sJatmRoSAifbgzaYbUz8+lv+IXy5GFuAmLnNbGjacB3IMGpa+lbFgih57/fIhamq
# 5VhxgaEmn/UjWyr+cPiAFWuTVIpfsOjbEAww75wURNM1Imp9NJKye1O24EspEHmb
# DmqCUcq7NqkOKIG4PVm3hDDED/WQpzJDkvu4FrIbvyTGVU01vKsg4UfcdiZ0fQ+/
# V0hf8yrtq9CkB8iIuk5bBxuPMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# 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
# /Xmfwb1tbWrJUnMTDXpQzTGCGiYwghoiAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAAQEbHQG/1crJ3IAAAAABAQwDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIAuE9IJ4WuqE/Px+REsmHNv0
# 2aiJ7RCpD2XOks1IU2M0MEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEAc5u5GLQCadOA09YaVw40zE/s7H4ro1fjYQ+Zoa8utzUFlzKRtAHJEQDN
# C55l1Xh9EdnD1pVATnDGaPVxu6Hdl+b/i47piWzUvt/XavwIBfjrxVEjJ2lY7bA+
# A8uQGTGCXPF4Y30m430UMF9kVQXnEYrnuWpsWkC5ax0Jlwj2SvCoi/z60NAS8mWh
# qSakC9Qq2h1hT9dangQPfTFRCJ1tFNnH6lbi6Om9ML7n65FLEeDy9BEYf2cuAIZ2
# r3Gej9TiDulxcIbm9NV9pCz61Ft1haPxSmmE6dh8x5CpgF4k+jLafuONwXGOuvfF
# CD8ke73tU6DgWS/czIYktX/zWXv8CaGCF7AwghesBgorBgEEAYI3AwMBMYIXnDCC
# F5gGCSqGSIb3DQEHAqCCF4kwgheFAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFaBgsq
# hkiG9w0BCRABBKCCAUkEggFFMIIBQQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCD+V4Y1uaQWHFQi971Zn6fN0DoI6Iq10HVcua17e6YCuQIGZ0of5w18
# GBMyMDI0MTIwNDE1MDMzMi42ODFaMASAAgH0oIHZpIHWMIHTMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT
# Tjo0MzFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# U2VydmljZaCCEf4wggcoMIIFEKADAgECAhMzAAAB+vs7RNN3M8bTAAEAAAH6MA0G
# CSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI0
# MDcyNTE4MzExMVoXDTI1MTAyMjE4MzExMVowgdMxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9w
# ZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjQzMUEt
# MDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl
# MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyhZVBM3PZcBfEpAf7fII
# hygwYVVP64USeZbSlRR3pvJebva0LQCDW45yOrtpwIpGyDGX+EbCbHhS5Td4J0Yl
# c83ztLEbbQD7M6kqR0Xj+n82cGse/QnMH0WRZLnwggJdenpQ6UciM4nMYZvdQjyb
# A4qejOe9Y073JlXv3VIbdkQH2JGyT8oB/LsvPL/kAnJ45oQIp7Sx57RPQ/0O6qay
# J2SJrwcjA8auMdAnZKOixFlzoooh7SyycI7BENHTpkVKrRV5YelRvWNTg1pH4EC2
# KO2bxsBN23btMeTvZFieGIr+D8mf1lQQs0Ht/tMOVdah14t7Yk+xl5P4Tw3xfAGg
# Hsvsa6ugrxwmKTTX1kqXH5XCdw3TVeKCax6JV+ygM5i1NroJKwBCW11Pwi0z/ki9
# 0ZeO6XfEE9mCnJm76Qcxi3tnW/Y/3ZumKQ6X/iVIJo7Lk0Z/pATRwAINqwdvzpdt
# X2hOJib4GR8is2bpKks04GurfweWPn9z6jY7GBC+js8pSwGewrffwgAbNKm82ZDF
# vqBGQQVJwIHSXpjkS+G39eyYOG2rcILBIDlzUzMFFJbNh5tDv3GeJ3EKvC4vNSAx
# tGfaG/mQhK43YjevsB72LouU78rxtNhuMXSzaHq5fFiG3zcsYHaa4+w+YmMrhTEz
# D4SAish35BjoXP1P1Ct4Va0CAwEAAaOCAUkwggFFMB0GA1UdDgQWBBRjjHKbL5WV
# 6kd06KocQHphK9U/vzAfBgNVHSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBf
# BgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz
# L2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmww
# bAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29m
# dC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0El
# MjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUF
# BwMIMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAgEAuFbCorFrvodG
# +ZNJH3Y+Nz5QpUytQVObOyYFrgcGrxq6MUa4yLmxN4xWdL1kygaW5BOZ3xBlPY7V
# puf5b5eaXP7qRq61xeOrX3f64kGiSWoRi9EJawJWCzJfUQRThDL4zxI2pYc1wnPp
# 7Q695bHqwZ02eaOBudh/IfEkGe0Ofj6IS3oyZsJP1yatcm4kBqIH6db1+weM4q46
# NhAfAf070zF6F+IpUHyhtMbQg5+QHfOuyBzrt67CiMJSKcJ3nMVyfNlnv6yvttYz
# LK3wS+0QwJUibLYJMI6FGcSuRxKlq6RjOhK9L3QOjh0VCM11rHM11ZmN0euJbbBC
# VfQEufOLNkG88MFCUNE10SSbM/Og/CbTko0M5wbVvQJ6CqLKjtHSoeoAGPeeX24f
# 5cPYyTcKlbM6LoUdO2P5JSdI5s1JF/On6LiUT50adpRstZajbYEeX/N7RvSbkn0d
# jD3BvT2Of3Wf9gIeaQIHbv1J2O/P5QOPQiVo8+0AKm6M0TKOduihhKxAt/6Yyk17
# Fv3RIdjT6wiL2qRIEsgOJp3fILw4mQRPu3spRfakSoQe5N0e4HWFf8WW2ZL0+c83
# Qzh3VtEPI6Y2e2BO/eWhTYbIbHpqYDfAtAYtaYIde87ZymXG3MO2wUjhL9HvSQzj
# oquq+OoUmvfBUcB2e5L6QCHO6qTO7WowggdxMIIFWaADAgECAhMzAAAAFcXna54C
# m0mZAAAAAAAVMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZp
# Y2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAxODIyMjVaFw0zMDA5MzAxODMy
# MjVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH
# EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV
# BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0B
# AQEFAAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5vQ7VgtP97pwHB9KpbE51
# yMo1V/YBf2xK4OK9uT4XYDP/XE/HZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY
# 6GB9alKDRLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhuje3XD9gmU3w5YQJ6xKr9
# cmmvHaus9ja+NSZk2pg7uhp7M62AW36MEBydUv626GIl3GoPz130/o5Tz9bshVZN
# 7928jaTjkY+yOSxRnOlwaQ3KNi1wjjHINSi947SHJMPgyY9+tVSP3PoFVZhtaDua
# Rr3tpK56KTesy+uDRedGbsoy1cCGMFxPLOJiss254o2I5JasAUq7vnGpF1tnYN74
# kpEeHT39IM9zfUGaRnXNxF803RKJ1v2lIH1+/NmeRd+2ci/bfV+AutuqfjbsNkz2
# K26oElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afomXw/TNuvXsLz1dhzPUNOwTM5
# TI4CvEJoLhDqhFFG4tG9ahhaYQFzymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZk
# i1ugpoMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y1BzFa/ZcUlFdEtsluq9Q
# BXpsxREdcu+N+VLEhReTwDwV2xo3xwgVGD94q0W29R6HXtqPnhZyacaue7e3Pmri
# Lq0CAwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUC
# BBYEFCqnUv5kxJq+gpE8RjUpzxD/LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJl
# pxtTNRnpcjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9y
# eS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUA
# YgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU
# 1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2Ny
# bC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIw
# MTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0w
# Ni0yMy5jcnQwDQYJKoZIhvcNAQELBQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/yp
# b+pcFLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27DzHkwo/7bNGhlBgi7ulm
# ZzpTTd2YurYeeNg2LpypglYAA7AFvonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM
# 9W0jVOR4U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9AkvUCgvxm2EhIRXT0n4ECW
# OKz3+SmJw7wXsFSFQrP8DJ6LGYnn8AtqgcKBGUIZUnWKNsIdw2FzLixre24/LAl4
# FOmRsqlb30mjdAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2kQH2zsZ0/fZMcm8Qq3Uw
# xTSwethQ/gpY3UA8x1RtnWN0SCyxTkctwRQEcb9k+SS+c23Kjgm9swFXSVRk2XPX
# fx5bRAGOWhmRaw2fpCjcZxkoJLo4S5pu+yFUa2pFEUep8beuyOiJXk+d0tBMdrVX
# VAmxaQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L+DvktxW/tM4+pTFRhLy/AsGC
# onsXHRWJjXD+57XQKBqJC4822rpM+Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU
# 5nR0W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/2XBjU02N7oJtpQUQwXEG
# ahC0HVUzWLOhcGbyoYIDWTCCAkECAQEwggEBoYHZpIHWMIHTMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT
# Tjo0MzFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# U2VydmljZaIjCgEBMAcGBSsOAwIaAxUA94Z+bUJn+nKwBvII6sg0Ny7aPDaggYMw
# gYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQsF
# AAIFAOr6jPUwIhgPMjAyNDEyMDQwODEwMjlaGA8yMDI0MTIwNTA4MTAyOVowdzA9
# BgorBgEEAYRZCgQBMS8wLTAKAgUA6vqM9QIBADAKAgEAAgIXjQIB/zAHAgEAAgIS
# 3DAKAgUA6vvedQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAow
# CAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBCwUAA4IBAQDPPd1HJ/gN
# bg8NlD1VkvZ3hq5sL/D4pPZ53+McXvc5gWQUD/+7sdCGHoEy+2Bwbo1KHNQj4AEJ
# OjmYrvczOOfTLb0a2Bg3UIXlYAI9wsSPtUiXFYImrSg1KTRvNsH2YiJWMy9GUPKw
# nEsJnsfZ4IrzbzJRRCA3nTn/18liyrlXrN+SCntqLPgI+ZolzohLEpHGy+r45T5I
# WpUd1lhhfGD7H/BiJLIjUo37E+MNDSW5qqP3CziwVoJ/V/omSTJdgeFvwrWpRS/S
# APuLtV7T9IRj5O0/a9h1mi5kJdhsc3iOKr/mMhp6LrZ3MAPDweBmuJShyeDFNIM4
# UdRxm1ol0mgoMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
# IDIwMTACEzMAAAH6+ztE03czxtMAAQAAAfowDQYJYIZIAWUDBAIBBQCgggFKMBoG
# CSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgsGN9Kz8u
# I7QHjr6Hr8Q6v9M+ms0jrU9hiG5dxtDXld4wgfoGCyqGSIb3DQEJEAIvMYHqMIHn
# MIHkMIG9BCB98n8tya8+B2jjU/dpJRIwHwHHpco5ogNStYocbkOeVjCBmDCBgKR+
# MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT
# HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB+vs7RNN3M8bTAAEA
# AAH6MCIEIBbs2QBr//WX4EjY9A/juKhaSHF2xW/WZbmZJY9gklquMA0GCSqGSIb3
# DQEBCwUABIICAMQ5sEDVf8mzxfXHy2G16FMv4WZ9I8FRWm93s0BwIy7vXchh16RZ
# NwpbWoVG2L31JmO3ULBNTyAV6H44YI4fiJJtIk/qwpAS2Yhc4zgw3vEhvq9BUG24
# mSuOutOx/5mnd/Mnc8FQ2FW3vhKz3v4vA5ykUDpligrUrlrVgsfovi1P5ns04Rk/
# TWV+3c81ZxJ/s68ybj6uOhWZhsHqg+wsi0u17wDYRHPeckRQeH4TqlTqqWwwxzxZ
# vuQOEE5sOr7NRdozcYKVCSUFLLLPmiGq9a/A8xcWWWkeDtSDaBl4ZuU6+KvAkIZb
# nZxsYk5qslOuoblK8NSvzfwwKG5dCH6kJYuSg3DuMTm5JByEeAPTmSIehQvU96ce
# sy6IGILeMnPLfTshQiDn7ar9B6rqJm/jVOjDpVigZP/+TwRO1OpwwUpdMoglUwid
# m8Ccz3RPPIIgsIqCIHX3rBzwtXDSiCSvWzJOcW0BGaQC/x3eZZuo1SZiP/5CGPbh
# 68nTLGHklKH7GvZwjV8r5V5beLrlAxNgOBfadckIW+TS2nqtYYhwhDkxSELxEXlz
# p/oxP3llapebtYPx40CUVAU4n4RCgg3dJBvxc3ZKF8HX2m9RKV2fY1CcfDpuQyK0
# wFoI7LLK22aWK/oCwrtWig5GYc559/1MO5Hn0Chn8VHt4sR6HF7d8VpC
# SIG # End signature block