Obs/scripts/StandaloneObservabilityHelper.psm1

##------------------------------------------------------------------
## <copyright file="StandaloneObservabilityHelper.psm1" company="Microsoft">
## Copyright (C) Microsoft. All rights reserved.
## </copyright>
##------------------------------------------------------------------

Import-Module "$PSScriptRoot\GMATenantJsonHelper.psm1"
Import-Module "$PSScriptRoot\StandaloneObservabilityConstants.psm1"
Import-Module "$PSScriptRoot\ExtensionHelper.psm1"

function Install-AzureConnectedMachineAgent
{
    param (
        [Parameter(Mandatory)]
        [System.String] $ResourceName,

        [Parameter(Mandatory)]
        [System.String] $ResourceGroupName,

        [Parameter(Mandatory)]
        [System.String] $TenantId,

        [Parameter(Mandatory)]
        [System.String] $RegionName,

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

        [Parameter(Mandatory)]
        [System.String] $Cloud,

        [Parameter(Mandatory)]
        [System.String] $StampId,

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

        [Parameter(Mandatory = $true, ParameterSetName = "DefaultSet")]
        [System.String] $AccessToken
    )

    ## Run connect command
    $timestamp = [DateTime]::Now.ToString("yyyyMMdd-HHmmss")
    $logPath = Get-LogFolderPath
    $logFile = Join-Path -Path $logPath -ChildPath "ArcForServerInstall_${timestamp}.txt"

    $AgentWebLink = $PipelineConstants.ArcForServerAgentWebLink
    $AgentMsiPath = Join-Path -Path $logPath -ChildPath $PipelineConstants.ArcForServerMsiFileName
    $AgentExePath = $PipelineConstants.ArcForServerExePath

    Write-Host "Starting Arc-for-server agent install AgentWebLink: $AgentWebLink AgentMsiPath: $AgentMsiPath AgentExePath: $AgentExePath logs: $logFile"
    if ($PSCmdlet.ParameterSetName -eq "ServicePrincipal") {
        $regSpNetworkCreds = $RegistrationSPCredential.GetNetworkCredential()
        Write-Host "Creating ArcContext for SPN: $($regSpNetworkCreds.UserName)"
        $arcContext = New-Object Microsoft.AzureStack.Observability.ObservabilityCommon.ArcForServer.ArcContextSpn
        $arcContext.SubscriptionId = $SubscriptionId
        $arcContext.ResourceGroup = $ResourceGroupName
        $arcContext.Location = $RegionName
        $arcContext.Cloud = $Cloud
        $arcContext.ResourceName = $ResourceName
        $arcContext.TenantId = $TenantId
        $arcContext.ServicePrincipalId = $regSpNetworkCreds.UserName
        $arcContext.ServicePrincipalSecret = $regSpNetworkCreds.Password
    }
    else {
        Write-Host "Creating ArcContext with AccessToken Length: $($AccessToken.Length)"
        $arcContext = New-Object Microsoft.AzureStack.Observability.ObservabilityCommon.ArcForServer.ArcContext
        $arcContext.SubscriptionId = $SubscriptionId
        $arcContext.ResourceGroup = $ResourceGroupName
        $arcContext.Location = $RegionName
        $arcContext.Cloud = $Cloud
        $arcContext.ResourceName = $ResourceName
        $arcContext.TenantId = $TenantId
        $arcContext.AccessToken = $AccessToken
    }

    $arcAgent = New-Object Microsoft.AzureStack.Observability.ObservabilityCommon.ArcForServer.ArcAgent
    $res = $arcAgent.Onboard($arcContext, $AgentWebLink, $AgentMsiPath, $logFile, $AgentExePath)

    Write-Host "Arc-for-server agent install $env:COMPUTERNAME. Status $res"

    if($res -eq $true) {
        Write-Host -ForegroundColor yellow "To view your onboarded server(s), navigate to https://ms.portal.azure.com/#blade/Microsoft_Azure_HybridCompute/AzureArcCenterBlade/servers"
    }
    else {
        throw "Hybrid agent connection failed. LogPath: $logFile"
    }
}

function Remove-AzureConnectedMachineAgent
{
    param (
        [Parameter(Mandatory = $true, ParameterSetName = "ServicePrincipal")]
        [PSCredential] $RegistrationSPCredential,

        [Parameter(Mandatory = $true, ParameterSetName = "DefaultSet")]
        [System.String] $AccessToken
    )

    $timestamp = [DateTime]::Now.ToString("yyyyMMdd-HHmmss")
    $logPath = Get-LogFolderPath
    $logFile = Join-Path -Path $logPath -ChildPath "ArcForServerUninstall_${timestamp}.txt"

    $AgentExePath = $PipelineConstants.ArcForServerExePath
    $AgentMsiPath = Join-Path -Path $logPath -ChildPath $PipelineConstants.ArcForServerMsiFileName

    if ($PSCmdlet.ParameterSetName -eq "ServicePrincipal") {
        $regSpNetworkCreds = $RegistrationSPCredential.GetNetworkCredential()
        Write-Host "Creating ArcContext for SPN: $($regSpNetworkCreds.UserName)"
        $arcContext = New-Object Microsoft.AzureStack.Observability.ObservabilityCommon.ArcForServer.ArcContextSpn
        $arcContext = New-Object Microsoft.AzureStack.Observability.ObservabilityCommon.ArcForServer.ArcContextSpn
        $arcContext.ServicePrincipalId = $regSpNetworkCreds.UserName
        $arcContext.ServicePrincipalSecret = $regSpNetworkCreds.Password
    }
    else {
        Write-Host "Creating ArcContext with AccessToken Length: $($AccessToken.Length)"
        $arcContext = New-Object Microsoft.AzureStack.Observability.ObservabilityCommon.ArcForServer.ArcContext
        $arcContext.AccessToken = $AccessToken
    }

    $arcAgent = New-Object Microsoft.AzureStack.Observability.ObservabilityCommon.ArcForServer.ArcAgent
    $res = $arcAgent.Offboard($arcContext, $logFile, $AgentExePath, $AgentMsiPath)
    if($res -eq $true) {
        Write-Host -ForegroundColor yellow "ArcAgent uninstall succeeded"
    }
    else {
        throw "ArcAgent uninstall failed. LogPath: $logFile"
    }
}

function Get-GmaStateFolders {
    param (
        [Parameter(Mandatory)]
        [System.String] $ObsRootFolderPath
    )
    
    $gmaCacheDirectories = [ordered] @{
        RuntimeSettings = "$ObsRootFolderPath\RuntimeSettings"
        GMACache = "$ObsRootFolderPath\GMACache"
        DiagnosticsCache = "$ObsRootFolderPath\GMACache\DiagnosticsCache"
        HealthCache = "$ObsRootFolderPath\GMACache\HealthCache"
        JsonDropLocation = "$ObsRootFolderPath\GMACache\JsonDropLocation"
        MonAgentHostCache = "$ObsRootFolderPath\GMACache\MonAgentHostCache"
        TelemetryCache = "$ObsRootFolderPath\GMACache\TelemetryCache"
    }

    return $gmaCacheDirectories

}

function New-GmaStateFolders {
    param (
        [Parameter(Mandatory)]
        [System.String] $ObsRootFolderPath
    )
    
    $gmaCacheDirectories = Get-GmaStateFolders -ObsRootFolderPath $ObsRootFolderPath

    foreach ($directory in $gmaCacheDirectories.Values) {
        if (-not (Test-Path $directory -PathType Container)) {
            New-Item -ItemType Directory -Path $directory -Force -Verbose *>> $temp
        }
    }
}

function Test-IsArcAgentConnected() {
    [CmdletBinding()]
    param (
    )

    Write-Host "Checking if Arc connection already exists..."
    try {
        $arcAgentInfo = @{}
        $arcAgentExePath = $PipelineConstants.ArcForServerExePath
        $arcshow = & $arcAgentExePath show
        $arcshow | ForEach-Object {
            $arcProperty = $_.split(':')
            $arcAgentInfo[$arcProperty[0].trim()] =  if ($arcProperty.Count -eq 2) { $arcProperty[1].trim() } else {""}
        }

        Write-Host "Checking Agent connection status: $($arcAgentInfo.'Agent Status')"
        return ($arcAgentInfo.'Agent Status' -eq "Connected")
    }
    catch {
        Write-Host "Error $_ checking if Arc Agent is connected"
        return $false
    }
}

function Test-IsAzure() {
    [CmdletBinding()]
    param (
    )

    Write-Host "Checking if this is an Azure virtual machine"
    try {
        $response = Invoke-WebRequest -UseBasicParsing -Uri "http://169.254.169.254/metadata/instance/compute?api-version=2019-06-01" -Headers @{Metadata = "true"} -TimeoutSec 1 -ErrorAction SilentlyContinue
    }
    catch {
        Write-Verbose "Error $_ checking if we are in Azure"
        return $false
    }
    if ($null -ne $response -and $response.StatusCode -eq 200) {
        Write-Verbose "Azure check indicates that we are in Azure"
        return $true
    }
    return $false
}

function Set-StampGuid() {
    [CmdletBinding()]
    param (
    )

    $StampGuid = $env:STAMP_GUID
    Write-Host "Checking if STAMP_GUID environment is empty: $StampGuid"
    if ($null -eq $env:STAMP_GUID) {
        $StampGuid = (Get-CimInstance -Class Win32_ComputerSystemProduct).UUID
        Write-Host "$functionName Setting the STAMP_GUID variable to $StampGuid"
        $env:STAMP_GUID = $StampGuid
    }

    return $StampGuid
}

function Set-HandlerEnvInfo {
    param (
        [Parameter(Mandatory)]
        [System.String] $ObsRootFolderPath,

        [Parameter(Mandatory)]
        [System.String] $CloudName,

        [Parameter(Mandatory)]
        [System.String] $RegionName
    )

    <#
    Sample HandlerEnvironment.json content:
    [
        {
            "handlerEnvironment": {
                "configFolder": "C:\\Packages\\Plugins\\Microsoft.AzureStack.Observability.Observability\\0.0.0.4\\RuntimeSettings",
                "deploymentid": "",
                "heartbeatFile": "C:\\Packages\\Plugins\\Microsoft.AzureStack.Observability.Observability\\0.0.0.4\\status\\HeartBeat.Json",
                "hostResolverAddress": "",
                "instance": "",
                "logFolder": "C:\\ProgramData\\GuestConfig\\extension_logs\\Microsoft.AzureStack.Observability.Observability",
                "rolename": "",
                "statusFolder": "C:\\Packages\\Plugins\\Microsoft.AzureStack.Observability.Observability\\0.0.0.4\\status"
            },
            "name": "Microsoft.RecoveryServices.Test.AzureSiteRecovery",
            "version": "1"
        }
    ]
    #>


    $handlerEnvironment = @{}
    $handlerEnvironment.configFolder = "$ObsRootFolderPath\RuntimeSettings"
    $handlerEnvironment.deploymentid = ""
    $handlerEnvironment.heartbeatFile = "$ObsRootFolderPath\HeartBeat.Json"
    $handlerEnvironment.hostResolverAddress = ""
    $handlerEnvironment.instance = ""
    $handlerEnvironment.logFolder = "$ObsRootFolderPath"
    $handlerEnvironment.rolename = ""
    $handlerEnvironment.statusFolder = "$ObsRootFolderPath"

    $jsonArray = @{}
    $jsonArray.Add("handlerEnvironment",$handlerEnvironment)
    $jsonArray.Add("name","Microsoft.AzureStack.Observability.Standalone")
    $jsonArray.Add("version","1")

    $jsonContent = ConvertTo-Json -InputObject $jsonArray

    $envFile = "$global:extensionRootLocation\HandlerEnvironment.json"
    $functionName = $MyInvocation.MyCommand.Name

    Write-Host "$functionName : HandlerEnvironment.json doesn't exist at path $envFile. So creating new file"
    Set-Content -Path $envFile -Value $jsonContent

    # Set the runtime settings
    $runtimeSettingsFile = "$ObsRootFolderPath\RuntimeSettings\0.settings"

    $publicSettings = @{}
    $publicSettings.cloudName = $CloudName
    $publicSettings.deviceType = "EnvValidatorStandAlone"
    $publicSettings.region = $RegionName

    $handlerSettings = @{}
    $handlerSettings.publicSettings = $publicSettings

    $jsonArray = @{}
    $jsonArray.Add("handlerSettings",$handlerSettings)

    $runtimeSettings = @{}
    $runtimeSettings.runtimeSettings = @($jsonArray)
    $jsonContent = ConvertTo-Json -InputObject $runtimeSettings -Depth 10

    Set-Content -Path $runtimeSettingsFile -Value $jsonContent
}

function Set-StandaloneScenarioRegistry {
    [CmdletBinding()]
    Param ()

    $functionName = $MyInvocation.MyCommand.Name
    Write-Host "[$functionName] Entering."

    if (-not (Test-Path $MiscConstants.GMAScenarioRegKey.Path)) {
        Write-Host "[$functionName] Creating GMAScenario registry key at path $($MiscConstants.GMAScenarioRegKey.Path) as it does not exists."
        New-Item -Path $MiscConstants.GMAScenarioRegKey.Path -Force
    }

    if (-not ((Test-RegKeyExists -Path $MiscConstants.GMAScenarioRegKey.Path -Name $MiscConstants.GMAScenarioRegKey.Name -GetValueIfExists) -eq $MiscConstants.GMAScenarioRegKey.OneP)) {
        New-ItemProperty `
            -Path $MiscConstants.GMAScenarioRegKey.Path `
            -Name $MiscConstants.GMAScenarioRegKey.Name `
            -PropertyType $MiscConstants.GMAScenarioRegKey.PropertyType `
            -Value $MiscConstants.GMAScenarioRegKey.OneP
    }

    Write-Host "[$functionName] Exiting."
}

function Confirm-IsArcAEnvironment {
    return (Test-RegKeyExists -Path $MiscConstants.ArcARegKey.Path -Name $MiscConstants.ArcARegKey.Name -GetValueIfExists) -eq $true
 }

function Wait-ForGcsConfigSync {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$False)]
        [System.String] $LogFile,

        [Parameter(Mandatory=$False)]
        [int] $TimeInSeconds = 60
    )

    $functionName = $MyInvocation.MyCommand.Name
    Write-Host "[$functionName] Entering. TimeOut: $TimeInSeconds"

    Write-Host "[$functionName] Going to wait for GCSConfig sync $TimeInSeconds"
    Start-Sleep -Seconds $TimeInSeconds
    
    $cacheDir = Join-Path -Path $env:SystemDrive -ChildPath "GMACache\DiagnosticsCache"
    $gcsConfigFiles = Get-ChildItem -Path $cacheDir -Filter GcsConfig -Recurse
    
    if ($gcsConfigFiles.Count -eq 0)
    {
        Write-Error "[$functionName] GCSConfig files are not found. Please check the logs for further investigation."
    }

    Write-Host "[$functionName] Exiting. GCSCongfile count: $($gcsConfigFiles.Count)"
}

# Export section
Export-ModuleMember -Function Remove-AzureConnectedMachineAgent
Export-ModuleMember -Function Install-AzureConnectedMachineAgent
Export-ModuleMember -Function New-GmaStateFolders
Export-ModuleMember -Function Set-HandlerEnvInfo
Export-ModuleMember -Function Test-IsAzure
Export-ModuleMember -Function Set-StampGuid
Export-ModuleMember -Function Test-IsArcAgentConnected

Export-ModuleMember -Function Get-GmaStateFolders
Export-ModuleMember -Function Set-StandaloneScenarioRegistry
Export-ModuleMember -Function Confirm-IsArcAEnvironment
Export-ModuleMember -Function Wait-ForGcsConfigSync
# SIG # Begin signature block
# MIIoQwYJKoZIhvcNAQcCoIIoNDCCKDACAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC9mog4d8kj2ohM
# 3pOWvrYjSGCGiLfNzeY2rEJk8GvDFKCCDXYwggX0MIID3KADAgECAhMzAAAEBGx0
# 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
# /Xmfwb1tbWrJUnMTDXpQzTGCGiMwghofAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAAQEbHQG/1crJ3IAAAAABAQwDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIEz5gtlAaxke0zn3nzBWuuUb
# XEWSe0u/G0+GEBR7wIa8MEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEALs2kxTl73VFdt9rIm36ePPV/nX8qQ3i2BaDc+AMkxmoBQqr/SOBrNAWl
# Z9jxIhzd7mkPqZIwT88dmtX67zdAlhJ6JHiyHmeOrxYMLXn+fiKSCPUiD78hzJof
# pdsFrM5L05RNqoB0iCX/UE8az+Rjc8CCjfoC2djPdYt8vlAxtIf093Ekr7oNLrKk
# zA2tH99Jfxgsf5AQn5/U5voXCP+8Al70AJ8F1tlZf0GLS7jHW1kPUSIkDjy3ODPW
# GHJGL3pF0W5W2AYNKQhcKOC6NmClJpJDcfNyKi51+25YpLGwgZklwcnQoiN+R+KX
# R7uIPDPJ32T6tjGzmaty0uZtrwmFD6GCF60wghepBgorBgEEAYI3AwMBMYIXmTCC
# F5UGCSqGSIb3DQEHAqCCF4YwgheCAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFaBgsq
# hkiG9w0BCRABBKCCAUkEggFFMIIBQQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCAG+fcfxvMDenO0SsoG/91ki84PE6mlo4P6+52LJBv8MQIGZ0pekNch
# GBMyMDI0MTIwNDE1MDMzMi41OTFaMASAAgH0oIHZpIHWMIHTMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT
# Tjo2NTFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# U2VydmljZaCCEfswggcoMIIFEKADAgECAhMzAAAB9ZkJlLzxxlCMAAEAAAH1MA0G
# CSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI0
# MDcyNTE4MzEwMVoXDTI1MTAyMjE4MzEwMVowgdMxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9w
# ZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjY1MUEt
# MDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl
# MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzO90cFQTWd/WP84IT7JM
# IW1fQL61sdfgmhlfT0nvYEb2kvkNF073ZwjveuSWot387LjE0TCiG93e6I0HzIFQ
# BnbxGP/WPBUirFq7WE5RAsuhNfYUL+PIb9jJq3CwWxICfw5t/pTyIOHjKvo1lQOT
# WZypir/psZwEE7y2uWAPbZJTFrKen5R73x2Hbxy4eW1DcmXjym2wFWv10sBH40aj
# Jfe+OkwcTdoYrY3KkpN/RQSjeycK0bhjo0CGYIYa+ZMAao0SNR/R1J1Y6sLkiCJO
# 3aQrbS1Sz7l+/qJgy8fyEZMND5Ms7C0sEaOvoBHiWSpTM4vc0xDLCmc6PGv03CtW
# u2KiyqrL8BAB1EYyOShI3IT79arDIDrL+de91FfjmSbBY5j+HvS0l3dXkjP3Hon8
# b74lWwikF0rzErF0n3khVAusx7Sm1oGG+06hz9XAy3Wou+T6Se6oa5LDiQgPTfWR
# /j9FNk8Ju06oSfTh6c03V0ulla0Iwy+HzUl+WmYxFLU0PiaXsmgudNwVqn51zr+B
# i3XPJ85wWuy6GGT7nBDmXNzTNkzK98DBQjTOabQXUZ884Yb9DFNcigmeVTYkyUXZ
# 6hscd8Nyq45A3D3bk+nXnsogK1Z7zZj6XbGft7xgOYvveU6p0+frthbF7MXv+i5q
# cD9HfFmOq4VYHevVesYb6P0CAwEAAaOCAUkwggFFMB0GA1UdDgQWBBRV4Hxb9Uo0
# oHDwJZJe22ixe2B1ATAfBgNVHSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBf
# BgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz
# L2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmww
# bAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29m
# dC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0El
# MjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUF
# BwMIMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAgEAcwxmVPaA9xHf
# fuom0TOSp2hspuf1G0cHW/KXHAuhnpW8/Svlq5j9aKI/8/G6fGIQMr0zlpau8jy8
# 3I4zclGdJjl5S02SxDlUKawtWvgf7ida06PgjeQM1eX4Lut4bbPfT0FEp77G76hh
# ysXxTJNHv5y+fwThUeiiclihZwqcZMpa46m+oV6igTU6I0EnneotMqFs0Q3zHgVV
# r4WXjnG2Bcnkip42edyg/9iXczqTBrEkvTz0UlltpFGaQnLzq+No8VEgq0UG7W1E
# LZGhmmxFmHABwTT6sPJFV68DfLoC0iB9Qbb9VZ8mvbTV5JtISBklTuVAlEkzXi9L
# IjNmx+kndBfKP8dxG/xbRXptQDQDaCsS6ogLkwLgH6zSs+ul9WmzI0F8zImbhnZh
# UziIHheFo4H+ZoojPYcgTK6/3bkSbOabmQFf95B8B6e5WqXbS5s9OdMdUlW1gTI1
# r5u+WAwH2KG7dxneoTbf/jYl3TUtP7AHpyck2c0nun/Q0Cycpa9QUH/Dy01k6tQo
# mNXGjivg2/BGcgZJ0Hw8C6KVelEJ31xLoE21m9+NEgSKCRoFE1Lkma31SyIaynbd
# YEb8sOlZynMdm8yPldDwuF54vJiEArjrcDNXe6BobZUiTWSKvv1DJadR1SUCO/Od
# 21GgU+hZqu+dKgjKAYdeTIvi9R2rtLYwggdxMIIFWaADAgECAhMzAAAAFcXna54C
# 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
# ahC0HVUzWLOhcGbyoYIDVjCCAj4CAQEwggEBoYHZpIHWMIHTMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT
# Tjo2NTFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# U2VydmljZaIjCgEBMAcGBSsOAwIaAxUAJsAKu48NbR5YRg3WSBQCyjzdkvaggYMw
# gYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQsF
# AAIFAOr6y6EwIhgPMjAyNDEyMDQxMjM3NTNaGA8yMDI0MTIwNTEyMzc1M1owdDA6
# BgorBgEEAYRZCgQBMSwwKjAKAgUA6vrLoQIBADAHAgEAAgIi6jAHAgEAAgISjjAK
# AgUA6vwdIQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB
# AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBCwUAA4IBAQBYhQMogIeFDUrR
# IwRU4YMHsPiZCGElvUgmgHgddtSF1sr0JWg6uW+zVTBBOPVsl+IUHxqIx0c72OTL
# MYEhF1tr9geRKbk1x7eJmLMBfbipeAwga/pJq6RPG2vnSilmuq/hrp4hqO2vPBYa
# Pmiir3aQJKVLJwIjeM7GN/M9nWH0gIiWev64zrGtXt+AyMp6Q3wsJN3xTSZVrHWp
# ZduSgBh32boIPCrc4IPPTBbzWEEaGJ0RoC95zkbUd0MF6vT0T9m/FlfiCK3X5l7/
# zSjz3taMFlfojh46I2eEn4MxHWnP/R3Goj+B5qycd5iyGoXYSIJpCodlpF4ISHe+
# QOPU9cJrMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh
# c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD
# b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw
# MTACEzMAAAH1mQmUvPHGUIwAAQAAAfUwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqG
# SIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgpVVpkQHkG9rb
# e9WshGDiA05V/lfVbLlsrKPu+/nCQCowgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHk
# MIG9BCDB1vLSFwh09ISu4kdEv4/tg9eR1Yk8w5x7j5GThqaPNTCBmDCBgKR+MHwx
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1p
# Y3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB9ZkJlLzxxlCMAAEAAAH1
# MCIEIJsAF0QtKp+zWOqWDTyk72cE0rYTpB5dhe7Q+cQ+Uo6GMA0GCSqGSIb3DQEB
# CwUABIICAJ4ztnrZastKQiALqvYzKpIceMaKzKm8bWnJQSQan7rq15d8YJ8iTk8K
# d4zZc4RePPW81kVhQpSLqFBc/lH3US5mfgyekG7/FvO8JbDLshB766UPHlS93e+f
# DJipuAS+N/33aamG3Q+vofDlCkwi4+IfGnZzojuJHDNOn1JJjTj2uIEY0CZsBsMz
# mjdKpPLgEs+XG55cepzC43tC2oFfLE1cAnCENSLUQB8yLYnxVGaS3wM4CIcLZFvR
# cMqYmmygH4ru8ewJjZ7TiUgHjJKsOf086KVjWQlBs/TReHzyXkfaYtyIP0fLnWCi
# 3XBGHBLPTaCdPqrEhe+DGJIijN2e3V1WjkSdRm5otroEgl6hkgEhFQtXQUgag8H5
# Z8VjDQtJD4jGdmeKOKXFhtEZgPmUKaOkZ8B4uCnb2MdKbqO4zgZ80e3oJTKIGp9b
# QFGU6Egr1VP/v+/qUuj/VlLFN4wqzPZJBDkaGYsovpaWJvjAqzTTF2H+g6gdNRpg
# 5Y1jFIh/Q8bobdOQHG70Y8kE6EMLexW6CVJqKKvcbQLLkcDSdwf5BOqdG1lNBmB7
# CK2gnouhRWlijou0fCVKpGp4DjrQMJG18o7LjihsA2wn4IgyseyaxfCeFEcs9gcE
# DZvfzRdHT7I3YaA+v+s6McunkiE7IVcO/qtpgpcKR0TdJ1mV+sV3
# SIG # End signature block