Obs/bin/GMA/ArcExtensionInstallUninstallScripts/Uninstall-ObservabilityExtension.ps1

#Requires -RunAsAdministrator

##------------------------------------------------------------------
## <copyright file="Uninstall-ObservabilityExtension.ps1" company="Microsoft">
## Copyright (C) Microsoft. All rights reserved.
## </copyright>
##------------------------------------------------------------------

<#
.SYNOPSIS
Uninstalls Observability Arc Extension.
 
.DESCRIPTION
The script uninstalls Observability Arc Extension from HCI environment.
1. It retrieves information from Arc for Server Agent, including subscriptionId, tenantId and resourceGroupName.
2. It installs Az.Accounts and Az.ConnectedMachine modules (if not present already) which are needed as a pre-requisite step.
3. It authenticates the user using 2FA and will continue after user confirms their identity by following the instructions (i.e. "WARNING: To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code ****** to authenticate..").
4. It uninstalls the extension from all cluster nodes or local node only (if the node is not a part of cluster).
 
.EXAMPLE
.\Uninstall-ObservabilityExtension.ps1
.\Uninstall-ObservabilityExtension.ps1 -Verbose
 
.NOTES
If Arc for Server agent is not running on the nodes or if cluster is not registered, the script would fail as it cannot fetch the required details to install the extension.
#>


[CmdletBinding()]
Param()

$ErrorActionPreference = "Stop"
$functionName = $MyInvocation.MyCommand.Name

#region Constants
$customExtName = 'TelemetryAndDiagnostics'
#endregion Constants

#region Helper Functions
Function Get-ExceptionDetails {
    [CmdLetBinding()]
    Param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Management.Automation.ErrorRecord] $ErrorObject
    )

    return @{
        Errormsg = $ErrorObject.ToString()
        Exception = $ErrorObject.Exception.ToString()
        Stacktrace = $ErrorObject.ScriptStackTrace
        Failingline = $ErrorObject.InvocationInfo.Line
        Positionmsg = $ErrorObject.InvocationInfo.PositionMessage
        PScommandpath = $ErrorObject.InvocationInfo.PSCommandPath
        Failinglinenumber = $ErrorObject.InvocationInfo.ScriptLineNumber
        Scriptname = $ErrorObject.InvocationInfo.ScriptName
    } | ConvertTo-Json # The ConvertTo-Json will return the entire hashtable as string.
}

Function Write-Log {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.String] $Message,

        [Parameter(Mandatory=$False)]
        [ValidateSet("INFO","VERBOSE","WARN","ERROR")]
        [System.String] $Level = "INFO"
    )

    $dateTimeStamp = $(Get-Date).ToString('u')
    $formattedMessage = "$dateTimeStamp : $Level : $Message"
    switch($Level.toUpper()) {
        "INFO" {
                    Write-Host $formattedMessage
                    break;
                }
        "VERBOSE" {
                    Write-Verbose $formattedMessage
                    break;
                }
        "WARN" {
                    Write-Warning $formattedMessage
                    break;
                }
        "ERROR" {
                    Write-Error $formattedMessage
                    break;
                }
    }
}

Function Confirm-GetClusterNodeIsAvailable {
    return (
        (Get-Command Get-ClusterNode `
            -ErrorAction SilentlyContinue) -and `
        (Get-ClusterNode `
          -ErrorAction SilentlyContinue `
          -ErrorVariable clusterNodeError `
          -WarningAction SilentlyContinue `
          -WarningVariable clusterNodeWarning) -and `
        $null -eq $clusterNodeError[0] -and `
        $null -eq $clusterNodeWarning[0]
    )
}

Function Get-ClusterNodesLocal {
    [CmdletBinding()]
    Param()

    $functionName = $MyInvocation.MyCommand.Name

    Write-Log "$functionName : Fetching cluster node(s)."

    $clusterNodes = @()

    if (Confirm-GetClusterNodeIsAvailable) {
        $clusterNodes = (Get-ClusterNode).Name
    }
    else {
        $clusterNodes += $env:COMPUTERNAME
    }

    Write-Log "$functionName : Cluster node(s) = $($clusterNodes -join ',')."

    return $clusterNodes
}

Function Get-ArcAgentInfo {
    [CmdletBinding()]
    Param()

    $functionName = $MyInvocation.MyCommand.Name

    try {
        Write-Log "$functionName : Fetching required details from Arc agent."
        
        $arcShow = & "$($env:ProgramW6432)\AzureConnectedMachineAgent\azcmagent.exe" show -j | ConvertFrom-Json

        $requiredArcAgentInfo = [Ordered] @{
            ResourceGroupName = $arcShow.resourceGroup
            SubscriptionId = $arcShow.subscriptionId
            TenantId = $arcShow.tenantId
            CloudName = $arcShow.cloud
            Location = $arcShow.location
        }

        $missingDetails = @()
        foreach ($key in $requiredArcAgentInfo.Keys) {
            if ([System.String]::IsNullOrEmpty($requiredArcAgentInfo[$key])) {
                $missingDetails += $key
            }
        }

        if ($missingDetails.Count -gt 0) {
            Write-Log "$functionName : The script cannot proceed as the required registration detail(s) are missing (i.e. $($missingDetails -join ',')) from the Arc agent." -Level ERROR
        }

        Write-Log "$functionName : Arc agent info = $($requiredArcAgentInfo | ConvertTo-Json -Compress)"

        return $requiredArcAgentInfo
    }
    catch {
        $exceptionDetails = Get-ExceptionDetails $_
        Write-Log "$functionName : Exception occurred while fetching required details from Arc agent. Exception : $exceptionDetails." -Level ERROR
    }
}

Function Install-RequiredPackagesAndModules {
    [CmdletBinding()]
    Param()

    $functionName = $MyInvocation.MyCommand.Name

    Write-Log "$functionName : Installing NuGet package provider."
    Install-PackageProvider -Name NuGet -Force -Confirm:$false

    Write-Log "$functionName : Installing PowershellGet."
    Install-Module -Name PowershellGet -Force -Confirm:$false -SkipPublisherCheck

    $modulesToInstall = @(
        @{
            Name = 'Az.Accounts'
            Version = '2.10.4'
        },
        @{
            Name = 'Az.ConnectedMachine'
            Version = '0.4.1'
        }
    )

    foreach ($module in $modulesToInstall) {
        $mName = $module.Name
        $mVersion = $module.Version
        
        $isModuleInstalled = $false
        Write-Log "$functionName : Checking whether module ($mName) with version ($mVersion) is already present. If not then install or update to the respective version."
        $existingModule = Get-Module -Name $mName -ListAvailable | Sort-Object -Property Version -Descending | Microsoft.PowerShell.Utility\Select-Object -First 1

        if ([System.String]::IsNullOrEmpty($existingModule) -or ($existingModule.Version -lt $mVersion)) {
            Write-Log "$functionName : Installing or updating module ($mName) with version ($mVersion)."
            Write-Log "$functionName : $(Install-Module $mName -RequiredVersion $mVersion -Force -AllowClobber -Verbose)" -Level VERBOSE

            Write-Log "$functionName : Importing module ($mName) with version ($mVersion)."
            Write-Log "$functionName : $(Import-Module $mName -RequiredVersion $mVersion -Force -Verbose)" -Level VERBOSE

            $isModuleInstalled = $true
        }
        else {
            Write-Log "$functionName : Module ($mName) with version ($mVersion) already exists."
        }


        if ($isModuleInstalled) {
            Write-Log "$functionName : Validating whether module ($mName) is installed with correct version ($mVersion) or not."
            $installedModule = Get-Module -Name $mName -ListAvailable | Sort-Object -Property Version -Descending | Microsoft.PowerShell.Utility\Select-Object -First 1

            if ([System.String]::IsNullOrEmpty($installedModule) -or ($installedModule.Version -lt $mVersion)) {
                Write-Log "$functionName : Failed to install or update module ($mName) with version ($mVersion)." `
                    -Level ERROR
            }
            else {
                Write-Log "$functionName : Successfully installed and imported module ($mName) with version ($mVersion)."
            }
        }
    }
}
#endregion Helper Functions

#region Main

try {
    Write-Log "$functionName : Starting uninstallation of Observability extension."
    
    $arcAgentInfo = Get-ArcAgentInfo

    Install-RequiredPackagesAndModules

    Write-Log "$functionName : Authenticate user's identity."
    Connect-AzAccount `
        -SubscriptionId $arcAgentInfo.SubscriptionId `
        -TenantId $arcAgentInfo.TenantId `
        -DeviceCode `
        -ErrorAction Stop `
        | Out-Null
    
    Write-Log "$functionName : User authenticated successfully."
    
    $clusterNodes = Get-ClusterNodesLocal

    $exceptionMessage = [System.String]::Empty

    foreach ($clusterNode in $clusterNodes) {
        Write-Log "$functionName : Checking whether extension already exists on $clusterNode or not."
        $extensionExistsResult = Get-AzConnectedMachineExtension `
                                    -Name $customExtName `
                                    -ResourceGroupName $arcAgentInfo.ResourceGroupName `
                                    -MachineName $clusterNode `
                                    -ErrorAction SilentlyContinue

        if ($null -eq $extensionExistsResult) {
            $exceptionMessage += "ExtensionExistsResult = $extensionExistsResult`n"
            Write-Log "$functionName : Extension does not exists on node $clusterNode. Skipping uninstallation."
            continue
        }
        else {
            Write-Log "$functionName : Extension found on node $clusterNode."
        }

        Write-Log "$functionName : Uninstalling extension from node $clusterNode. This might take a while..."
    
        Remove-AzConnectedMachineExtension `
            -Name $customExtName `
            -ResourceGroupName $arcAgentInfo.ResourceGroupName `
            -MachineName $clusterNode  `
            | Out-Null

        Write-Log "$functionName : Checking whether extension is uninstalled from node $clusterNode or not."
        $uninstallationCheckResult = Get-AzConnectedMachineExtension `
                        -Name $customExtName `
                        -ResourceGroupName $arcAgentInfo.ResourceGroupName `
                        -MachineName $clusterNode `
                        -ErrorAction SilentlyContinue

        if ($null -ne $uninstallationCheckResult) {
            $exceptionMessage += "UninstallationCheckResult = $uninstallationCheckResult`n"
            Write-Log "$functionName : Extension uninstallation failed on node $clusterNode with exception $uninstallationCheckResult." -Level ERROR
            continue
        }
        else {
            Write-Log "$functionName : Observability extension uninstallation succeeded on node $clusterNode."
        }
    }

    if ([System.String]::IsNullOrEmpty($exceptionMessage) -and `
        [System.String]::IsNullOrWhiteSpace($exceptionMessage)) {
        Write-Log "$functionName : Successfully uninstalled Observability extension from all the cluster nodes."
    }
}
catch {
    $exceptionDetails = Get-ExceptionDetails -ErrorObject $_
    Write-Log "$functionName : Observability extension uninstallation failed due to unhandled exception: $exceptionDetails" -Level ERROR
}
#endregion Main
# SIG # Begin signature block
# MIInwgYJKoZIhvcNAQcCoIInszCCJ68CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDcDFNqgPEj5/+U
# LG3iKXkB5oJrMRj9vzj+wTVQd8JuIqCCDXYwggX0MIID3KADAgECAhMzAAADrzBA
# DkyjTQVBAAAAAAOvMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjMxMTE2MTkwOTAwWhcNMjQxMTE0MTkwOTAwWjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDOS8s1ra6f0YGtg0OhEaQa/t3Q+q1MEHhWJhqQVuO5amYXQpy8MDPNoJYk+FWA
# hePP5LxwcSge5aen+f5Q6WNPd6EDxGzotvVpNi5ve0H97S3F7C/axDfKxyNh21MG
# 0W8Sb0vxi/vorcLHOL9i+t2D6yvvDzLlEefUCbQV/zGCBjXGlYJcUj6RAzXyeNAN
# xSpKXAGd7Fh+ocGHPPphcD9LQTOJgG7Y7aYztHqBLJiQQ4eAgZNU4ac6+8LnEGAL
# go1ydC5BJEuJQjYKbNTy959HrKSu7LO3Ws0w8jw6pYdC1IMpdTkk2puTgY2PDNzB
# tLM4evG7FYer3WX+8t1UMYNTAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQURxxxNPIEPGSO8kqz+bgCAQWGXsEw
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzUwMTgyNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAISxFt/zR2frTFPB45Yd
# mhZpB2nNJoOoi+qlgcTlnO4QwlYN1w/vYwbDy/oFJolD5r6FMJd0RGcgEM8q9TgQ
# 2OC7gQEmhweVJ7yuKJlQBH7P7Pg5RiqgV3cSonJ+OM4kFHbP3gPLiyzssSQdRuPY
# 1mIWoGg9i7Y4ZC8ST7WhpSyc0pns2XsUe1XsIjaUcGu7zd7gg97eCUiLRdVklPmp
# XobH9CEAWakRUGNICYN2AgjhRTC4j3KJfqMkU04R6Toyh4/Toswm1uoDcGr5laYn
# TfcX3u5WnJqJLhuPe8Uj9kGAOcyo0O1mNwDa+LhFEzB6CB32+wfJMumfr6degvLT
# e8x55urQLeTjimBQgS49BSUkhFN7ois3cZyNpnrMca5AZaC7pLI72vuqSsSlLalG
# OcZmPHZGYJqZ0BacN274OZ80Q8B11iNokns9Od348bMb5Z4fihxaBWebl8kWEi2O
# PvQImOAeq3nt7UWJBzJYLAGEpfasaA3ZQgIcEXdD+uwo6ymMzDY6UamFOfYqYWXk
# ntxDGu7ngD2ugKUuccYKJJRiiz+LAUcj90BVcSHRLQop9N8zoALr/1sJuwPrVAtx
# HNEgSW+AKBqIxYWM4Ev32l6agSUAezLMbq5f3d8x9qzT031jMDT+sUAoCw0M5wVt
# CUQcqINPuYjbS1WgJyZIiEkBMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# 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
# /Xmfwb1tbWrJUnMTDXpQzTGCGaIwghmeAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAAOvMEAOTKNNBUEAAAAAA68wDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIGN0AZlscMEbcSFZhjzG5XBG
# utXqrgmsC1ldo3YGuhulMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEAsmNvJymeIESQXScEHsxDG+eZeimRe4+dLc++gwAFrSQf2JErsT0p4NaG
# 6E0Igl0UN3EQ6QVbOxnDP7e+5F69j8VsnplpnLENmrI5dyOFZC/sv9/PPRwIo2ED
# gGbDez5kCAUAMduPhHdR8yz3BUV8qX/fnGZK2G1qXkpM0EqK7O+Oq92HUXMOEJoo
# ieXmKANcGAywluBm0QDCbKbDj6scOEfDMtgSJMy+M+q8Cs7ukujBSsfZXajiUpEx
# SLzOrzvylL/A4uTKp2TDlzmdKOuuveFX2VyYh0sUcOqhaI3N6yg8Waa8F1tqJ49r
# p/eTUM4f7KEvllsN4FCf/eFNvCzakqGCFywwghcoBgorBgEEAYI3AwMBMYIXGDCC
# FxQGCSqGSIb3DQEHAqCCFwUwghcBAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZBgsq
# hkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCCLrmgHcnSrWF+KPKnqSo6pXPiqOZBat1jTyv8OTZhr/wIGZpe2OLLY
# GBMyMDI0MDcyMzExMDMwMS45NjZaMASAAgH0oIHYpIHVMIHSMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO
# OjA4NDItNEJFNi1DMjlBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT
# ZXJ2aWNloIIRezCCBycwggUPoAMCAQICEzMAAAHajtXJWgDREbEAAQAAAdowDQYJ
# KoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjMx
# MDEyMTkwNjU5WhcNMjUwMTEwMTkwNjU5WjCB0jELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3Bl
# cmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjowODQyLTRC
# RTYtQzI5QTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCC
# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJOQBgh2tVFR1j8jQA4NDf8b
# cVrXSN080CNKPSQo7S57sCnPU0FKF47w2L6qHtwm4EnClF2cruXFp/l7PpMQg25E
# 7X8xDmvxr8BBE6iASAPCfrTebuvAsZWcJYhy7prgCuBf7OidXpgsW1y8p6Vs7sD2
# aup/0uveYxeXlKtsPjMCplHkk0ba+HgLho0J68Kdji3DM2K59wHy9xrtsYK+X9er
# bDGZ2mmX3765aS5Q7/ugDxMVgzyj80yJn6ULnknD9i4kUQxVhqV1dc/DF6UBeuzf
# ukkMed7trzUEZMRyla7qhvwUeQlgzCQhpZjz+zsQgpXlPczvGd0iqr7lACwfVGog
# 5plIzdExvt1TA8Jmef819aTKwH1IVEIwYLA6uvS8kRdA6RxvMcb//ulNjIuGceyy
# kMAXEynVrLG9VvK4rfrCsGL3j30Lmidug+owrcCjQagYmrGk1hBykXilo9YB8Qyy
# 5Q1KhGuH65V3zFy8a0kwbKBRs8VR4HtoPYw9z1DdcJfZBO2dhzX3yAMipCGm6Smv
# mvavRsXhy805jiApDyN+s0/b7os2z8iRWGJk6M9uuT2493gFV/9JLGg5YJJCJXI+
# yxkO/OXnZJsuGt0+zWLdHS4XIXBG17oPu5KsFfRTHREloR2dI6GwaaxIyDySHYOt
# vIydla7u4lfnfCjY/qKTAgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQUoXyNyVE9ZhOV
# izEUVwhNgL8PX0UwHwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYD
# VR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j
# cmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwG
# CCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIw
# MjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcD
# CDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBALmDVdTtuI0jAEt4
# 1O2OM8CU237TGMyhrGr7FzKCEFaXxtoqk/IObQriq1caHVh2vyuQ24nz3TdOBv7r
# cs/qnPjOxnXFLyZPeaWLsNuARVmUViyVYXjXYB5DwzaWZgScY8GKL7yGjyWrh78W
# JUgh7rE1+5VD5h0/6rs9dBRqAzI9fhZz7spsjt8vnx50WExbBSSH7rfabHendpeq
# bTmW/RfcaT+GFIsT+g2ej7wRKIq/QhnsoF8mpFNPHV1q/WK/rF/ChovkhJMDvlqt
# ETWi97GolOSKamZC9bYgcPKfz28ed25WJy10VtQ9P5+C/2dOfDaz1RmeOb27Kbeg
# ha0SfPcriTfORVvqPDSa3n9N7dhTY7+49I8evoad9hdZ8CfIOPftwt3xTX2RhMZJ
# CVoFlabHcvfb84raFM6cz5EYk+x1aVEiXtgK6R0xn1wjMXHf0AWlSjqRkzvSnRKz
# FsZwEl74VahlKVhI+Ci9RT9+6Gc0xWzJ7zQIUFE3Jiix5+7KL8ArHfBY9UFLz4sn
# boJ7Qip3IADbkU4ZL0iQ8j8Ixra7aSYfToUefmct3dM69ff4Eeh2Kh9NsKiiph58
# 9Ap/xS1jESlrfjL/g/ZboaS5d9a2fA598mubDvLD5x5PP37700vm/Y+PIhmp2fTv
# uS2sndeZBmyTqcUNHRNmCk+njV3nMIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJ
# mQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNh
# dGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1
# WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEB
# BQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjK
# NVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhg
# fWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJp
# rx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/d
# vI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka9
# 7aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKR
# Hh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9itu
# qBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyO
# ArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItb
# oKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6
# bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6t
# AgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQW
# BBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacb
# UzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYz
# aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnku
# aHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIA
# QwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2
# VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwu
# bWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEw
# LTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93
# d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYt
# MjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/q
# XBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6
# U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVt
# I1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis
# 9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTp
# kbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0
# sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138e
# W0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJ
# sWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7
# Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0
# dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQ
# tB1VM1izoXBm8qGCAtcwggJAAgEBMIIBAKGB2KSB1TCB0jELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxh
# bmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjow
# ODQyLTRCRTYtQzI5QTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy
# dmljZaIjCgEBMAcGBSsOAwIaAxUAQqIfIYljHUbNoY0/wjhXRn/sSA2ggYMwgYCk
# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF
# AOpJdEUwIhgPMjAyNDA3MjMwODEzNTdaGA8yMDI0MDcyNDA4MTM1N1owdzA9Bgor
# BgEEAYRZCgQBMS8wLTAKAgUA6kl0RQIBADAKAgEAAgIDkwIB/zAHAgEAAgITfzAK
# AgUA6krFxQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB
# AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAB7theSlPlAa3Om3
# 7CGZyKI/cimh9QcNu7loxdPTrjWjJSJCm9z6dfgUoUJim2ZBCAa7itcKtepbZ9gw
# H+03vuDQB9dhtbr/ddyXaE5rxseQhG+gdoBPJ9k9FiyDRw/ris3brY58JboyAdB/
# BncEOhp9kqHj6nRYMO4rGM8GtVVSMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTACEzMAAAHajtXJWgDREbEAAQAAAdowDQYJYIZIAWUD
# BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B
# CQQxIgQgKCqkEbxM8NAUKJGPMAVs3xGJtatTUgBUnKJcCU7TAf0wgfoGCyqGSIb3
# DQEJEAIvMYHqMIHnMIHkMIG9BCAipaNpYsDvnqTe95Dj1C09020I5ljibrW/ndIC
# Oxg9xjCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB
# 2o7VyVoA0RGxAAEAAAHaMCIEIJGjH57tPoVGjuwlcHBErOvv4lGKR+F1gXonKOCZ
# JQ3JMA0GCSqGSIb3DQEBCwUABIICABbBX87ZhWu7bdU//l/N/yilxV8AEedd+Vb7
# sLVQJVS8EnRwebPCCPc5vAF9Lfsgp6JeYadJp1bEWy5gTMGtDJqaXK2R1cLAbrGJ
# z7VTbyecGArueby8peoJjqABPDxFiD8CneK+hr/qf7tG9kEeSnKypknsVojAUiKV
# 0bsQbJyP7geRNdSsE0mVGXdpyB4fzvd1Wvo6rZs3FtXBbBicFvO04izpsUsLVNFC
# MR8mnpo6L1LZR8eaBYL/MC/5uK+cLUTUFgx40b9kKxt5OyLv5iNNBa0sBZSsSVqU
# ijD8JI5QOeHmFhcsseUcY502DIvje0y+QoKf/3mVtWZamLv1chLZmASoqwjvC2vA
# WjFIT2p9tlFZxVZysOUbMwapk7PlNjueF9CYKcuLLyyYPsIzlO3n/nKP1GIvsT88
# wAdnd0rG3cPQMbbkTtIykH2pqgeyTi5tk6zJyf2KlT3rVZBs9GvmKTmg03mkzQ+Y
# DP254vQzmPEGLt9FB3TFYaSm9yrQZWXL5xYsMie+RnMh9qK0oHYzbmfeHDvd++5M
# UwOs1mqZnf2QO7Oj6TOHwXbXk1c7IXW5KsWivOoRhC5RciJyuGIf94gnHSwWUekt
# xE0nP+/w32beaC6KqIhD8pZ5HpgtrEyTu4KCYYbr06Z1Tz2QSqfL9GbAiDCvRKy4
# YVx7qgcc
# SIG # End signature block