Public/TenantGovernance/Get-EntraOpsTenantGovernanceSnapshotReport.ps1

<#
.SYNOPSIS
    Analyze EntraOps Tenant Governance (UTCM) snapshot health: compares the resource types
    configured in EntraOpsConfig.json against what has actually been persisted to disk, and
    (optionally) enriches the report with the underlying snapshot job details from Microsoft Graph.

.DESCRIPTION
    Helps troubleshoot runs of Save-EntraOpsTenantGovernanceSnapshotJson that reported a
    'partiallySuccessful' (or 'failed') snapshot job, e.g.:
      WARNING: Snapshot job 224f12bf-0057-4817-916b-71a297abada1 completed as 'partiallySuccessful' -
      one or more resource types may have failed to capture. Check the snapshot job details in the
      Microsoft Entra admin center for more information.

    The cmdlet performs two independent checks and correlates them:
    1. Disk inventory: enumerates <SnapshotFolder>/<resourceType>/... and compares the resource
       types found there against TenantGovernanceSnapshot.ResourcesToInclude in EntraOpsConfig.json,
       flagging configured resource types that have no folder ("Missing") or an empty folder
       ("Empty") on disk, as well as resource types found on disk that are no longer part of the
       current configuration ("NotInConfig").
    2. Microsoft Graph job details: retrieves the configurationSnapshotJob object(s) for either the
       explicitly supplied -SnapshotJobId value(s), or the most recent -RecentJobsCount jobs (via
       the List API), including the 'status', 'errorDetails' and requested 'resources' properties -
       none of which are returned by default and require an explicit $select. Microsoft Graph does
       not expose a per-resource-type success/failure list on the job itself, so as a best-effort
       diagnostic the cmdlet cross-references each job's requested resource types against the disk
       inventory (see above) to highlight which of the requested types are likely the ones that
       failed to capture in a 'partiallySuccessful' job.

    The raw 'errorDetails' strings returned by Graph are a single, hard-to-read blob per resource
    type (e.g. "microsoft.entra.conditionalAccessPolicy: Error exporting resource [...]. exceptionMessage(s):msg1 ,msg2 ,...").
    ConvertFrom-EntraOpsTenantGovernanceSnapshotErrorDetail splits each blob into one object per
    individual message (ResourceType, ErrorCategory, ErrorCode, Message, ReferencedObjectIds), and
    near-identical messages that only differ by object Id (e.g. dozens of "Resource '<id>' does not
    exist..." entries for different objects/policies) are deduplicated into a single ErrorSummary row
    with an occurrence count and the full list of affected Ids. For each 'partiallySuccessful'/'failed'
    job, this deduped summary is also printed to the console (grouped by resource type, with occurrence
    counts) using the same Show-EntraOpsWarningSummary display used by other EntraOps cmdlets.

    Requires the "ConfigurationMonitoring.Read.All" (or ReadWrite.All) Microsoft Graph permission to
    read snapshot job details - no write/create permission is needed for this read-only report.

.PARAMETER ConfigFilePath
    Path to the EntraOps config file used to determine the configured resource set. Default is
    <EntraOpsBaseFolder>/EntraOpsConfig.json. Falls back to the recommended default resource set
    (Get-EntraOpsTenantGovernanceResourceDefinition) if the file is missing or can't be parsed.

.PARAMETER SnapshotFolder
    Folder containing the per-resource snapshot files written by Save-EntraOpsTenantGovernanceSnapshotJson.
    Default is <EntraOpsBaseFolder>/TenantGovernance/Snapshots.

.PARAMETER SnapshotJobId
    One or more specific snapshot job Ids to retrieve from Microsoft Graph (e.g. the Id shown in a
    'partiallySuccessful' warning message). If omitted, the most recent -RecentJobsCount jobs are
    retrieved instead.

.PARAMETER RecentJobsCount
    Number of most recent snapshot jobs to retrieve from Microsoft Graph when -SnapshotJobId is not
    specified. Default is 5.

.PARAMETER SkipGraphJobDetails
    Skip the Microsoft Graph lookup entirely and only report the disk-vs-config comparison. Useful
    when not connected to Microsoft Graph, or to only check local file inventory.

.EXAMPLE
    Analyze the snapshot job referenced in a 'partiallySuccessful' warning message.
    Get-EntraOpsTenantGovernanceSnapshotReport -SnapshotJobId "224f12bf-0057-4817-916b-71a297abada1"

.EXAMPLE
    Check the 10 most recent snapshot jobs and compare them against the local snapshot folder.
    Get-EntraOpsTenantGovernanceSnapshotReport -RecentJobsCount 10

.EXAMPLE
    Only compare the local snapshot folder against EntraOpsConfig.json, without calling Microsoft Graph.
    Get-EntraOpsTenantGovernanceSnapshotReport -SkipGraphJobDetails

.EXAMPLE
    Drill into the deduped, per-resource-type error summary of the most recent job(s) instead of the console output.
    (Get-EntraOpsTenantGovernanceSnapshotReport).SnapshotJobs.ErrorSummary | Format-Table ResourceType, ErrorCode, Occurrences, Message -Wrap
#>

function Get-EntraOpsTenantGovernanceSnapshotReport {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)]
        [System.String]$ConfigFilePath = "$EntraOpsBaseFolder/EntraOpsConfig.json"
        ,
        [Parameter(Mandatory = $false)]
        [System.String]$SnapshotFolder = "$EntraOpsBaseFolder/TenantGovernance/Snapshots"
        ,
        [Parameter(Mandatory = $false)]
        [System.String[]]$SnapshotJobId
        ,
        [Parameter(Mandatory = $false)]
        [System.Int32]$RecentJobsCount = 5
        ,
        [Parameter(Mandatory = $false)]
        [switch]$SkipGraphJobDetails
    )

    $ErrorActionPreference = "Stop"

    $SnapshotManifest = $null
    $SnapshotManifestPath = Join-Path $SnapshotFolder '.SnapshotManifest.json'
    if (Test-Path -Path $SnapshotManifestPath -ErrorAction SilentlyContinue) {
        try {
            $SnapshotManifest = Get-Content -Path $SnapshotManifestPath -Raw | ConvertFrom-Json -Depth 10
        } catch {
            Write-Warning "Failed to read/parse snapshot manifest '$SnapshotManifestPath'. Error: $_"
        }
    }
    $LastAttemptManifest = $null
    $LastAttemptManifestPath = Join-Path $SnapshotFolder '.LastAttemptManifest.json'
    if (Test-Path -Path $LastAttemptManifestPath -ErrorAction SilentlyContinue) {
        try {
            $LastAttemptManifest = Get-Content -Path $LastAttemptManifestPath -Raw | ConvertFrom-Json -Depth 10
        } catch {
            Write-Warning "Failed to read/parse latest-attempt manifest '$LastAttemptManifestPath'. Error: $_"
        }
    }

    #region Determine the configured resource set from EntraOpsConfig.json (fallback: recommended default set)
    $ConfiguredResources = $null
    if (Test-Path -Path $ConfigFilePath -ErrorAction SilentlyContinue) {
        try {
            $ConfigContent = Get-Content -Path $ConfigFilePath -Raw | ConvertFrom-Json -Depth 10
            $ConfiguredResources = @($ConfigContent.TenantGovernanceSnapshot.ResourcesToInclude) | Where-Object { $null -ne $_ }
        } catch {
            Write-Warning "Failed to read/parse config file '$ConfigFilePath'. Falling back to the recommended default resource set. Error: $_"
        }
    } else {
        Write-Warning "Config file '$ConfigFilePath' not found. Falling back to the recommended default resource set."
    }
    if (-not $ConfiguredResources -or $ConfiguredResources.Count -eq 0) {
        $ConfiguredResources = @((Get-EntraOpsTenantGovernanceResourceDefinition).DefaultResources)
    }
    # Graph normalizes resourceType to lower-case in the captured snapshot resources/folder names;
    # keep a lower-case lookup set for case-insensitive comparisons throughout.
    $ConfiguredResourcesLower = @($ConfiguredResources | ForEach-Object { $_.ToLowerInvariant() })
    #endregion

    #region Inventory of resource types actually present on disk under $SnapshotFolder
    $DiskInventory = [System.Collections.Generic.List[object]]::new()
    if (Test-Path -Path $SnapshotFolder -ErrorAction SilentlyContinue) {
        $ResourceTypeFolders = Get-ChildItem -Path $SnapshotFolder -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -notlike '.*' }
        foreach ($Folder in $ResourceTypeFolders) {
            $ResourceFiles = @(Get-ChildItem -Path $Folder.FullName -Filter "*.json" -File -Recurse -ErrorAction SilentlyContinue)
            $LastWriteTime = ($ResourceFiles | Sort-Object LastWriteTimeUtc -Descending | Select-Object -First 1).LastWriteTimeUtc
            $DiskInventory.Add(
                [PSCustomObject]@{
                    ResourceType  = $Folder.Name
                    FileCount     = $ResourceFiles.Count
                    LastWriteTime = $LastWriteTime
                }
            )
        }
    } else {
        Write-Warning "Snapshot folder '$SnapshotFolder' not found. No resources have been saved to disk yet."
    }
    $DiskInventoryLookup = @{}
    foreach ($Entry in $DiskInventory) { $DiskInventoryLookup[$Entry.ResourceType.ToLowerInvariant()] = $Entry }
    #endregion

    #region Build resource coverage report: configured resources vs what's actually on disk
    $ResourceTypeStateLookup = @{}
    foreach ($ResourceTypeState in @($SnapshotManifest.ResourceTypeStates)) {
        if ($ResourceTypeState.ResourceType) { $ResourceTypeStateLookup[$ResourceTypeState.ResourceType.ToLowerInvariant()] = $ResourceTypeState }
    }
    $ResourceCoverage = foreach ($Resource in $ConfiguredResources) {
        $DiskEntry = $DiskInventoryLookup[$Resource.ToLowerInvariant()]
        $ResourceTypeState = $ResourceTypeStateLookup[$Resource.ToLowerInvariant()]
        $PublishedResourceCount = if ($null -ne $ResourceTypeState.PublishedResourceCount) {
            [int]$ResourceTypeState.PublishedResourceCount
        } elseif ($DiskEntry) { $DiskEntry.FileCount } else { 0 }
        $AttemptResourceCount = if ($null -ne $ResourceTypeState.AttemptResourceCount) {
            [int]$ResourceTypeState.AttemptResourceCount
        } elseif ($null -ne $ResourceTypeState.CapturedResourceCount) {
            [int]$ResourceTypeState.CapturedResourceCount
        } else { $null }
        $Status = if ($ResourceTypeState.Status -eq 'PreservedStale') {
            "Stale"
        } elseif ($ResourceTypeState.Status -eq 'PublishedWithErrors') {
            "PublishedWithErrors"
        } elseif ($ResourceTypeState.Status -eq 'Published' -and $PublishedResourceCount -eq 0) {
            "Empty"
        } elseif (-not $DiskEntry) {
            "Missing"
        } elseif ($DiskEntry.FileCount -eq 0) {
            "Empty"
        } else {
            "Present"
        }
        [PSCustomObject]@{
            ResourceType           = $Resource
            Status                 = $Status
            FileCount              = if ($DiskEntry) { $DiskEntry.FileCount } else { 0 }
            AttemptResourceCount   = $AttemptResourceCount
            PublishedResourceCount = $PublishedResourceCount
            RetainedResourceCount  = if ($null -ne $ResourceTypeState.RetainedResourceCount) { [int]$ResourceTypeState.RetainedResourceCount } else { 0 }
            LastWriteTime          = if ($DiskEntry) { $DiskEntry.LastWriteTime } else { $null }
            PublishedSnapshotId    = $ResourceTypeState.PublishedSnapshotId
            PublishedSource        = $ResourceTypeState.PublishedSource
            AttemptSnapshotId      = $ResourceTypeState.AttemptSnapshotId
            Diagnostics            = @($ResourceTypeState.Diagnostics)
        }
    }

    # Resource types found on disk that are no longer part of the currently configured resource set
    # (e.g. removed from EntraOpsConfig.json since the last successful snapshot).
    $UnconfiguredOnDisk = @(
        foreach ($Entry in $DiskInventory) {
            if ($ConfiguredResourcesLower -notcontains $Entry.ResourceType.ToLowerInvariant()) {
                [PSCustomObject]@{
                    ResourceType  = $Entry.ResourceType
                    Status        = "NotInConfig"
                    FileCount     = $Entry.FileCount
                    LastWriteTime = $Entry.LastWriteTime
                }
            }
        }
    )

    # An empty category can be valid (for example, a tenant may have no Android policy or
    # group lifecycle policy). It is inventory information, not evidence that a snapshot failed.
    $MissingOrEmptyResourceTypes = @($ResourceCoverage | Where-Object { $_.Status -in @("Missing", "Empty") } | Select-Object -ExpandProperty ResourceType)
    $StaleResourceTypes = @($ResourceCoverage | Where-Object { $_.Status -eq "Stale" } | Select-Object -ExpandProperty ResourceType)
    #endregion

    #region Retrieve snapshot job details from Microsoft Graph (either explicit Id(s), or the most recent jobs)
    $SnapshotJobs = [System.Collections.Generic.List[object]]::new()
    if (-not $SkipGraphJobDetails) {
        $MgContext = Get-MgContext -ErrorAction SilentlyContinue
        $IsRestOnlyMode = [bool]$__EntraOpsSession['UseInvokeRestMethodOnly']
        if (-not $MgContext -and -not $IsRestOnlyMode) {
            Write-Warning "No active Microsoft Graph session found. Run Connect-EntraOps or Connect-MgGraph (scope 'ConfigurationMonitoring.Read.All' or higher) to retrieve snapshot job details. Skipping Microsoft Graph lookup."
        } else {
            $JobIdsToQuery = @()
            if ($SnapshotJobId -and $SnapshotJobId.Count -gt 0) {
                $JobIdsToQuery = @($SnapshotJobId)
            } else {
                try {
                    $RecentJobsList = @(Invoke-EntraOpsMsGraphQuery -Method GET -Uri "/beta/admin/configurationManagement/configurationSnapshotJobs?`$orderby=createdDateTime desc&`$top=$RecentJobsCount" -OutputType PSObject -DisableCache -ThrowOnFailure -FirstPageOnly)
                    $JobIdsToQuery = @($RecentJobsList | Select-Object -ExpandProperty id)
                } catch {
                    Write-Warning "Failed to list recent Tenant Governance snapshot jobs from Microsoft Graph. Error: $_"
                }
            }

            foreach ($JobId in $JobIdsToQuery) {
                try {
                    # 'errorDetails' and 'resources' are not returned by default and require an explicit $select.
                    $JobDetail = Invoke-EntraOpsMsGraphQuery -Method GET -Uri "/beta/admin/configurationManagement/configurationSnapshotJobs/$($JobId)?`$select=id,displayName,description,status,resources,errorDetails,createdDateTime,completedDateTime,resourceLocation,tenantId" -OutputType PSObject -DisableCache -ThrowOnFailure
                } catch {
                    Write-Warning "Failed to retrieve details of snapshot job '$JobId'. Error: $_"
                    continue
                }

                $RequestedResources = @($JobDetail.resources) | Where-Object { $null -ne $_ }

                # Parse the free-text 'errorDetails' blob(s) into one object per individual message
                # (ResourceType, ErrorCategory, ErrorCode, Message, ReferencedObjectIds) instead of
                # the raw, hard-to-read strings returned by Graph.
                $ParsedErrorDetails = @(@($JobDetail.errorDetails) | Where-Object { $_ } | ConvertFrom-EntraOpsTenantGovernanceSnapshotErrorDetail)

                # Graph does not expose a structured per-resource-type job outcome. Its errorDetails
                # are the authoritative signal for a resource type that failed or was only partially
                # captured; an empty folder alone can simply mean the tenant has no objects of that type.
                $ResourceTypesWithErrors = @(
                    $ParsedErrorDetails | ForEach-Object { $_.ResourceType } | Where-Object { $_ } | Select-Object -Unique
                )

                # Deduplicate near-identical messages that only differ by object Id (e.g. dozens of
                # "Resource '<id>' does not exist..." entries for different objects) into a single,
                # readable summary row with an occurrence count and the full list of affected Ids.
                $ErrorSummary = @(
                    $ParsedErrorDetails | Group-Object ResourceType, ErrorCategory, ErrorCode, NormalizedMessage | ForEach-Object {
                        $First = $_.Group[0]
                        [PSCustomObject]@{
                            ResourceType        = $First.ResourceType
                            ErrorCategory       = $First.ErrorCategory
                            ErrorCode           = $First.ErrorCode
                            Occurrences         = $_.Count
                            Message             = $First.NormalizedMessage
                            ReferencedObjectIds = @($_.Group | ForEach-Object { $_.ReferencedObjectIds } | Select-Object -Unique)
                        }
                    }
                )

                $SnapshotJobs.Add(
                    [PSCustomObject]@{
                        SnapshotId             = $JobDetail.id
                        DisplayName            = $JobDetail.displayName
                        Status                 = $JobDetail.status
                        CreatedDateTime        = $JobDetail.createdDateTime
                        CompletedDateTime      = $JobDetail.completedDateTime
                        RequestedResourceCount = $RequestedResources.Count
                        ResourceTypesWithErrors = $ResourceTypesWithErrors
                        ErrorDetails           = $ParsedErrorDetails
                        ErrorSummary           = $ErrorSummary
                    }
                )
            }
        }
    }
    #endregion

    #region Emit warnings summarizing the actionable findings, then return the full report object
    if ($MissingOrEmptyResourceTypes.Count -gt 0) {
        Write-Verbose "$($MissingOrEmptyResourceTypes.Count) configured resource type(s) currently have no data on disk under '$SnapshotFolder': $($MissingOrEmptyResourceTypes -join ', ')"
    }
    $PersistedDiagnostics = @($SnapshotManifest.Diagnostics)
    # A rejected attempt (for example duplicate canonical identities) is recorded only in the
    # last-attempt manifest so the last successful snapshot manifest stays intact. Surface its
    # diagnostics whenever that attempt is a different job than the published snapshot.
    $LastAttemptDiagnostics = @()
    if ($LastAttemptManifest -and -not [string]::IsNullOrWhiteSpace($LastAttemptManifest.SnapshotId) -and $LastAttemptManifest.SnapshotId -ne $SnapshotManifest.SnapshotId) {
        $LastAttemptDiagnostics = @($LastAttemptManifest.Diagnostics)
    }
    if ($SnapshotJobs.Count -eq 0 -and ($PersistedDiagnostics.Count -gt 0 -or $LastAttemptDiagnostics.Count -gt 0)) {
        $PersistedWarnings = @(
            foreach ($Diagnostic in $PersistedDiagnostics) {
                [PSCustomObject]@{
                    Type = $Diagnostic.ResourceType
                    Message = "$(if ($Diagnostic.ErrorCode) { "[$($Diagnostic.ErrorCode)] " })$($Diagnostic.Message)$(if ($Diagnostic.Occurrences -gt 1) { " ($($Diagnostic.Occurrences)x)" }) — $($Diagnostic.RemediationHint)"
                }
            }
            foreach ($Diagnostic in $LastAttemptDiagnostics) {
                [PSCustomObject]@{
                    Type = $Diagnostic.ResourceType
                    Message = "[Last attempt $($LastAttemptManifest.SnapshotId)] $(if ($Diagnostic.ErrorCode) { "[$($Diagnostic.ErrorCode)] " })$($Diagnostic.Message)$(if ($Diagnostic.Occurrences -gt 1) { " ($($Diagnostic.Occurrences)x)" }) — $($Diagnostic.RemediationHint)"
                }
            }
        )
        Show-EntraOpsWarningSummary -WarningMessages $PersistedWarnings
    }
    foreach ($Job in $SnapshotJobs) {
        if ($Job.Status -notin @("partiallySuccessful", "failed")) { continue }

        Write-Host ""
        Write-Host "═══════════════════════════════════════════════════════════════════════════════" -ForegroundColor Yellow
        Write-Host " ⚠ Snapshot job $($Job.SnapshotId) ('$($Job.DisplayName)') - status: $($Job.Status)" -ForegroundColor Yellow
        Write-Host "═══════════════════════════════════════════════════════════════════════════════" -ForegroundColor Yellow

        if ($Job.ResourceTypesWithErrors.Count -gt 0) {
            Write-Host " Resource type(s) with Graph-reported errors: $($Job.ResourceTypesWithErrors -join ', ')" -ForegroundColor Yellow
        }

        # Reuse the shared, grouped/deduped warning display (Type -> distinct Message with occurrence
        # counts) instead of dumping the raw errorDetails blob as a single hard-to-read line.
        $JobWarningMessages = [System.Collections.Generic.List[psobject]]::new()
        foreach ($ErrorEntry in $Job.ErrorSummary) {
            # Avoid double-prefixing when the message already starts with its own bracketed error code.
            $Prefix = if ($ErrorEntry.ErrorCode -and $ErrorEntry.Message -notlike "[$($ErrorEntry.ErrorCode)]*") { "[$($ErrorEntry.ErrorCode)] " } else { "" }
            $MessageText = "$Prefix$($ErrorEntry.Message)$(if ($ErrorEntry.Occurrences -gt 1) { " ($($ErrorEntry.Occurrences)x)" })"
            $JobWarningMessages.Add([PSCustomObject]@{ Type = $ErrorEntry.ResourceType; Message = $MessageText })
        }
        Show-EntraOpsWarningSummary -WarningMessages $JobWarningMessages
    }

    [PSCustomObject]@{
        TenantId                    = $Global:TenantIdContext
        GeneratedDateTime           = (Get-Date).ToUniversalTime().ToString("o")
        ConfigFilePath              = $ConfigFilePath
        SnapshotFolder              = $SnapshotFolder
        SnapshotManifest            = $SnapshotManifest
        LastAttemptManifest         = $LastAttemptManifest
        ConfiguredResourceCount     = $ConfiguredResources.Count
        MissingOrEmptyResourceTypes = $MissingOrEmptyResourceTypes
        StaleResourceTypes          = $StaleResourceTypes
        PersistedDiagnostics        = $PersistedDiagnostics
        LastAttemptDiagnostics      = $LastAttemptDiagnostics
        ErrorDetailsUnavailable     = $SnapshotManifest.ErrorDetailsUnavailable -eq $true
        ResourceCoverage            = @($ResourceCoverage | Sort-Object Status, ResourceType)
        UnconfiguredResourcesOnDisk = $UnconfiguredOnDisk
        SnapshotJobs                = @($SnapshotJobs)
    }
    #endregion
}

# SIG # Begin signature block
# MIIniAYJKoZIhvcNAQcCoIIneTCCJ3UCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDKR7rGLamHqI8I
# ekWDkSgYAezvQuNNMGtyo448YI9FLaCCIDwwggXJMIIEsaADAgECAhAbtY8lKt8j
# AEkoya49fu0nMA0GCSqGSIb3DQEBDAUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK
# ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2Vy
# dGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5l
# dHdvcmsgQ0EwHhcNMjEwNTMxMDY0MzA2WhcNMjkwOTE3MDY0MzA2WjCBgDELMAkG
# A1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAl
# BgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMb
# Q2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMIICIjANBgkqhkiG9w0BAQEFAAOC
# Ag8AMIICCgKCAgEAvfl4+ObVgAxknYYblmRnPyI6HnUBfe/7XGeMycxca6mR5rlC
# 5SBLm9qbe7mZXdmbgEvXhEArJ9PoujC7Pgkap0mV7ytAJMKXx6fumyXvqAoAl4Va
# qp3cKcniNQfrcE1K1sGzVrihQTib0fsxf4/gX+GxPw+OFklg1waNGPmqJhCrKtPQ
# 0WeNG0a+RzDVLnLRxWPa52N5RH5LYySJhi40PylMUosqp8DikSiJucBb+R3Z5yet
# /5oCl8HGUJKbAiy9qbk0WQq/hEr/3/6zn+vZnuCYI+yma3cWKtvMrTscpIfcRnNe
# GWJoRVfkkIJCu0LW8GHgwaM9ZqNd9BjuiMmNF0UpmTJ1AjHuKSbIawLmtWJFfzcV
# WiNoidQ+3k4nsPBADLxNF8tNorMe0AZa3faTz1d1mfX6hhpneLO/lv403L3nUlbl
# s+V1e9dBkQXcXWnjlQ1DufyDljmVe2yAWk8TcsbXfSl6RLpSpCrVQUYJIP4ioLZb
# MI28iQzV13D4h1L92u+sUS4Hs07+0AnacO+Y+lbmbdu1V0vc5SwlFcieLnhO+Nqc
# noYsylfzGuXIkosagpZ6w7xQEmnYDlpGizrrJvojybawgb5CAKT41v4wLsfSRvbl
# jnX98sy50IdbzAYQYLuDNbdeZ95H7JlI8aShFf6tjGKOOVVPORa5sWOd/7cCAwEA
# AaOCAT4wggE6MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLahVDkCw6A/joq8
# +tT4HKbROg79MB8GA1UdIwQYMBaAFAh2zcsH/yT2xc3tu5C84oQ3RnX3MA4GA1Ud
# DwEB/wQEAwIBBjAvBgNVHR8EKDAmMCSgIqAghh5odHRwOi8vY3JsLmNlcnR1bS5w
# bC9jdG5jYS5jcmwwawYIKwYBBQUHAQEEXzBdMCgGCCsGAQUFBzABhhxodHRwOi8v
# c3ViY2Eub2NzcC1jZXJ0dW0uY29tMDEGCCsGAQUFBzAChiVodHRwOi8vcmVwb3Np
# dG9yeS5jZXJ0dW0ucGwvY3RuY2EuY2VyMDkGA1UdIAQyMDAwLgYEVR0gADAmMCQG
# CCsGAQUFBwIBFhhodHRwOi8vd3d3LmNlcnR1bS5wbC9DUFMwDQYJKoZIhvcNAQEM
# BQADggEBAFHCoVgWIhCL/IYx1MIy01z4S6Ivaj5N+KsIHu3V6PrnCA3st8YeDrJ1
# BXqxC/rXdGoABh+kzqrya33YEcARCNQOTWHFOqj6seHjmOriY/1B9ZN9DbxdkjuR
# mmW60F9MvkyNaAMQFtXx0ASKhTP5N+dbLiZpQjy6zbzUeulNndrnQ/tjUoCFBMQl
# lVXwfqefAcVbKPjgzoZwpic7Ofs4LphTZSJ1Ldf23SIikZbr3WjtP6MZl9M7JYjs
# NhI9qX7OAo0FmpKnJ25FspxihjcNpDOO16hO0EoXQ0zF8ads0h5YbBRRfopUofbv
# n3l6XYGaFpAP4bvxSgD5+d2+7arszgowggZrMIIEU6ADAgECAhBX5gfP8LabvnxC
# XoItEBa/MA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhB
# c3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBDb2RlIFNp
# Z25pbmcgMjAyMSBDQTAeFw0yNjEwMDYwODI4MjNaFw0yNzEwMDYwODI4MjJaMIGQ
# MQswCQYDVQQGEwJERTEdMBsGA1UECAwUUmhpbmVsYW5kLVBhbGF0aW5hdGUxEjAQ
# BgNVBAcMCUxhaG5zdGVpbjEeMBwGA1UECgwVT3BlbiBTb3VyY2UgRGV2ZWxvcGVy
# MS4wLAYDVQQDDCVPcGVuIFNvdXJjZSBEZXZlbG9wZXIgVGhvbWFzIE5hdW5oZWlt
# MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAphp2ljSmVsRB6gvkEq7A
# DUMENpKZiDFoAfF1ktD1nCwaVyI+bJ0ROjHwAn4iJgS+vlBDPOUIw2GLHkiqUhOg
# HSwzg6gj45GNmt6zxsKP0zPlhMB/fb0zmz7/F7e3yfKCXLfhbHuk0Es9gJ+8Wmet
# 9StVwwSE/K0MpW06GKA9nPWyE4N/ADwigDIj9rsTKJR4cve9w8G1b3zohpUc/qMr
# onab+HVw5r9b3sOBnnaXC6zvrX4fovRz1ztwfWjRjILLoFG6mW/TuK5F2ICD3Rhl
# 9R2rJWWWhzTDMsQlzvX+/SaM74q3OFpIVNdhvtmWU9pKUBRAyYMijcDqKEFGSzrc
# 8JFmGMpP5TuroWF+N21d3px2eARvW1mV0DCfFAH+Og20SH/gVW6/ltQQEkH/z/Zt
# 49ARyJrgESLOQtlMCIYSoKISdkk4ml4VUS1E/PcbzXEYj3a7m6DOxcd3WuY03UT9
# cnk3p0LMN6LeLYAZgVY+HRloZlH+kW2SkOHNuWgtXvZhAgMBAAGjggF4MIIBdDAM
# BgNVHRMBAf8EAjAAMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly9jY3NjYTIwMjEu
# Y3JsLmNlcnR1bS5wbC9jY3NjYTIwMjEuY3JsMHMGCCsGAQUFBwEBBGcwZTAsBggr
# BgEFBQcwAYYgaHR0cDovL2Njc2NhMjAyMS5vY3NwLWNlcnR1bS5jb20wNQYIKwYB
# BQUHMAKGKWh0dHA6Ly9yZXBvc2l0b3J5LmNlcnR1bS5wbC9jY3NjYTIwMjEuY2Vy
# MB8GA1UdIwQYMBaAFN10XUwA23ufoHTKsW73PMAywHDNMB0GA1UdDgQWBBTBvGyH
# MXj1dMbVnxuof9oP4PY7cDBLBgNVHSAERDBCMAgGBmeBDAEEATA2BgsqhGgBhvZ3
# AgUBBDAnMCUGCCsGAQUFBwIBFhlodHRwczovL3d3dy5jZXJ0dW0ucGwvQ1BTMBMG
# A1UdJQQMMAoGCCsGAQUFBwMDMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsF
# AAOCAgEAkC165c7/r6y3VeEEpNXas9bQs6ZpKPdJffHkf9xoPi4kWvAIWGc4ocG1
# DJLb18RXiDYShDmT5sb/6I08FeipKi2DNJkuOq6yOTJ3S7+hNFIZCyd6i7WKO8ea
# P5I+FAbyJP4+5coZoMg45bUa9JoocaJCm+5n7WiCfN7/EQpClW5Trm4+ZMbp7EMN
# F1uLJgGCMnN0SsoUarp7PHlqupMxMC4Uhhe+lAP2ytcSIU81L97/qjrADbLWqICk
# 9il5iGM+aZndguq/cmfH9rJj4GRZHWnG6WwnzYD9+KgYlRI6em+/9dq+IRiXe2Bs
# thUuIaFW7U2gWw8tBTEYm1UBLHdJx+L1gjJCLs5KT/wy5wU32wZxNDqPUQejUC1x
# GvU3cKqmLgRq+yNf3gFVfzqIoggyM3U9wQvnk7nBtCkpjXS2hdx4yxzcmrlWsDHr
# z6+6/jeyQySzbYUlFmKnc4n9Mw2soHOajEua5poFARVRS5KgUBuiiQ21YFNYzWBo
# qGuECaaAI/C5rGxapMJ3dAOBnHw6NLqBet+ZXBJEccX5y89MLlOQu+jDmJP8HieR
# qDLlwfhVZnw4RY8Rus43NZBwhNlVuOUXqocHKmJhKNlekda3qsJj93eQBQOxKJcc
# w5O08q9jukpH2+yzFAj+7ll33DuNUgWptogc4Awtbx3glzcEvJQwggaCMIIEaqAD
# AgECAhAo8HfBHDa9/l90MkdwJy4DMA0GCSqGSIb3DQEBDAUAMFYxCzAJBgNVBAYT
# AlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4xJDAiBgNVBAMT
# G0NlcnR1bSBUaW1lc3RhbXBpbmcgMjAyMSBDQTAeFw0yNjAzMTEwNzM0NTRaFw0z
# NjAyMjcwNzM0NTRaMFAxCzAJBgNVBAYTAlBMMSEwHwYDVQQKDBhBc3NlY28gRGF0
# YSBTeXN0ZW1zIFMuQS4xHjAcBgNVBAMMFUNlcnR1bSBUaW1lc3RhbXAgMjAyNjCC
# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALi9zfBw+xI12hi2F9uNQBNO
# STkTIi13fuh3DiWybI5392bBfJw6tO7zDs7UqP/rejVf4xFTU3XNOPsuOFkP5SPF
# snWwn3VJL+NhNhsuujvYsQtaQnvNo1wk46reTgws1L8OFEdKaxgyqT4zOQx81mLw
# NZSqXNU9Rrb9oAJWJF4Mydz+C1ebu0D0+vgM3tqAQ8TU203fqgKCPqIdHloaFqH9
# eyR+8tnpmy2hnnN59ZOtmsuHeRwf5iTOhlhf0d/qqquLOSIGPgGVL1Hj5XBUox2O
# E7tQRr+l/xxNbL6OuwBO4Aa2EbuxYg37cpXv5i2/lTL4NASB/duf9W4fjA3Ro+GP
# +bC54n2OYiM3Cq8z92ejx8F5HWO2e4/75eC95B9tMw/eq7109GDyW2+2ut5Bn0o8
# MXJTERkyystWbqcnUkomA97qfiDCwvpWuOMdfTOksXe9S+xDxOJaCbiyTivgPulx
# cT+tfQTRXBr5UwtPvc+INnafOEilzVHA11tv+pHcylryxBc08cUihf/ttqkZS0bm
# x3VxQay8uohaTp0//ECA9vYGW51SKdJMJhXvKyIdBLy8WYNchTt8yRITv9Adxg9J
# qGujh+TnP1bW3aQjFtlEl7SzgNeI8OVA/CJAWWUuRT3Au9ToOpHy/flrrknhN4he
# Ew7/6ILMv4HSsj0YZ4otAgMBAAGjggFQMIIBTDB1BggrBgEFBQcBAQRpMGcwOwYI
# KwYBBQUHMAKGL2h0dHA6Ly9zdWJjYS5yZXBvc2l0b3J5LmNlcnR1bS5wbC9jdHNj
# YTIwMjEuY2VyMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2NzcC1jZXJ0dW0u
# Y29tMB8GA1UdIwQYMBaAFL5UAi+/QGxzQ86sCSVOnkNEGu7gMAwGA1UdEwEB/wQC
# MAAwOQYDVR0fBDIwMDAuoCygKoYoaHR0cDovL3N1YmNhLmNybC5jZXJ0dW0ucGwv
# Y3RzY2EyMDIxLmNybDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8E
# BAMCB4AwIgYDVR0gBBswGTAIBgZngQwBBAIwDQYLKoRoAYb2dwIFAQswHQYDVR0O
# BBYEFCM5aiirmhKnpKfyH/lFmr+N/oIDMA0GCSqGSIb3DQEBDAUAA4ICAQBm/pOb
# rAveTqYFPCSthxNVwBHTW4TfuBQY2KGCdMlH6ALjVPYf2XARNGMTXScR0JQ2/c2L
# xLpoHHhRwxFf3d933DhFS165rYj6iPSQKm41rc9AMpTCjMdmai1aK9o870pOzTzL
# RhWFVIxm+5Qf+t4V5z14tPoUsAWdfRKD1Zzvzfe5AHlE9rxOZcGROiULxs3Edf7Z
# J8RZI0jzD9juj2oEX2XS7BVKuNCK1W0h1mjapv028yqo8UtZh5/1Ib48Yyp88BDY
# Shh5PSGUUuS5tYXVfcduIPBe3PdNTYRYdT5VNxkwLgjMzIcr/6SH2fejFJDjnCxj
# vXLuarErJg/IdI+hXdGIBDVOGv1zrkF4ktoQvFZIp5DIAYh0GwyUhc87qqn5UaYT
# j2+iyiBDSNh7aGCrkuAnqg7Qh7frhX+QhMGeJ9SpLpnG76qN85hLyjvYR6gRYiLT
# j8G4fxjqV400/buPw0rBo9yJZyq/8cGdCOLPOYf9qpefTcrZKeO9pg+UJHx8UABb
# aTqizRxm8sUSEycccCCFhIit3AI/3wGuSOADeiA676NcQ7+dsP04YvzvdN7pTZNy
# g672WpBDJdg0eItHl4yomvNeIOWh1qs4QqOW6TI8Ocsi4uSDTLorxLUDnBTwKQzz
# pxd1YUctEgRh32R0J9FkFkeEruJU94RljFr1uDCCBrkwggShoAMCAQICEQCZo4AK
# JlU7ZavcboSms+o5MA0GCSqGSIb3DQEBDAUAMIGAMQswCQYDVQQGEwJQTDEiMCAG
# A1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVt
# IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3Rl
# ZCBOZXR3b3JrIENBIDIwHhcNMjEwNTE5MDUzMjE4WhcNMzYwNTE4MDUzMjE4WjBW
# MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu
# MSQwIgYDVQQDExtDZXJ0dW0gQ29kZSBTaWduaW5nIDIwMjEgQ0EwggIiMA0GCSqG
# SIb3DQEBAQUAA4ICDwAwggIKAoICAQCdI88EMCM7wUYs5zNzPmNdenW6vlxNur3r
# Lfi+5OZ+U3iZIB+AspO+CC/bj+taJUbMbFP1gQBJUzDUCPx7BNLgid1TyztVLn52
# NKgxxu8gpyTr6EjWyGzKU/gnIu+bHAse1LCitX3CaOE13rbuHbtrxF2tPU8f253Q
# gX6eO8yTbGps1Mg+yda3DcTsOYOhSYNCJiL+5wnjZ9weoGRtvFgMHtJg6i671OPX
# IciiHO4Lwo2p9xh/tnj+JmCQEn5QU0NxzrOiRna4kjFaA9ZcwSaG7WAxeC/xoZSx
# F1oK1UPZtKVt+yrsGKqWONoK6f5EmBOAVEK2y4ATDSkb34UD7JA32f+Rm0wsr5aj
# zftDhA5mBipVZDjHpwzv8bTKzCDUSUuUmPo1govD0RwFcTtMXcfJtm1i+P2UNXad
# PyYVKRxKQATHN3imsfBiNRdN5kiVVeqP55piqgxOkyt+HkwIA4gbmSc3hD8ke66t
# 9MjlcNg73rZZlrLHsAIV/nJ0mmgSjBI/TthoGJDydekOQ2tQD2Dup/+sKQptalDl
# ui59SerVSJg8gAeV7N/ia4mrGoiez+SqV3olVfxyLFt3o/OQOnBmjhKUANoKLYlK
# mUpKEFI0PfoT8Q1W/y6s9LTI6ekbi0igEbFUIBE8KDUGfIwnisEkBw5KcBZ3XwnH
# mfznwlKo8QIDAQABo4IBVTCCAVEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
# 3XRdTADbe5+gdMqxbvc8wDLAcM0wHwYDVR0jBBgwFoAUtqFUOQLDoD+Oirz61Pgc
# ptE6Dv0wDgYDVR0PAQH/BAQDAgEGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMDAGA1Ud
# HwQpMCcwJaAjoCGGH2h0dHA6Ly9jcmwuY2VydHVtLnBsL2N0bmNhMi5jcmwwbAYI
# KwYBBQUHAQEEYDBeMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2NzcC1jZXJ0
# dW0uY29tMDIGCCsGAQUFBzAChiZodHRwOi8vcmVwb3NpdG9yeS5jZXJ0dW0ucGwv
# Y3RuY2EyLmNlcjA5BgNVHSAEMjAwMC4GBFUdIAAwJjAkBggrBgEFBQcCARYYaHR0
# cDovL3d3dy5jZXJ0dW0ucGwvQ1BTMA0GCSqGSIb3DQEBDAUAA4ICAQB1iFgP5Y9Q
# KJpTnxDsQ/z0O23JmoZifZdEOEmQvo/79PQg9nLF/GJe6ZiUBEyDBHMtFRK0mXj3
# Qv3gL0sYXe+PPMfwmreJHvgFGWQ7XwnfMh2YIpBrkvJnjwh8gIlNlUl4KENTK5DL
# qsYPEtRQCw7R6p4s2EtWyDDr/M58iY2UBEqfUU/ujR9NuPyKk0bEcEi62JGxauFY
# zZ/yld13fHaZskIoq2XazjaD0pQkcQiIueL0HKiohS6XgZuUtCKA7S6CHttZEsOb
# QJ1j2s0urIDdqF7xaXFVaTHKtAuMfwi0jXtF3JJphrJfc+FFILgCbX/uYBPBlbBI
# P4Ht4xxk2GmfzMn7oxPITpigQFJFWuzTMUUgdRHTxaTSKRJ/6Uh7ki/pFjf9sUAS
# WgxT69QF9Ki4JF5nBIujxZ2sOU9e1HSCJwOfK07t5nnzbs1LbHuAIGJsRJiQ6HX/
# DW1XFOlXY1rc9HufFhWU+7Uk+hFkJsfzqBz3pRO+5aI6u5abI4Qws4YaeJH7H7M8
# X/YNoaArZbV4Ql+jarKsE0+8XvC4DJB+IVcvC9Ydqahi09mjQse4fxfef0L7E3hh
# o2O3bLDM6v60rIRUCi2fJT2/IRU5ohgyTch4GuYWefSBsp5NPJh4QRTP9DC3gc5Q
# EKtbrTY0Ka87Web7/zScvLmvQBm8JDFpDjCCBrkwggShoAMCAQICEQDn/2nHOzXO
# S5Em2HR8aKWHMA0GCSqGSIb3DQEBDAUAMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UE
# ChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl
# cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBO
# ZXR3b3JrIENBIDIwHhcNMjEwNTE5MDUzMjA3WhcNMzYwNTE4MDUzMjA3WjBWMQsw
# CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMSQw
# IgYDVQQDExtDZXJ0dW0gVGltZXN0YW1waW5nIDIwMjEgQ0EwggIiMA0GCSqGSIb3
# DQEBAQUAA4ICDwAwggIKAoICAQDpEh8ENe25XXrFppVBvoplf0530W0lddNmjtv4
# YSh/f7eDQKFaIqc7tHj7ox+u8vIsJZlroakUeMS3i3T8aJRC+eQs4FF0GqvkM6+W
# ZO8kmzZfxmZaBYmMLs8FktgFYCzywmXeQ1fEExflee2OpbHVk665eXRHjH7MYZIz
# Nnjl2m8Hy8ulB9mR8wL/W0v0pjKNT6G0sfrx1kk+3OGosFUb7yWNnVkWKU4qSxLv
# 16kJ6oVJ4BSbZ4xMak6JLeB8szrK9vwGDpvGDnKCUMYL3NuviwH1x4gZG0JAXU3x
# 2pOAz91JWKJSAmRy/l0s0l5bEYKolg+DMqVhlOANd8Yh5mkQWaMEvBRE/kAGzIqg
# WhwzN2OsKIVtO8mf5sPWSrvyplSABAYa13rMYnzwfg08nljZHghquCJYCa/xHK9a
# cev9UD7Y+usr15d7mrszzxhF1JOr1Mpup2chNSBlyOObhlSO16rwrffVrg/SzaKf
# SndS5swRhr8bnDqNJY9TNyEYvBYpgF95K7p0g4LguR4A++Z1nFIHWVY5v0fNVZmg
# zxD9uVo/gta3onGOQj3JCxgYx0KrCXu4yc9QiVwTFLWbNdHFSjBCt5/8Q9pLuRhV
# ocdCunhcHudMS1CGQ/Rn0+7P+fzMgWdRKfEOh/hjLrnQ8BdJiYrZNxvIOhM2aa3z
# EDHNwwIDAQABo4IBVTCCAVEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUvlQC
# L79AbHNDzqwJJU6eQ0Qa7uAwHwYDVR0jBBgwFoAUtqFUOQLDoD+Oirz61PgcptE6
# Dv0wDgYDVR0PAQH/BAQDAgEGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMDAGA1UdHwQp
# MCcwJaAjoCGGH2h0dHA6Ly9jcmwuY2VydHVtLnBsL2N0bmNhMi5jcmwwbAYIKwYB
# BQUHAQEEYDBeMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2NzcC1jZXJ0dW0u
# Y29tMDIGCCsGAQUFBzAChiZodHRwOi8vcmVwb3NpdG9yeS5jZXJ0dW0ucGwvY3Ru
# Y2EyLmNlcjA5BgNVHSAEMjAwMC4GBFUdIAAwJjAkBggrBgEFBQcCARYYaHR0cDov
# L3d3dy5jZXJ0dW0ucGwvQ1BTMA0GCSqGSIb3DQEBDAUAA4ICAQC4k1l3yUwV/ZQH
# CKCneqAs8EGTnwEUJLdDpokN/dMhKjK0rR5qX8nIIHzxpQR3TAw2IRw1Uxsr2Pli
# G3bCFqSdQTUbfaTq6V3vBzEebDru9QFjqlKnxCF2h1jhLNFFplbPJiW+JSnJTh1f
# KEqEdKdxgl9rVTvlxfEJ7exOn25MGbd/wGPwuSmMxRJVO0wnqgS7kmoJjNF9zqee
# hFSDDP8ZVkWg4EZ2tIS0M3uZmByRr+1Lkwjjt8AtW83mVnZTyTsOb+FNfwJY7DS4
# FmWhkRbgcHRetreoTirPOr/ozyDKhT8MTSTf6Lttg6s6T/u08mDWw6HK04ZRDfQ9
# sb77QV8mKgO44WGP31vXnVKoWVJpFBjPvjL8/Zck/5wXX2iqjOaLStFOR/IQki+E
# hn4zlcgVm22ZVCBPF+l8nAwUUShCtKuSU7GmZLKCmmxQMkSiWILTm8EtVD6AxnJh
# oq8EnhjEEyUoflkeRF2WhFiVQOmWTwZRr44IxWGkNJC6tTorW5rl2Zl+2e9JLPYf
# 3pStAPMDoPKIjVXd6NW2+fZrNUBeDo2eOa5Fn7Brs/HLQff5Xgris5MeUbdVgDrF
# 8uxO6cLPvZPo63j62SsNg55pTWk9fUIF9iPoRbb4QurjoY/woI1RAOKtYtTic6aA
# Jq3u83RIPpGXBSJKwx4KJAOZnCDCtTGCBqIwggaeAgEBMGowVjELMAkGA1UEBhMC
# UEwxITAfBgNVBAoTGEFzc2VjbyBEYXRhIFN5c3RlbXMgUy5BLjEkMCIGA1UEAxMb
# Q2VydHVtIENvZGUgU2lnbmluZyAyMDIxIENBAhBX5gfP8LabvnxCXoItEBa/MA0G
# CWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZI
# hvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcC
# ARUwLwYJKoZIhvcNAQkEMSIEID1Y/S0QDpCcYFz93I69yQFfehA1C7OZQwEH2xDu
# FVrZMA0GCSqGSIb3DQEBAQUABIIBgFISNI1b4gL3gWYUToiOzclLqXhxfCakXKOi
# xJ64ZUhKW03QI1hNMm5co0LUT/Pr5R/DqUDj3edNH1EgFN4r8vsASBt3pvLRnogx
# h0LkCKR8ecO6/2y9eYNvxE9l1wdTklYluNDeZCEDXVsGV9ran6K40xFsNe/98Hm4
# uVnsEzkOE8CsgPxaOHoPwOqDFO4r3d6zpUyrX6cneeza8zm4RHXVoxx2DY902XA9
# fZMZEU61ZQNl0bJ72xrBzDgnSHfdi2UGWacoLuCkP48p841pLdmPwUiLps0vavRN
# DMwOF6xXE+NZ/RnPmYKU0Y6uf31+Oj+2AiJDtQsraXxv6bWSpJIyJzihGNLMuh02
# gbbpGTEt4UkSLWfFUYTR+/tefUBSHPZQHv2Pg/uFLA73InLz80U5OHKjIXujfXlF
# d8LD1h0c2epoqxVykfbR3wVwSB6U8ii57MNwYemqmETEOJsxaHAmJxBNKryMy7Fw
# yT8e+jKMxeD3+Ha5bWV39KNt7E1s66GCBAIwggP+BgkqhkiG9w0BCQYxggPvMIID
# 6wIBATBqMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0
# ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBUaW1lc3RhbXBpbmcgMjAyMSBDQQIQ
# KPB3wRw2vf5fdDJHcCcuAzANBglghkgBZQMEAgIFAKCCAVYwGgYJKoZIhvcNAQkD
# MQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNjEwMTMwOTQyMDlaMDcG
# CyqGSIb3DQEJEAIvMSgwJjAkMCIEIIW+kOEK0kONfMkotq9IsJqyCBd87PiwEmxY
# 05EFJcQ8MD8GCSqGSIb3DQEJBDEyBDDGzaW/zY560H1xa+yUyRUMxc+hCqzYZmNy
# Lu+U2qC6kPuT9vAoJaS8Y7ptJx/rB1IwgZ8GCyqGSIb3DQEJEAIMMYGPMIGMMIGJ
# MIGGBBRXFGhBDKha80JO+RZKUTYQ9NONmDBuMFqkWDBWMQswCQYDVQQGEwJQTDEh
# MB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMSQwIgYDVQQDExtDZXJ0
# dW0gVGltZXN0YW1waW5nIDIwMjEgQ0ECECjwd8EcNr3+X3QyR3AnLgMwDQYJKoZI
# hvcNAQEBBQAEggIAepoqzF3MgerMNMqvwe8mZzmfC2of1NoBqTe30iOBdPmBt9Z1
# 2buoIsE2VhzHkPqGQ21bSeRxcksmuVb2YDvSxHnlD73ZCIoEvccdeZU0xQIaR+6E
# fwgU9CRkCZS4EeEfB5S7cPkhHLi+q+pSrlKd2h0X/QJUBLfgSwnjcEFKod/ZeVNI
# Fc0xwdvUWJ9p+CSGiGriOoofXqb+StdBVDtqylofwY1qvjxpV1oWV2jH2+OB4ZUI
# Cy3+nJKH1C+a1f1udWuWnfWZ8UtXnTvzFmi8CibPfsLeaFGi8Wo4KzV9gqfucZQT
# VWFkaiBpHc1uAurFHngHM+qP/eteRczEMGkxOiaRbWD5b/zFSsRVGqPr+N+/qYa+
# VrEqBVTvN4pVowr/RiwTqkRXSZmly3rGKOipvDABgJ4jNbhQWQZAf9Vn7kzwd2k8
# f6WeCBQ/oO5msUKQhhPKdPPwZbMC7oKBrAgNCQhSCxhJP4i2BdPrLru/xP26q3vc
# QEtUgOrUzuAdvhR0rFOGUtm6ReW65THzvWLywZPdY6+KbvgQpIUQAqKNMP57LFFu
# GQ+4/+5pbKlF9zNRxH5O+pDH9jYyNBJDGzOx86W5B/8ShJ/nSRmkLocbRHGDonzE
# hooLuknPH1WZqwqoPFUTiM6UcB7Vrf6/5OozZ2SF4oVujiopENxa+eYmWAA=
# SIG # End signature block