Public/Get-VdcAttribute.ps1

function Get-VdcAttribute {
    <#
    .SYNOPSIS
    Get object attributes as well as policy attributes
 
    .DESCRIPTION
    Retrieves object attributes as well as policy attributes.
    You can either retrieve all attributes or individual ones.
    Policy folders can have attributes as well as policies which apply to the resultant objects.
    For more info on policies and how they are different than attributes, see https://docs.venafi.com/Docs/current/TopNav/Content/Policies/c_policies_tpp.php.
 
    Attribute properties are directly added to the return object for ease of access.
    To retrieve attribute configuration, see the Attribute property of the return object which has properties
    Name, PolicyPath, Locked, Value, Overridden (when applicable), and CustomFieldGuid (when applicable).
 
    .PARAMETER Path
    Path to the object. If the root is excluded, \ved\policy will be prepended.
 
    .PARAMETER Attribute
    Only retrieve the value/values for this attribute.
    For custom fields, you provided either the Guid or Label.
 
    .PARAMETER Class
    Get policy attributes instead of object attributes.
    Provide the class name to retrieve the value for.
    If unsure of the class name, add the value through the TLSPDC UI and go to Support->Policy Attributes to find it.
    The Attribute property will contain the path where the policy was applied.
 
    .PARAMETER All
    Get all object attributes or policy attributes.
    This will perform 3 steps, get the object type, enumerate the attributes for the object type, and get all the values.
    Note, expect this to take longer than usual given the number of api calls.
 
    .PARAMETER NoLookup
    Default functionality is to perform lookup of attributes names to see if they are custom fields or not.
    If they are, pass along the guid instead of name required by the api for custom fields.
    To override this behavior and use the attribute name as is, add -NoLookup.
    Useful if on the off chance you have a custom field with the same name as a built-in attribute.
    Can also be used with -All and the output will contain guids instead of looked up names.
 
    .PARAMETER VenafiSession
    Authentication for the function.
    The value defaults to the script session object $VenafiSession created by New-VenafiSession.
    A TLSPDC token can be provided directly.
    If providing a TLSPDC token, an environment variable named VDC_SERVER must also be set.
 
    .INPUTS
    Path
 
    .OUTPUTS
    PSCustomObject
 
    .EXAMPLE
    Get-VdcAttribute -Path '\VED\Policy\certificates\test.gdb.com' -Attribute 'State'
 
    Name : test.gdb.com
    Path : \VED\Policy\Certificates\test.gdb.com
    TypeName : X509 Server Certificate
    Guid : b7a7221b-e038-41d9-9d49-d7f45c1ca128
    Attribute : {@{Name=State; PolicyPath=\VED\Policy\Certificates; Locked=False; Value=UT; Overridden=False}}
    State : UT
 
    Retrieve a single attribute
 
    .EXAMPLE
    Get-VdcAttribute -Path '\VED\Policy\certificates\test.gdb.com' -Attribute 'State', 'Driver Name'
 
    Name : test.gdb.com
    Path : \VED\Policy\Certificates\test.gdb.com
    TypeName : X509 Server Certificate
    Guid : b7a7221b-e038-41d9-9d49-d7f45c1ca128
    Attribute : {@{Name=State; PolicyPath=\VED\Policy\Certificates; Locked=False; Value=UT; Overridden=False}, @{Name=Driver
                Name; PolicyPath=; Locked=False; Value=appx509certificate; Overridden=False}}
    State : UT
    Driver Name : appx509certificate
 
    Retrieve multiple attributes
 
    .EXAMPLE
    Get-VdcAttribute -Path '\VED\Policy\certificates\test.gdb.com' -Attribute 'ServiceNow Assignment Group'
 
    Name : test.gdb.com
    Path : \VED\Policy\Certificates\test.gdb.com
    TypeName : X509 Server Certificate
    Guid : b7a7221b-e038-41d9-9d49-d7f45c1ca128
    Attribute : {@{CustomFieldGuid={7f214dec-9878-495f-a96c-57291f0d42da}; Name=ServiceNow Assignment Group;
                                PolicyPath=; Locked=False; Value=Venafi Management; Overridden=False}}
    ServiceNow Assignment Group : Venafi Management
 
    Retrieve a custom field attribute.
    You can specify either the guid or custom field label name.
 
    .EXAMPLE
    Get-VdcAttribute -Path '\VED\Policy\mydevice\myapp' -Attribute 'Certificate' -NoLookup
 
    Name : myapp
    Path : \VED\Policy\mydevice\myapp
    TypeName : Adaptable App
    Guid : b7a7221b-e038-41d9-9d49-d7f45c1ca128
    Attribute : {@{Name=Certificate; PolicyPath=; Value=\VED\Policy\mycert; Locked=False; Overridden=False}}
    Certificate : \VED\Policy\mycert
 
    Retrieve an attribute value without custom value lookup
 
    .EXAMPLE
    Get-VdcAttribute -Path '\VED\Policy\certificates\test.gdb.com' -All
 
    Name : test.gdb.com
    Path : \VED\Policy\Certificates\test.gdb.com
    TypeName : X509 Server Certificate
    Guid : b7a7221b-e038-41d9-9d49-d7f45c1ca128
    Attribute : {@{CustomFieldGuid={7f214dec-9878-495f-a96c-57291f0d42da}; Name=ServiceNow
                                            Assignment Group; PolicyPath=; Locked=False; Value=Venafi Management;
                                            Overridden=False}…}
    ServiceNow Assignment Group : Venafi Management
    City : Salt Lake City
    Consumers : {\VED\Policy\Installations\Agentless\US Zone\mydevice\myapp}
    Contact : local:{b1c77034-c099-4a5c-9911-9e26007817da}
    Country : US
    Created By : WebAdmin
    Driver Name : appx509certificate
    ...
 
    Retrieve all attributes applicable to this object
 
    .EXAMPLE
    Get-VdcAttribute -Path 'Certificates' -Class 'X509 Certificate' -Attribute 'State'
 
    Name : Certificates
    Path : \VED\Policy\Certificates
    TypeName : Policy
    Guid : a91fc152-a9fb-4b49-a7ca-7014b14d73eb
    Attribute : {@{Name=State; PolicyPath=\VED\Policy\Certificates; Locked=False; Value=UT}}
    ClassName : X509 Certificate
    State : UT
 
    Retrieve a policy attribute value for the specified policy folder and class.
    \ved\policy will be prepended to the path.
 
    .EXAMPLE
    Get-VdcAttribute -Path '\VED\Policy\certificates' -Class 'X509 Certificate' -All
 
    Name : Certificates
    Path : \VED\Policy\Certificates
    TypeName : Policy
    Guid : a91fc152-a9fb-4b49-a7ca-7014b14d73eb
    Attribute : {@{CustomFieldGuid={7f214dec-9878-495f-a96c-57291f0d42da}; Name=ServiceNow
                                            Assignment Group; PolicyPath=; Locked=False; Value=}…}
    ClassName : X509 Certificate
    Approver : local:{b1c77034-c099-4a5c-9911-9e26007817da}
    Key Algorithm : RSA
    Key Bit Strength : 2048
    Managed By : Aperture
    Management Type : Enrollment
    Network Validation Disabled : 1
    Notification Disabled : 0
    ...
 
    Retrieve all policy attributes for the specified policy folder and class
 
    .LINK
    http://VenafiPS.readthedocs.io/en/latest/functions/Get-VdcAttribute/
 
    .LINK
    https://github.com/Venafi/VenafiPS/blob/main/VenafiPS/Public/Get-VdcAttribute.ps1
 
    .LINK
    https://docs.venafi.com/Docs/currentSDK/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-findpolicy.php
 
    .LINK
    https://docs.venafi.com/Docs/current/TopNav/Content/SDK/WebSDK/r-SDK-POST-Config-readeffectivepolicy.php
 
    #>

    [CmdletBinding(DefaultParameterSetName = 'Attribute')]
    [Alias('Get-TppAttribute')]

    param (

        [Parameter(Mandatory, ParameterSetName = 'Attribute', ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory, ParameterSetName = 'All', ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('DN')]
        [String] $Path,

        [Parameter(Mandatory, ParameterSetName = 'Attribute')]
        [ValidateNotNullOrEmpty()]
        [String[]] $Attribute,

        [ValidateNotNullOrEmpty()]
        [Alias('ClassName', 'PolicyClass')]
        [string] $Class,

        [Parameter(Mandatory, ParameterSetName = 'All')]
        [switch] $All,

        [Parameter()]
        [switch] $NoLookup,

        [Parameter()]
        [psobject] $VenafiSession
    )

    begin {

        Write-Debug $PSCmdlet.ParameterSetName

        Test-VenafiSession -VenafiSession $VenafiSession -Platform 'VDC'

        $newAttribute = $Attribute
        if ( $All -and $Class ) {
            Write-Verbose "Getting attributes for class $Class"
            $newAttribute = Get-VdcClassAttribute -ClassName $Class | Select-Object -ExpandProperty Name -Unique
        }

        $params = @{

            Method        = 'Post'
            Body          = @{}
            UriLeaf       = 'config/ReadEffectivePolicy'
        }

        if ( $PSBoundParameters.ContainsKey('Class') ) {
            $params.UriLeaf = 'config/FindPolicy'
            $params.Body.Class = $Class
        }
    }

    process {

        $newPath = $Path | ConvertTo-VdcFullPath
        $thisObject = Get-VdcObject -Path $newPath

        if ( $PSBoundParameters.ContainsKey('Class') -and $thisObject.TypeName -ne 'Policy' ) {
            Write-Error ('You are attempting to retrieve policy attributes, but {0} is not a policy path' -f $newPath)
            continue
        }

        # get all attributes if item is an object other than a policy
        # Get-VdcClassAttribute will return matching names from different classes so ensure the list is unique
        if ( $All -and -not $PSBoundParameters.ContainsKey('Class') ) {
            $newAttribute = Get-VdcClassAttribute -ClassName $thisObject.TypeName | Select-Object -ExpandProperty Name -Unique
        }

        $params.Body.ObjectDN = $newPath

        $return = [pscustomobject] @{
            Name      = $thisObject.Name
            Path      = $newPath
            TypeName  = $thisObject.TypeName
            Guid      = $thisObject.Guid
            Attribute = [pscustomobject] @{}
        }

        if ( $PSBoundParameters.ContainsKey('Class') ) {
            $return | Add-Member @{ 'ClassName' = $Class }
        }

        $allAttributes = Invoke-VenafiParallel -InputObject $newAttribute -ScriptBlock {

            $thisAttribute = $PSItem
            $params = ($using:params).Clone()
            Write-Verbose "Processing attribute $thisAttribute"

            $params.Body.AttributeName = $thisAttribute
            $customField = $null

            if ( -not $NoLookup ) {
                $customField = $VenafiSession.CustomField | Where-Object { $_.Label -eq $thisAttribute -or $_.Guid -eq $thisAttribute }

                if ( $customField ) {
                    $params.Body.AttributeName = $customField.Guid
                }
            }

            # disabled is a special kind of attribute which cannot be read with readeffectivepolicy
            if ( $params.Body.AttributeName -eq 'Disabled' ) {
                $response = Invoke-VenafiRestMethod @params -UriLeaf 'Config/Read'
            }
            else {
                $response = Invoke-VenafiRestMethod @params
            }

            if ( $response.Error ) {
                if ( $response.Result -in 601, 112) {
                    Write-Error "'$thisAttribute' is not a valid attribute for $newPath. Are you looking for a policy attribute? If so, add -Class."
                    continue
                }
                elseif ( $response.Result -eq 102) {
                    # attribute is valid, but value not set
                    # we're ok with this one
                }
                else {
                    Write-Error $response.Error
                    continue
                }
            }

            $valueOut = $null

            if ( $response.Values ) {
                switch ($response.Values.GetType().Name) {
                    'Object[]' {
                        switch ($response.Values.Count) {
                            1 {
                                $valueOut = $response.Values[0]
                            }

                            Default {
                                $valueOut = $response.Values
                            }
                        }
                    }
                    Default {
                        $valueOut = $response.Values
                    }
                }
            }

            $newProp = [pscustomobject] @{}

            # only add attributes to the root of the response object if they have a value
            # always add them to .Attribute ($newProp)
            if ( $CustomField ) {
                $newProp | Add-Member @{
                    Name              = $customField.Label
                    'CustomFieldGuid' = $customField.Guid
                }

                if ( $valueOut ) {
                    $using:return | Add-Member @{ $customField.Label = $valueOut }
                }

            }
            else {

                if ( $valueOut ) {
                    $using:return | Add-Member @{ $thisAttribute = $valueOut } -ErrorAction SilentlyContinue
                }

                $newProp | Add-Member @{ Name = $thisAttribute }
            }

            $newProp | Add-Member @{
                Value      = $valueOut
                PolicyPath = $response.PolicyDN
                Locked     = $response.Locked
            }

            # overridden not available at policy level
            if ( -not $PSBoundParameters.ContainsKey('Class') ) {
                $newProp | Add-Member @{ 'Overridden' = $response.Overridden }
            }

            $newProp

        } -ThrottleLimit 20 -ProgressTitle 'Getting attributes'

        $return.Attribute = @($allAttributes)
        $return

    }
}
# SIG # Begin signature block
# MIIoFQYJKoZIhvcNAQcCoIIoBjCCKAICAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU4neY4hrnfkted06wDt62hxf0
# 7+KggiE9MIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkqhkiG9w0B
# AQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk
# IElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5WjBiMQsw
# CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
# ZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQw
# ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz
# 7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS
# 5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7
# bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfI
# SKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jH
# trHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14
# Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2
# h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt
# 6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPR
# iQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ER
# ElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4K
# Jpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUwAwEB/zAd
# BgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAUReuir/SS
# y4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEBBG0wazAk
# BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAC
# hjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURS
# b290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0
# LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAowCDAGBgRV
# HSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/Vwe9mqyh
# hyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLeJLxSA8hO
# 0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE1Od/6Fmo
# 8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc/RzY9HdaXFSMb++h
# UD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbObyMt9H5x
# aiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMIIGrjCCBJag
# AwIBAgIQBzY3tyRUfNhHrP0oZipeWzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQG
# EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
# cnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIw
# MzIzMDAwMDAwWhcNMzcwMzIyMjM1OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UE
# ChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQg
# UlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEF
# AAOCAg8AMIICCgKCAgEAxoY1BkmzwT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCw
# zIP5WvYRoUQVQl+kiPNo+n3znIkLf50fng8zH1ATCyZzlm34V6gCff1DtITaEfFz
# sbPuK4CEiiIY3+vaPcQXf6sZKz5C3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ
# 7Gnf2ZCHRgB720RBidx8ald68Dd5n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7
# QKxfst5Kfc71ORJn7w6lY2zkpsUdzTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/teP
# c5OsLDnipUjW8LAxE6lXKZYnLvWHpo9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCY
# OjgRs/b2nuY7W+yB3iIU2YIqx5K/oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9K
# oRxrOMUp88qqlnNCaJ+2RrOdOqPVA+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6
# dSgkQe1CvwWcZklSUPRR8zZJTYsg0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM
# 1+mYSlg+0wOI/rOP015LdhJRk8mMDDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbC
# dLI/Hgl27KtdRnXiYKNYCQEoAA6EVO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbEC
# AwEAAaOCAV0wggFZMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1N
# hS9zKXaaL3WMaiCPnshvMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9P
# MA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcB
# AQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggr
# BgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
# c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAI
# BgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7Zv
# mKlEIgF+ZtbYIULhsBguEE0TzzBTzr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI
# 2AvlXFvXbYf6hCAlNDFnzbYSlm/EUExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/ty
# dBTX/6tPiix6q4XNQ1/tYLaqT5Fmniye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVP
# ulr3qRCyXen/KFSJ8NWKcXZl2szwcqMj+sAngkSumScbqyQeJsG33irr9p6xeZmB
# o1aGqwpFyd/EjaDnmPv7pp1yr8THwcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc
# 6UsCUqc3fpNTrDsdCEkPlM05et3/JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3c
# HXg65J6t5TRxktcma+Q4c6umAU+9Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0d
# KNPH+ejxmF/7K9h+8kaddSweJywm228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZP
# J/tgZxahZrrdVcA6KYawmKAr7ZVBtzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLe
# Mt8EifAAzV3C+dAjfwAL5HYCJtnwZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDy
# Divl1vupL0QVSucTDh3bNzgaoSv27dZ8/DCCBrAwggSYoAMCAQICEAitQLJg0pxM
# n17Nqb2TrtkwDQYJKoZIhvcNAQEMBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoT
# DERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UE
# AxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIxMDQyOTAwMDAwMFoXDTM2
# MDQyODIzNTk1OVowaTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
# bmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBS
# U0E0MDk2IFNIQTM4NCAyMDIxIENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
# AgoCggIBANW0L0LQKK14t13VOVkbsYhC9TOM6z2Bl3DFu8SFJjCfpI5o2Fz16zQk
# B+FLT9N4Q/QX1x7a+dLVZxpSTw6hV/yImcGRzIEDPk1wJGSzjeIIfTR9TIBXEmtD
# mpnyxTsf8u/LR1oTpkyzASAl8xDTi7L7CPCK4J0JwGWn+piASTWHPVEZ6JAheEUu
# oZ8s4RjCGszF7pNJcEIyj/vG6hzzZWiRok1MghFIUmjeEL0UV13oGBNlxX+yT4Us
# SKRWhDXW+S6cqgAV0Tf+GgaUwnzI6hsy5srC9KejAw50pa85tqtgEuPo1rn3MeHc
# reQYoNjBI0dHs6EPbqOrbZgGgxu3amct0r1EGpIQgY+wOwnXx5syWsL/amBUi0nB
# k+3htFzgb+sm+YzVsvk4EObqzpH1vtP7b5NhNFy8k0UogzYqZihfsHPOiyYlBrKD
# 1Fz2FRlM7WLgXjPy6OjsCqewAyuRsjZ5vvetCB51pmXMu+NIUPN3kRr+21CiRshh
# WJj1fAIWPIMorTmG7NS3DVPQ+EfmdTCN7DCTdhSmW0tddGFNPxKRdt6/WMtyEClB
# 8NXFbSZ2aBFBE1ia3CYrAfSJTVnbeM+BSj5AR1/JgVBzhRAjIVlgimRUwcwhGug4
# GXxmHM14OEUwmU//Y09Mu6oNCFNBfFg9R7P6tuyMMgkCzGw8DFYRAgMBAAGjggFZ
# MIIBVTASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBRoN+Drtjv4XxGG+/5h
# ewiIZfROQjAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8B
# Af8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYIKwYBBQUHAQEEazBpMCQG
# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKG
# NWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290
# RzQuY3J0MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3JsMBwGA1UdIAQVMBMwBwYFZ4EMAQMw
# CAYGZ4EMAQQBMA0GCSqGSIb3DQEBDAUAA4ICAQA6I0Q9jQh27o+8OpnTVuACGqX4
# SDTzLLbmdGb3lHKxAMqvbDAnExKekESfS/2eo3wm1Te8Ol1IbZXVP0n0J7sWgUVQ
# /Zy9toXgdn43ccsi91qqkM/1k2rj6yDR1VB5iJqKisG2vaFIGH7c2IAaERkYzWGZ
# gVb2yeN258TkG19D+D6U/3Y5PZ7Umc9K3SjrXyahlVhI1Rr+1yc//ZDRdobdHLBg
# XPMNqO7giaG9OeE4Ttpuuzad++UhU1rDyulq8aI+20O4M8hPOBSSmfXdzlRt2V0C
# FB9AM3wD4pWywiF1c1LLRtjENByipUuNzW92NyyFPxrOJukYvpAHsEN/lYgggnDw
# zMrv/Sk1XB+JOFX3N4qLCaHLC+kxGv8uGVw5ceG+nKcKBtYmZ7eS5k5f3nqsSc8u
# pHSSrds8pJyGH+PBVhsrI/+PteqIe3Br5qC6/To/RabE6BaRUotBwEiES5ZNq0RA
# 443wFSjO7fEYVgcqLxDEDAhkPDOPriiMPMuPiAsNvzv0zh57ju+168u38HcT5uco
# P6wSrqUvImxB+YJcFWbMbA7KxYbD9iYzDAdLoNMHAmpqQDBISzSoUSC7rRuFCOJZ
# DW3KBVAr6kocnqX9oKcfBnTn8tZSkP2vhUgh+Vc7tJwD7YZF9LRhbr9o4iZghurI
# r6n+lB3nYxs6hlZ4TjCCBsIwggSqoAMCAQICEAVEr/OUnQg5pr/bP1/lYRYwDQYJ
# KoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
# bmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2
# IFRpbWVTdGFtcGluZyBDQTAeFw0yMzA3MTQwMDAwMDBaFw0zNDEwMTMyMzU5NTla
# MEgxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjEgMB4GA1UE
# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
# DwAwggIKAoICAQCjU0WHHYOOW6w+VLMj4M+f1+XS512hDgncL0ijl3o7Kpxn3GIV
# WMGpkxGnzaqyat0QKYoeYmNp01icNXG/OpfrlFCPHCDqx5o7L5Zm42nnaf5bw9Yr
# IBzBl5S0pVCB8s/LB6YwaMqDQtr8fwkklKSCGtpqutg7yl3eGRiF+0XqDWFsnf5x
# XsQGmjzwxS55DxtmUuPI1j5f2kPThPXQx/ZILV5FdZZ1/t0QoRuDwbjmUpW1R9d4
# KTlr4HhZl+NEK0rVlc7vCBfqgmRN/yPjyobutKQhZHDr1eWg2mOzLukF7qr2JPUd
# vJscsrdf3/Dudn0xmWVHVZ1KJC+sK5e+n+T9e3M+Mu5SNPvUu+vUoCw0m+PebmQZ
# BzcBkQ8ctVHNqkxmg4hoYru8QRt4GW3k2Q/gWEH72LEs4VGvtK0VBhTqYggT02ke
# fGRNnQ/fztFejKqrUBXJs8q818Q7aESjpTtC/XN97t0K/3k0EH6mXApYTAA+hWl1
# x4Nk1nXNjxJ2VqUk+tfEayG66B80mC866msBsPf7Kobse1I4qZgJoXGybHGvPrhv
# ltXhEBP+YUcKjP7wtsfVx95sJPC/QoLKoHE9nJKTBLRpcCcNT7e1NtHJXwikcKPs
# CvERLmTgyyIryvEoEyFJUX4GZtM7vvrrkTjYUQfKlLfiUKHzOtOKg8tAewIDAQAB
# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSltu8T
# 5+/N0GSh1VapZTGj3tXjSTBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCBGtbeoKm1mBe8cI1P
# ijxonNgl/8ss5M3qXSKS7IwiAqm4z4Co2efjxe0mgopxLxjdTrbebNfhYJwr7e09
# SI64a7p8Xb3CYTdoSXej65CqEtcnhfOOHpLawkA4n13IoC4leCWdKgV6hCmYtld5
# j9smViuw86e9NwzYmHZPVrlSwradOKmB521BXIxp0bkrxMZ7z5z6eOKTGnaiaXXT
# UOREEr4gDZ6pRND45Ul3CFohxbTPmJUaVLq5vMFpGbrPFvKDNzRusEEm3d5al08z
# jdSNd311RaGlWCZqA0Xe2VC1UIyvVr1MxeFGxSjTredDAHDezJieGYkD6tSRN+9N
# UvPJYCHEVkft2hFLjDLDiOZY4rbbPvlfsELWj+MXkdGqwFXjhr+sJyxB0JozSqg2
# 1Llyln6XeThIX8rC3D0y33XWNmdaifj2p8flTzU8AL2+nCpseQHc2kTmOt44Owde
# OVj0fHMxVaCAEcsUDH6uvP6k63llqmjWIso765qCNVcoFstp8jKastLYOrixRoZr
# uhf9xHdsFWyuq69zOuhJRrfVf8y2OMDY7Bz1tqG4QyzfTkx9HmhwwHcK1ALgXGC7
# KP845VJa1qwXIiNO9OzTF/tQa/8Hdx9xl0RBybhG02wyfFgvZ0dl5Rtztpn5aywG
# Ru9BHvDwX+Db2a2QgESvgBBBijCCB3wwggVkoAMCAQICEASyQEzq0fdqCZAgPWOK
# ulswDQYJKoZIhvcNAQELBQAwaTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
# ZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2ln
# bmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENBMTAeFw0yMzA5MTMwMDAwMDBaFw0y
# NDA5MTIyMzU5NTlaMIGDMQswCQYDVQQGEwJVUzENMAsGA1UECBMEVXRhaDEXMBUG
# A1UEBxMOU2FsdCBMYWtlIENpdHkxFTATBgNVBAoTDFZlbmFmaSwgSW5jLjEeMBwG
# A1UECxMVUHJvZmVzc2lvbmFsIFNlcnZpY2VzMRUwEwYDVQQDEwxWZW5hZmksIElu
# Yy4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDPaBrbDQ0fOiorU2nC
# Zx7SrZVmSiwwhrgzDh4WENmJKVpZbBdtYoERBUQSd3+Y+WY4y/aPAEQ9oF1PNfhu
# nkDiCvY3CnDYeOv2ex8G64w1WQIKKZd2OpsuGLJTShshiT7kQwfQufCfUC6lNuO2
# PreSqztkN8tFWrqNS0YlV18MoGsgCPKZ49eLnwbX33UnqLyiDe8qGE73uxZyIZct
# uWKtQU0ylmkrfZSsevZCSUiqFKjW5y9NPvHwQVbk/ffPZ6eJAIO+ywRSxXu8N1zj
# OWXaKj4HWPu9U6Jwsb4/wnGGPLXex4qyYBdLB8gPnW1Y7dXZJ8RkTbF+7FE31ss5
# 1fIH3zTyJ8gDLEubPfoqruRpf8tDp4Z5UPBue90BShRwIpgYrsU9hCEnRmfG1clE
# p9BTistpki1W2cls99cYlfVpcljXR8plK+cR9hGtxvqkxUXELIAyhkRTddKD1tLS
# hz1+D4VOOSdzPJcWMwP3s9lbwZwsBdZK6Ruy3LNIe+HQ2He3IbJc/LL7IbEyoDDp
# 6WincVwbWyrpGL2BI0D6gLMPZV/Cgqk8meiYsMati4Jgz9TVYbaGL/OCeGf8u797
# wUpOQjxlGFUHVh+/hWCvEZLHRYEj7hYHoHCLouNWZK5qwlnQ6huXuKTn2eSx5hzz
# zlTluOBZmtRteF/DfiVStWR8yQIDAQABo4ICAzCCAf8wHwYDVR0jBBgwFoAUaDfg
# 67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYEFKgZ4jlQ/frPU8WlXRbihgk1MZUo
# MD4GA1UdIAQ3MDUwMwYGZ4EMAQQBMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cu
# ZGlnaWNlcnQuY29tL0NQUzAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYB
# BQUHAwMwgbUGA1UdHwSBrTCBqjBToFGgT4ZNaHR0cDovL2NybDMuZGlnaWNlcnQu
# Y29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAy
# MUNBMS5jcmwwU6BRoE+GTWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy
# dFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMIGU
# BggrBgEFBQcBAQSBhzCBhDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl
# cnQuY29tMFwGCCsGAQUFBzAChlBodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v
# RGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0Ex
# LmNydDAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQANZ3pxjdxS5dfEWE7g
# 75U9E/E2K25iik7LeuC8uKjpidxh9tt9kpGYoFhvv0B7pz82zPords+WGz1S5ezp
# RWVXBGEel/zDWSjUj2VkuNkzyBuC9d62HIgHdu8MOstfIRJRfiIAv9QM3K4OOxAE
# 5N77ta/Jc+tmGqUkvSTA7qRowV9+b5X7NpMGtJrNyjsYuVdW2nr6NZgQdJ3PJp42
# Bjk3iPecqWx1WjguHKe7KoL1pPJZ/EL0o8qlE2r20bGRMfKN4IHrVj/IDoLrcakO
# Z+hm0PTHjqfOvWvsAblz/9N36D2woDitpqLZHFyYtfCggQb/lb/rRwpMBTjgSdRb
# h6n+uY8EEumKdjdyD6TZvlSMEhRth5tqdFUeJmaAK0spBgsd0fzK70tlz3ojp0aa
# bTwLzOxk9PKJiD0lp0o+UB2QjewHV3zbrdR+xrrO8cS9IrdCe60vEd9053/tdHCp
# JbH+heuTyOCC8CjcgAYRf9L5xEGMVi2Ypm6FlQ9BKHwtuJR+xEuwb92iagonDUlV
# m5t4U5GAHLKxyogMAigCm2PJl6golqX4wXUOGQacF6FQ11l76riHjdyvnvcJmXhd
# nBjnDfLd6RzgCGWz2QT9W8yqTEZ8ehOzCZpJUzcQm5nnUUKDTnjOiRoESDFnj/n2
# C+NQp9E25RgLwV8Pkt2PSgjbVDGCBkIwggY+AgEBMH0waTELMAkGA1UEBhMCVVMx
# FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVz
# dGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENBMQIQBLJA
# TOrR92oJkCA9Y4q6WzAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAA
# oQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4w
# DAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUaij/GsPQyF4omDgznsZsC4EP
# t2QwDQYJKoZIhvcNAQEBBQAEggIAi3MJqgUdwWnBSMOplT5OGkA2rBEBYDhNL9V+
# P2CtsqPiYC1rV+jQGl5s0kNuOeWMkmXqYsyxU8QYP409iFLTGoCVjDRW1M589BOw
# h17vBj0Y31nKty7hcMXXBg3IRinFi40yyRhCRCYZY/87tLXIFe3jiV6ZFdlb40Hl
# Rzn0KyaWsUa3IfMHB28sqa7MYEsNTP4Cd9CZAHre52l2aOZqKZ9XpwMCtfaLOVBK
# kY6oLrSu/HOCyyuijZmQGcaazsHAWvrYpHsnFfDVTijIwHWUkZv/646VCXXKF8R0
# 2ghdM8BEDJO4C801FqrHMUz5dP4Ic7UQCuyJLXxtxKNUxLZdYBLTGjpVtdH7hpQY
# 6TEB210P1QQhTMtHeh6vxavto7OwzZzk3ew3xjYKd7Kpc955Jsg5zrr9BJSsQ/Rp
# NWXe8fLQ4Qdh1zIYweQCumErfJ09uwTDTlzcFnlSDG8ifapwoRcw5oOe0Ud5IX+6
# HpT4atcNza5aN07nSRIr2qieQ3Q72vb1yeQJilUwhs3VEVpq9Jq/1KwYkNc968ST
# pAwgwEPVwnJm95bOxDdKzmEuHmCzxgEk4BtqF3dzvk876qI19wUvzkcf3RUymVmE
# KmuFzRZISRI4qWdYO9fCJH6MkyYl6d0OBK2rAL6v/SMNjuh8NZY4JBiuBmR33TQd
# C2occLuhggMgMIIDHAYJKoZIhvcNAQkGMYIDDTCCAwkCAQEwdzBjMQswCQYDVQQG
# EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0
# IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBAhAFRK/z
# lJ0IOaa/2z9f5WEWMA0GCWCGSAFlAwQCAQUAoGkwGAYJKoZIhvcNAQkDMQsGCSqG
# SIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjMxMTE2MTY1NzAyWjAvBgkqhkiG9w0B
# CQQxIgQg8j/Yg32VKDe47uCfItKrpCddZhKzGBm27FOaIbYLB88wDQYJKoZIhvcN
# AQEBBQAEggIAbNXyYqxR1hPe7S7bh51SPDm7BEI5y9165T3N9uDaCGa0O57ITSrO
# kIFEAks3sCJdSDTBTDPkR+vN8FeI5OS6y79hgQapO0lz7F7DQj8BlmebHYuYHa5B
# hEEYXrWQ0HALgRMfFH7cDgVWdSwRozewLRkbLeB6sx2mklhm2z2ZIcMJO76NjFBx
# 2BJm71ooEb18gDppeS3PCbO9qDHE+ZZ43WhAz3fPW8fP2eNMX2A7+UOSNryRLa8c
# FDIvoP1pSLAHuDCT0MJLV7TiatbfQPE7X3WieaFW70UQg/VhG9bT0eNnkm9bvd+z
# dEdHkcUFjBdGPJlWfgpm6E+KRlOmH5JiiuVxpkX0Daor0yG4qHx4vuFQolKlVNB/
# 8UWYczh32PIj4iprFThaJGHDLbJJ3NXT4Qd1tKk8+uH/RYPufYso+2HBzN3MQDMZ
# wFvCEcNxTwAxuc8UF2czx6wnYwO6ax1sJXelWaWEwUXTdf94kDhwiWS7eUpRuMrc
# S78GAd4jEiuwIyb+Fok61CnnsWXrEug4E7bcI2sbvYSJZ90OYQ72ArpRaX6iYNB1
# mO/5RkUL9UKQ+0GXd/MHsDxojLJ6RO20b6A4BIzrafHNlPMVeWnb5DhTxyB9srEv
# 9Fyxu6qxUe0swxOnaalyDsDAIXg6ngtAhgHLiH9wJWoJmhUkLI3p4g0=
# SIG # End signature block