EPM/Agents.ps1
|
class EpmAgentInfo { [string]$AgentUid [string]$MachineName [string]$Deployment [string]$Disabled [string]$Created [string]$Modified } class EpmAgentCollectionDetail { [string]$CollectionType [string]$CollectionUid [string]$Value } class EpmAgentCollectionSummary { [string]$CollectionType [int]$Count } function Resolve-KeeperEpmAgent { <# .Synopsis Resolve agent(s) by UID or machine name (case-insensitive). Returns matching agent(s) as an array. #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [string] $Identifier, [Parameter(Mandatory = $true)] [object] $Plugin ) $uid = $Identifier.Trim() if ([string]::IsNullOrEmpty($uid)) { return @() } $agent = $Plugin.Agents.GetEntity($uid) if ($null -ne $agent) { return @($agent) } $lUid = $uid.ToLowerInvariant() [array]$matched = @($Plugin.Agents.GetAll() | Where-Object { $null -ne $_ -and $_.MachineId -and $_.MachineId.ToLowerInvariant() -eq $lUid }) return $matched } function script:Resolve-KeeperEpmSingleAgent { [CmdletBinding()] param( [Parameter(Mandatory = $true)][string]$Identifier, [Parameter(Mandatory = $true)][object]$Plugin ) [array]$agents = @(Resolve-KeeperEpmAgent -Identifier $Identifier -Plugin $Plugin | Where-Object { $null -ne $_ }) if ($agents.Count -eq 0) { Write-Error -Message "Agent '$Identifier' not found." -ErrorAction Stop } if ($agents.Count -gt 1) { Write-Warning "Multiple agents found with machine name `"$Identifier`":" foreach ($a in $agents) { Write-Warning " UID: $($a.AgentUid) Machine: $($a.MachineId)" } Write-Error -Message "Machine name `"$Identifier`" is not unique. Use Agent UID." -ErrorAction Stop } return $agents[0] } function Get-KeeperEpmAgentList { <# .Synopsis List all EPM agents. .Description Takes no parameters; outputs a table of agents with deployment, machine name, and status. #> [CmdletBinding()] Param () $plugin = ensureEpmPlugin if (-not $plugin) { Write-Error -Message "EPM plugin is not available. Enterprise admin access is required." -ErrorAction Stop } [array]$agents = @($plugin.Agents.GetAll() | Where-Object { $null -ne $_ } | Sort-Object -Property AgentUid) if ($agents.Count -eq 0) { Write-Output "No agents found." return } $rows = [System.Collections.Generic.List[EpmAgentInfo]]::new() foreach ($ag in $agents) { $deploymentName = '' if (-not [string]::IsNullOrEmpty($ag.DeploymentUid)) { $dep = $plugin.Deployments.GetEntity($ag.DeploymentUid) if ($dep) { $deploymentName = if ($dep.Name) { $dep.Name } else { $ag.DeploymentUid } } else { $deploymentName = $ag.DeploymentUid } } $machineName = if ($ag.MachineId) { $ag.MachineId } else { '' } $disabled = if ($ag.Disabled) { 'True' } else { 'False' } $created = [DateTimeOffset]::FromUnixTimeMilliseconds($ag.Created).ToString("yyyy-MM-dd HH:mm:ss") $modified = [DateTimeOffset]::FromUnixTimeMilliseconds($ag.Modified).ToString("yyyy-MM-dd HH:mm:ss") $row = [EpmAgentInfo]::new() $row.AgentUid = $ag.AgentUid $row.MachineName = $machineName $row.Deployment = $deploymentName $row.Disabled = $disabled $row.Created = $created $row.Modified = $modified $rows.Add($row) } $rows | Format-Table -AutoSize } function Get-KeeperEpmAgent { <# .Synopsis View a single EPM agent by UID or machine name. .Parameter AgentUidOrName Agent UID or machine name (case-insensitive). #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true, Position = 0)] [string] $AgentUidOrName ) $plugin = ensureEpmPlugin if (-not $plugin) { Write-Error -Message "EPM plugin is not available. Enterprise admin access is required." -ErrorAction Stop } $agent = Resolve-KeeperEpmSingleAgent -Identifier $AgentUidOrName -Plugin $plugin $created = [DateTimeOffset]::FromUnixTimeMilliseconds($agent.Created).ToString("yyyy-MM-dd HH:mm:ss") $modified = [DateTimeOffset]::FromUnixTimeMilliseconds($agent.Modified).ToString("yyyy-MM-dd HH:mm:ss") Write-Output "Agent: $($agent.MachineId)" Write-Output " UID: $($agent.AgentUid)" Write-Output " Status: $(if ($agent.Disabled) { 'Disabled' } else { 'Active' })" if (-not [string]::IsNullOrEmpty($agent.DeploymentUid)) { Write-Output " Deployment: $($agent.DeploymentUid)" } Write-Output " Created: $created" Write-Output " Modified: $modified" } function Update-KeeperEpmAgent { <# .Synopsis Update EPM agent(s) - deployment and/or enable/disable. .Parameter AgentUidOrName One or more agent UIDs or machine names. .Parameter DeploymentUid Deployment UID to assign, if changing deployment. .Parameter Enable Use 'on' or 'off' to enable or disable the agent(s). #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [string[]] $AgentUidOrName, [Parameter()] [string] $DeploymentUid, [Parameter()] [ValidateSet('on', 'off')] [string] $Enable ) $plugin = ensureEpmPlugin if (-not $plugin) { Write-Error -Message "EPM plugin is not available. Enterprise admin access is required." -ErrorAction Stop } [array]$identifiers = @($AgentUidOrName | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) if ($identifiers.Count -eq 0) { Write-Error -Message "Agent UID(s) or machine name(s) are required for 'update' command." -ErrorAction Stop } $deploymentUidValue = $null if (-not [string]::IsNullOrWhiteSpace($DeploymentUid)) { $deployment = $plugin.Deployments.GetEntity($DeploymentUid.Trim()) if (-not $deployment) { Write-Error -Message "Deployment `"$DeploymentUid`" does not exist." -ErrorAction Stop } $deploymentUidValue = $DeploymentUid.Trim() } $disabledValue = $null if (-not [string]::IsNullOrWhiteSpace($Enable)) { $enableLower = $Enable.Trim().ToLowerInvariant() if ($enableLower -eq 'on') { $disabledValue = $false } elseif ($enableLower -eq 'off') { $disabledValue = $true } } $updateAgents = [System.Collections.Generic.List[KeeperSecurity.Plugins.EPM.UpdateAgent]]::new() foreach ($au in $identifiers) { try { $agent = Resolve-KeeperEpmSingleAgent -Identifier $au -Plugin $plugin } catch { Write-Warning "$($_.Exception.Message) Skipping." continue } $ua = New-Object KeeperSecurity.Plugins.EPM.UpdateAgent $ua.AgentUid = $agent.AgentUid $ua.DeploymentUid = $deploymentUidValue $ua.Disabled = $disabledValue $updateAgents.Add($ua) } if ($updateAgents.Count -eq 0) { return } try { $updateStatus = $plugin.ModifyAgents($updateAgents, $null).GetAwaiter().GetResult() $hasErrors = $false if ($updateStatus.UpdateErrors -and $updateStatus.UpdateErrors.Count -gt 0) { foreach ($err in $updateStatus.UpdateErrors) { if (-not $err.Success) { Write-Error -Message "Failed to update agent `"$($err.EntityUid)`": $($err.Message)" -ErrorAction Continue $hasErrors = $true } } } if ($updateStatus.Update -and $updateStatus.Update.Count -gt 0) { Write-Output "$($updateStatus.Update.Count) agent(s) updated." } elseif (-not $hasErrors) { Write-Warning "No agents were updated. Check server response." } writeEpmModifyStatus -Status $updateStatus $plugin.SyncDown($false).GetAwaiter().GetResult() | Out-Null } catch { Write-Error -Message "Error updating agent(s): $($_.Exception.Message)" -ErrorAction Stop } } function Remove-KeeperEpmAgent { <# .Synopsis Remove an EPM agent by UID or machine name. .Parameter AgentUidOrName Agent UID or machine name (case-insensitive). .Parameter Force If set, skip confirmation prompt before delete. #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] Param ( [Parameter(Mandatory = $true, Position = 0)] [string] $AgentUidOrName, [Parameter()] [switch] $Force ) $plugin = ensureEpmPlugin if (-not $plugin) { Write-Error -Message "EPM plugin is not available. Enterprise admin access is required." -ErrorAction Stop } $agent = Resolve-KeeperEpmSingleAgent -Identifier $AgentUidOrName -Plugin $plugin $label = if ($agent.MachineId) { $agent.MachineId } else { $agent.AgentUid } if (-not $Force -and -not $PSCmdlet.ShouldProcess("agent '$label'", "Delete")) { return } try { $removeStatus = $plugin.ModifyAgents($null, [string[]]@($agent.AgentUid)).GetAwaiter().GetResult() if ($removeStatus.RemoveErrors -and $removeStatus.RemoveErrors.Count -gt 0) { $err = $removeStatus.RemoveErrors[0] Write-Error -Message "Failed to remove agent `"$($err.EntityUid)`": $($err.Message)" -ErrorAction Stop } if ($removeStatus.Remove -and $removeStatus.Remove.Count -gt 0) { Write-Output "Agent '$($agent.AgentUid)' removed." } else { Write-Warning "No agent was removed. Check server response." } writeEpmModifyStatus -Status $removeStatus $plugin.SyncDown($false).GetAwaiter().GetResult() | Out-Null } catch { Write-Error -Message "Error removing agent: $($_.Exception.Message)" -ErrorAction Stop } } function Get-KeeperEpmAgentCollection { <# .Synopsis List collections linked to an EPM agent. .Description By default shows collection type and count. Use -CollectionVerbose for type, UID, and value per collection. .Parameter AgentUid The agent UID. .Parameter CollectionType Optional collection type number to filter results. .Parameter CollectionVerbose If set, show each collection's type, UID, and value instead of grouped counts. #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true, Position = 0)] [string] $AgentUid, [Parameter()] [int] $CollectionType = -1, [Parameter()] [switch] $CollectionVerbose ) $plugin = ensureEpmPlugin if (-not $plugin) { Write-Error -Message "EPM plugin is not available. Enterprise admin access is required." -ErrorAction Stop } $agent = Resolve-KeeperEpmSingleAgent -Identifier $AgentUid -Plugin $plugin [array]$resourceUids = @($plugin.CollectionLinks.GetLinksForObject($agent.AgentUid) | Where-Object { $null -ne $_ -and $_.LinkType -eq [int][PEDM.CollectionLinkType]::CltAgent } | ForEach-Object { $_.CollectionUid }) $collections = [System.Collections.Generic.List[object]]::new() foreach ($uid in $resourceUids) { $c = $plugin.Collections.GetEntity($uid) if ($null -ne $c) { $collections.Add($c) } } if ($PSBoundParameters.ContainsKey('CollectionType')) { [array]$filtered = @($collections | Where-Object { $_.CollectionType -eq $CollectionType }) $collections = $filtered } if ($collections.Count -eq 0) { Write-Output "No collections found for agent '$($agent.AgentUid)'." return } if ($CollectionVerbose) { $collections = $collections | Sort-Object -Property CollectionType, CollectionUid $rows = [System.Collections.Generic.List[EpmAgentCollectionDetail]]::new() foreach ($collection in $collections) { $typeName = getEpmCollectionTypeName -CollectionType $collection.CollectionType $value = '' if ($collection.CollectionData -and $collection.CollectionData.Length -gt 0) { try { $jsonStr = [System.Text.Encoding]::UTF8.GetString($collection.CollectionData) $data = $jsonStr | ConvertFrom-Json $parts = [System.Collections.Generic.List[string]]::new() $data.PSObject.Properties | ForEach-Object { $parts.Add("$($_.Name)=$($_.Value)") } $value = $parts -join ', ' } catch { $value = "(binary data, $($collection.CollectionData.Length) bytes)" } } $row = [EpmAgentCollectionDetail]::new() $row.CollectionType = "$typeName ($($collection.CollectionType))" $row.CollectionUid = $collection.CollectionUid $row.Value = $value $rows.Add($row) } $rows | Format-Table -AutoSize } else { $grouped = $collections | Group-Object -Property CollectionType | Sort-Object -Property Name $rows = [System.Collections.Generic.List[EpmAgentCollectionSummary]]::new() foreach ($g in $grouped) { $typeName = getEpmCollectionTypeName -CollectionType $g.Name $row = [EpmAgentCollectionSummary]::new() $row.CollectionType = "$typeName ($($g.Name))" $row.Count = $g.Count $rows.Add($row) } $rows | Format-Table -AutoSize } } New-Alias -Name kepm-agent-list -Value Get-KeeperEpmAgentList -ErrorAction SilentlyContinue New-Alias -Name kepm-agent-view -Value Get-KeeperEpmAgent -ErrorAction SilentlyContinue New-Alias -Name kepm-agent-edit -Value Update-KeeperEpmAgent -ErrorAction SilentlyContinue New-Alias -Name kepm-agent-delete -Value Remove-KeeperEpmAgent -ErrorAction SilentlyContinue New-Alias -Name kepm-agent-collection -Value Get-KeeperEpmAgentCollection -ErrorAction SilentlyContinue # SIG # Begin signature block # MIInvgYJKoZIhvcNAQcCoIInrzCCJ6sCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBj3UBlZDq4mIQn # qwcJRszFWYEbVkgSFMDkakxFt+q+x6CCITswggWNMIIEdaADAgECAhAOmxiO+dAt # 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa # Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD # ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC # ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E # MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy # unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF # xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1 # 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB # MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR # WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6 # nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB # YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S # UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x # q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB # NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP # TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC # AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp # Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0 # aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB # LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc # Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov # Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy # oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW # juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF # mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z # twGpn1eqXijiuZQwggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0GCSqG # SIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx # GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy # dXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTlaMGkx # CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4 # RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEzODQg # MjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C0Cit # eLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce2vnS # 1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0daE6ZM # swEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6TSXBC # Mo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoAFdE3 # /hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7OhD26j # q22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM1bL5 # OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z8ujo # 7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05huzU # tw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNYmtwm # KwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP/2NP # TLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0TAQH/ # BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYDVR0j # BBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1Ud # JQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0 # cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0 # cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8E # PDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz # dGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATANBgkq # hkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95RysQDK # r2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HLIvda # qpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5BtfQ/g+ # lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnhOE7a # brs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIhdXNS # y0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV9zeK # iwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/jwVYb # KyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYHKi8Q # xAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmCXBVm # zGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l/aCn # HwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZWeE4w # gga0MIIEnKADAgECAhANx6xXBf8hmS5AQyIMOkmGMA0GCSqGSIb3DQEBCwUAMGIx # CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 # dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH # NDAeFw0yNTA1MDcwMDAwMDBaFw0zODAxMTQyMzU5NTlaMGkxCzAJBgNVBAYTAlVT # MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1 # c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEwggIi # MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC0eDHTCphBcr48RsAcrHXbo0Zo # dLRRF51NrY0NlLWZloMsVO1DahGPNRcybEKq+RuwOnPhof6pvF4uGjwjqNjfEvUi # 6wuim5bap+0lgloM2zX4kftn5B1IpYzTqpyFQ/4Bt0mAxAHeHYNnQxqXmRinvuNg # xVBdJkf77S2uPoCj7GH8BLuxBG5AvftBdsOECS1UkxBvMgEdgkFiDNYiOTx4OtiF # cMSkqTtF2hfQz3zQSku2Ws3IfDReb6e3mmdglTcaarps0wjUjsZvkgFkriK9tUKJ # m/s80FiocSk1VYLZlDwFt+cVFBURJg6zMUjZa/zbCclF83bRVFLeGkuAhHiGPMvS # GmhgaTzVyhYn4p0+8y9oHRaQT/aofEnS5xLrfxnGpTXiUOeSLsJygoLPp66bkDX1 # ZlAeSpQl92QOMeRxykvq6gbylsXQskBBBnGy3tW/AMOMCZIVNSaz7BX8VtYGqLt9 # MmeOreGPRdtBx3yGOP+rx3rKWDEJlIqLXvJWnY0v5ydPpOjL6s36czwzsucuoKs7 # Yk/ehb//Wx+5kMqIMRvUBDx6z1ev+7psNOdgJMoiwOrUG2ZdSoQbU2rMkpLiQ6bG # RinZbI4OLu9BMIFm1UUl9VnePs6BaaeEWvjJSjNm2qA+sdFUeEY0qVjPKOWug/G6 # X5uAiynM7Bu2ayBjUwIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAd # BgNVHQ4EFgQU729TSunkBnx6yuKQVvYv1Ensy04wHwYDVR0jBBgwFoAU7NfjgtJx # XWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUF # BwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGln # aWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5j # b20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJo # dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNy # bDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQEL # BQADggIBABfO+xaAHP4HPRF2cTC9vgvItTSmf83Qh8WIGjB/T8ObXAZz8OjuhUxj # aaFdleMM0lBryPTQM2qEJPe36zwbSI/mS83afsl3YTj+IQhQE7jU/kXjjytJgnn0 # hvrV6hqWGd3rLAUt6vJy9lMDPjTLxLgXf9r5nWMQwr8Myb9rEVKChHyfpzee5kH0 # F8HABBgr0UdqirZ7bowe9Vj2AIMD8liyrukZ2iA/wdG2th9y1IsA0QF8dTXqvcnT # mpfeQh35k5zOCPmSNq1UH410ANVko43+Cdmu4y81hjajV/gxdEkMx1NKU4uHQcKf # ZxAvBAKqMVuqte69M9J6A47OvgRaPs+2ykgcGV00TYr2Lr3ty9qIijanrUR3anzE # wlvzZiiyfTPjLbnFRsjsYg39OlV8cipDoq7+qNNjqFzeGxcytL5TTLL4ZaoBdqbh # OhZ3ZRDUphPvSRmMThi0vw9vODRzW6AxnJll38F0cuJG7uEBYTptMSbhdhGQDpOX # gpIUsWTjd6xpR6oaQf/DJbg3s6KCLPAlZ66RzIg9sC+NJpud/v4+7RWsWCiKi9EO # LLHfMR2ZyJ/+xhCx9yHbxtl5TPau1j/1MIDpMPx0LckTetiSuEtQvLsNz3Qbp7wG # WqbIiOWCnb5WqxL3/BAPvIXKUjPSxyZsq8WhbaM2tszWkPZPubdcMIIG7TCCBNWg # AwIBAgIQCoDvGEuN8QWC0cR2p5V0aDANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQG # EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0 # IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUgQ0Ex # MB4XDTI1MDYwNDAwMDAwMFoXDTM2MDkwMzIzNTk1OVowYzELMAkGA1UEBhMCVVMx # FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBTSEEy # NTYgUlNBNDA5NiBUaW1lc3RhbXAgUmVzcG9uZGVyIDIwMjUgMTCCAiIwDQYJKoZI # hvcNAQEBBQADggIPADCCAgoCggIBANBGrC0Sxp7Q6q5gVrMrV7pvUf+GcAoB38o3 # zBlCMGMyqJnfFNZx+wvA69HFTBdwbHwBSOeLpvPnZ8ZN+vo8dE2/pPvOx/Vj8Tch # TySA2R4QKpVD7dvNZh6wW2R6kSu9RJt/4QhguSssp3qome7MrxVyfQO9sMx6ZAWj # FDYOzDi8SOhPUWlLnh00Cll8pjrUcCV3K3E0zz09ldQ//nBZZREr4h/GI6Dxb2Uo # yrN0ijtUDVHRXdmncOOMA3CoB/iUSROUINDT98oksouTMYFOnHoRh6+86Ltc5zjP # KHW5KqCvpSduSwhwUmotuQhcg9tw2YD3w6ySSSu+3qU8DD+nigNJFmt6LAHvH3KS # uNLoZLc1Hf2JNMVL4Q1OpbybpMe46YceNA0LfNsnqcnpJeItK/DhKbPxTTuGoX7w # JNdoRORVbPR1VVnDuSeHVZlc4seAO+6d2sC26/PQPdP51ho1zBp+xUIZkpSFA8vW # doUoHLWnqWU3dCCyFG1roSrgHjSHlq8xymLnjCbSLZ49kPmk8iyyizNDIXj//cOg # rY7rlRyTlaCCfw7aSUROwnu7zER6EaJ+AliL7ojTdS5PWPsWeupWs7NpChUk555K # 096V1hE0yZIXe+giAwW00aHzrDchIc2bQhpp0IoKRR7YufAkprxMiXAJQ1XCmnCf # gPf8+3mnAgMBAAGjggGVMIIBkTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTkO/zy # Me39/dfzkXFjGVBDz2GM6DAfBgNVHSMEGDAWgBTvb1NK6eQGfHrK4pBW9i/USezL # TjAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwgZUGCCsG # AQUFBwEBBIGIMIGFMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j # b20wXQYIKwYBBQUHMAKGUWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdp # Q2VydFRydXN0ZWRHNFRpbWVTdGFtcGluZ1JTQTQwOTZTSEEyNTYyMDI1Q0ExLmNy # dDBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGln # aUNlcnRUcnVzdGVkRzRUaW1lU3RhbXBpbmdSU0E0MDk2U0hBMjU2MjAyNUNBMS5j # cmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEB # CwUAA4ICAQBlKq3xHCcEua5gQezRCESeY0ByIfjk9iJP2zWLpQq1b4URGnwWBdEZ # D9gBq9fNaNmFj6Eh8/YmRDfxT7C0k8FUFqNh+tshgb4O6Lgjg8K8elC4+oWCqnU/ # ML9lFfim8/9yJmZSe2F8AQ/UdKFOtj7YMTmqPO9mzskgiC3QYIUP2S3HQvHG1FDu # +WUqW4daIqToXFE/JQ/EABgfZXLWU0ziTN6R3ygQBHMUBaB5bdrPbF6MRYs03h4o # bEMnxYOX8VBRKe1uNnzQVTeLni2nHkX/QqvXnNb+YkDFkxUGtMTaiLR9wjxUxu2h # ECZpqyU1d0IbX6Wq8/gVutDojBIFeRlqAcuEVT0cKsb+zJNEsuEB7O7/cuvTQasn # M9AWcIQfVjnzrvwiCZ85EE8LUkqRhoS3Y50OHgaY7T/lwd6UArb+BOVAkg2oOvol # /DJgddJ35XTxfUlQ+8Hggt8l2Yv7roancJIFcbojBcxlRcGG0LIhp6GvReQGgMgY # xQbV1S3CrWqZzBt1R9xJgKf47CdxVRd/ndUlQ05oxYy2zRWVFjF7mcr4C34Mj3oc # CVccAvlKV9jEnstrniLvUxxVZE/rptb7IRE2lskKPIJgbaP5t2nGj/ULLi49xTcB # ZU8atufk+EMF/cWuiC7POGT75qaL6vdCvHlshtjdNXOCIUjsarfNZzCCB0kwggUx # oAMCAQICEAe0P3SLJmcoVNrErUyxTt0wDQYJKoZIhvcNAQELBQAwaTELMAkGA1UE # BhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2Vy # dCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENB # MTAeFw0yNTEyMzEwMDAwMDBaFw0yOTAxMDIyMzU5NTlaMIHRMRMwEQYLKwYBBAGC # NzwCAQMTAlVTMRkwFwYLKwYBBAGCNzwCAQITCERlbGF3YXJlMR0wGwYDVQQPDBRQ # cml2YXRlIE9yZ2FuaXphdGlvbjEQMA4GA1UEBRMHMzQwNzk4NTELMAkGA1UEBhMC # VVMxETAPBgNVBAgTCElsbGlub2lzMRAwDgYDVQQHEwdDaGljYWdvMR0wGwYDVQQK # ExRLZWVwZXIgU2VjdXJpdHkgSW5jLjEdMBsGA1UEAxMUS2VlcGVyIFNlY3VyaXR5 # IEluYy4wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCUcNMoSVmxAi0a # vG+StFJMNFFTUIOo3HdBZ+0gqA1XpNgUx11vB1vCZrvFsD9m5oA58tdp4gZN3LmQ # aMvCl2ANUT7MilI02Hf1RWlygBzon6iE0GpU3lgRrwrk1dhtLpGsR6dbMKUUHprc # vKpXk90/VN+vhzY1uik1tCTxkDCPu/AYJg7m9+tR2KqvMuYMaMLhii66eWUAGsBC # h/uZxjkGoJF6qZ0DgFd7rW7VYljbfYSNPeZNGTDgB0J/wOsKl0mn612DTseIvAKt # 4vra/FLFukyEyStnfQ8lWYDcLLCMCjNVrzGipmT5E2iyx7Y1RZCIpNwVogp3Ixbk # Gbq5A/41YNOLLd4cFewyB2F037RevBCRsUODZEt1qBf7Jbu3DiYo1G+zTj9E0R1s # FzyijcfdsTm6X5ble+yCJeGkX5XgsyPnZpyz/FX9Fr0N9pMPGWwW2PKyHEnSytXm # 0Dxdq2P4mA4CBUxq7YoV26L2PF6QEh9BQdXTPcnLysUv7SI/a0ECAwEAAaOCAgIw # ggH+MB8GA1UdIwQYMBaAFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB0GA1UdDgQWBBRG # 4H6CH8pvNX632bsdnrda4MtJLDA9BgNVHSAENjA0MDIGBWeBDAEDMCkwJwYIKwYB # BQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAOBgNVHQ8BAf8EBAMC # B4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwgbUGA1UdHwSBrTCBqjBToFGgT4ZNaHR0 # cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25p # bmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwU6BRoE+GTWh0dHA6Ly9jcmw0LmRp # Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNI # QTM4NDIwMjFDQTEuY3JsMIGUBggrBgEFBQcBAQSBhzCBhDAkBggrBgEFBQcwAYYY # aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUFBzAChlBodHRwOi8vY2Fj # ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JT # QTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUA # A4ICAQA1Wlq0WzJa3N6DgjgBU7nagIJBab1prPARXZreX1MOv9VjnS5o0CrfQLr6 # z3bmWHw7xT8dt6bcSwRixqvPJtv4q8Rvo80O3eUMvMxQzqmi7z1zf+HG+/3G4F+2 # IYegvPc8Ui151XCV9rjA8tvFWRLRMX0ZRxY1zfT027HMw0iYL20z44+Cky//FAnL # iRwoNDGiRkZiHbB9YOftPAYNMG3gm1z3zOW5RdfKPrqvMuijE+dfyLIAA6Immpzu # FMH+Wgn8NnSlot9b4YKycaqqdjd7wXDjPub/oQ7VShuCSBWj+UNOTVh0vcZGackc # H1DLVgwp2dcKlxJiQKtkHT/T6LloY6LTe6+8wkVkr8EAv1W+q/+M1a4Ao+ykFbIA # 2LBEmA9qdgoLtenAYIiEg+48SjMPgyBbVPE3bhL1vIqjEIxYCfdmi6wx33oYX7HB # +bJ7zitHw4GgtpfPV8y8QRZImKmeDOKyXjQPDmQM/Eglm/Ns0GzBkVXM8h6UI34b # WZrHz9sbLSE20m5Svmxftvw5zju+I3WsmS/stNfWlOkwU0niUgwPHaz21kjXEA5A # g+aqv26wodqZcnGOlChoWDvSJ8KKgdOFbeAYKAMp1NY7iWV315zpGH19RipCR1NH # 0ND8iIubk3WGNf2rzEfqlOi3h2ywqVkU6AKXHdO5JV4otSKKEDGCBdkwggXVAgEB # MH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYD # VQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNI # QTM4NCAyMDIxIENBMQIQB7Q/dIsmZyhU2sStTLFO3TANBglghkgBZQMEAgEFAKCB # hDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEE # AYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJ # BDEiBCCs5HODLYqlRWFIlKVK38fv36aIP9qXa710TSiofnJxwTANBgkqhkiG9w0B # AQEFAASCAYBzR0Jfa69A7q9y2MxvDNejrHhcW2TgZzk/eFkIZKre9C2n6ifaGVBe # SWArPDZ/IYUd5aFPKgEyh5mwpXBMuxCX81/Z+Y4YyiiAw0Si7f+4DBYR1abmOndd # ZGz4mo54gw3qzPR/7RkXMMRXWCDPJzpyW+bhfbGAkV/C+VI2mu7nAaTIFLyz1R95 # 9E1cZje5y+ZsmZYpF9f/1N0SpXPCvSbZ6AtgvaqM3w0MenNbUpfviVhhCQtsrXeL # i+Gb0gceIluTfThlkKBka31rsGnd6B022GWZ4x9JcLp4rPSzE+NUyRKGD9Wz9bRf # FzM/M5NyDo3giQdqSPJWJH8mPPWvplB9H19s+qU0gDNiqch43jONDG0nLLDrxhmH # SRXZgrx7DMkT4xqx4xdRtnIUxG/JMt69TxwUO0Ayt4G8DXYSeQW0KTOP/pr1PCk4 # 5MTNtykzJHEn6wWI3t8B1D7rDiDHEAwG8MrnG1DbXs+8431mSeyW6qC0BlszzGMI # qYhCZjSq/UChggMmMIIDIgYJKoZIhvcNAQkGMYIDEzCCAw8CAQEwfTBpMQswCQYD # VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD # ZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUg # Q0ExAhAKgO8YS43xBYLRxHanlXRoMA0GCWCGSAFlAwQCAQUAoGkwGAYJKoZIhvcN # AQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjYwNTI2MDQwNjQ2WjAv # BgkqhkiG9w0BCQQxIgQgC2WDNkGUyFtjSxm/PeREZJn8MknNBmH5ANB2W37dTLYw # DQYJKoZIhvcNAQEBBQAEggIArRmVHMnh8XlR5vFbot8ho1V8GEmQh/GWUHwheTUm # YeOEHdf6lR7LRzxNBt8oRuuBo4510YLU739ZAReWqk4lKeUne8Il6Ft/MNc8uXLZ # DZ9U9XfMj/8W8Vsf7P2jYkkRv/x5/C9cDhrrnCNdTPFGGWfRN6Y3eAf98Em8np68 # df/d+FsS+Z2J/vvK71Mlx5mVpMHuiioWe0x9ne/OiVLD1PY1n6qysA2Sj4NQoSxK # s6OE14dQohOwhNYl119j14uu1liQlfP7LRVrpIvCkwbaeMaXmjASshcXIDvph2tJ # zve84QEozX+SM4uab7lBtJSCk7eBU26Tz4AYbKsEaB4+rjROjc+8Jdevgzt76UWW # Ahgs1csmAON3c2f77Zwi4rpWp9dn8AxBQcOzOU8Jk0w9wWbphVi8OOZNi1xNk2f0 # C0IyFKeXbS/4Y7AaseDaUuf8ALUn0Z/OxaISWtNNsW1oXPz7BpsqK+YN5wd0Jy2r # vCApJpUlEYmnA/iqtKRJjIPPrdlB5GOhr4+rlmbjfy74PGS0O9QGV0F9hDSQ84K0 # qdvFrYAcmHUXiOkWz/5gRt2kuycEtpKNKUz2Fc/TrrU4nDDkA0ldVwMwrhR0spon # Rz6ke/T8AR6JcIOgCLFsF/xHjxol/7nyi4vKtgAvQMBfJrybPZoW1xH3kMr8WYNB # otI= # SIG # End signature block |