Get-AADAssessConsentGrantReport.ps1

<#
.SYNOPSIS
    Gets a report of all members of roles
.DESCRIPTION
    This functions returns a list of consent grants in the directory
.EXAMPLE
    PS C:\> Get-AADAssessConsentGrantReport | Export-Csv -Path ".\ConsentGrantReport.csv"
#>

function Get-AADAssessConsentGrantReport {
    [CmdletBinding()]
    param(
        # App Role Assignment Data
        [Parameter(Mandatory = $false)]
        [psobject] $AppRoleAssignmentData,
        # OAuth2 Permission Grants Data
        [Parameter(Mandatory = $false)]
        [psobject] $OAuth2PermissionGrantData,
        # User Data
        [Parameter(Mandatory = $false)]
        [psobject] $UserData,
        # Service Principal Data
        [Parameter(Mandatory = $false)]
        [psobject] $ServicePrincipalData,
        # Generate Report Offline, only using the data passed in parameters
        [Parameter(Mandatory = $false)]
        [switch] $Offline
    )

    Start-AppInsightsRequest $MyInvocation.MyCommand.Name
    try {

        if ($Offline -and (!$PSBoundParameters['AppRoleAssignmentData'] -or !$PSBoundParameters['OAuth2PermissionGrantData'] -or !$PSBoundParameters['UserData'] -or !$PSBoundParameters['ServicePrincipalData'])) {
            Write-Error -Exception (New-Object System.Management.Automation.ItemNotFoundException -ArgumentList 'Use of the offline parameter requires that all data be provided using the data parameters.') -ErrorId 'DataParametersRequired' -Category ObjectNotFound
            return
        }

        function Extract-AppRoleAssignments {
            param (
                #
                [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
                [psobject] $InputObject,
                #
                [Parameter(Mandatory = $true)]
                [psobject] $ListVariable,
                #
                [Parameter(Mandatory = $false)]
                [switch] $PassThru
            )

            process {
                [PSCustomObject[]] $AppRoleAssignment = $InputObject.appRoleAssignedTo
                $ListVariable.AddRange($AppRoleAssignment)
                if ($PassThru) { return $InputObject }
            }
        }

        function Process-OAuth2PermissionGrant {
            param (
                #
                [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
                [psobject] $InputObject,
                #
                [Parameter(Mandatory = $true)]
                [psobject] $LookupCache,
                #
                [Parameter(Mandatory = $false)]
                [switch] $UseLookupCacheOnly
            )

            process {
                $oauth2PermissionGrant = $InputObject
                if ($oauth2PermissionGrant.scope) {
                    [string[]] $scopes = $oauth2PermissionGrant.scope.Trim().Split(" ")
                    foreach ($scope in $scopes) {
                        $client = Get-AadObjectById $oauth2PermissionGrant.clientId -Type servicePrincipal -LookupCache $LookupCache -UseLookupCacheOnly:$UseLookupCacheOnly -Properties 'id,displayName,appOwnerOrganizationId,appRoles'
                        $resource = Get-AadObjectById $oauth2PermissionGrant.resourceId -Type servicePrincipal -LookupCache $LookupCache -UseLookupCacheOnly:$UseLookupCacheOnly -Properties 'id,displayName,appOwnerOrganizationId,appRoles'
                        if ($oauth2PermissionGrant.principalId) {
                            $principal = Get-AadObjectById $oauth2PermissionGrant.principalId -Type user -LookupCache $LookupCache -UseLookupCacheOnly:$UseLookupCacheOnly -Properties 'id,displayName'
                        }

                        [PSCustomObject]@{
                            permission           = $scope
                            permissionType       = 'Delegated'
                            clientId             = $oauth2PermissionGrant.clientId
                            clientDisplayName    = if ($client) { $client.displayName } else { $null }
                            clientOwnerTenantId  = if ($client) { $client.appOwnerOrganizationId } else { $null }
                            resourceObjectId     = $oauth2PermissionGrant.resourceId
                            resourceDisplayName  = if ($resource) { $resource.displayName } else { $null }
                            consentType          = $oauth2PermissionGrant.consentType
                            principalObjectId    = $oauth2PermissionGrant.principalId
                            principalDisplayName = if ($oauth2PermissionGrant.principalId -and $principal) { $principal.displayName } else { $null }
                        }
                    }
                }
            }
        }

        function Process-AppRoleAssignment {
            param (
                #
                [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
                [psobject] $InputObject,
                #
                [Parameter(Mandatory = $true)]
                [psobject] $LookupCache,
                #
                [Parameter(Mandatory = $false)]
                [switch] $UseLookupCacheOnly
            )

            process {
                $appRoleAssignment = $InputObject
                if ($appRoleAssignment.principalType -eq "ServicePrincipal") {
                    $client = Get-AadObjectById $appRoleAssignment.principalId -Type $appRoleAssignment.principalType -LookupCache $LookupCache -UseLookupCacheOnly:$UseLookupCacheOnly -Properties 'id,displayName,appOwnerOrganizationId,appRoles'
                    $resource = Get-AadObjectById $appRoleAssignment.resourceId -Type servicePrincipal -LookupCache $LookupCache -UseLookupCacheOnly:$UseLookupCacheOnly -Properties 'id,displayName,appOwnerOrganizationId,appRoles'
                    $appRole = $resource.appRoles | Where-Object id -EQ $appRoleAssignment.appRoleId

                    [PSCustomObject]@{
                        permission           = if ($appRole) { $appRole.value } else { $null }
                        permissionType       = 'Application'
                        clientId             = $appRoleAssignment.principalId
                        clientDisplayName    = if ($client) { $client.displayName } else { $null }
                        clientOwnerTenantId  = if ($client) { $client.appOwnerOrganizationId } else { $null }
                        resourceObjectId     = $appRoleAssignment.ResourceId
                        resourceDisplayName  = if ($resource) { $resource.displayName } else { $null }
                        consentType          = $null
                        principalObjectId    = $null
                        principalDisplayName = $null
                    }
                }
            }
        }

        $LookupCache = New-LookupCache
        if ($UserData) {
            if ($UserData -is [System.Collections.Generic.Dictionary[guid, pscustomobject]]) {
               $LookupCache.user = $UserData
            }
            else {
                $UserData | Add-AadObjectToLookupCache -Type user -LookupCache $LookupCache
            }
        }
        if ($ServicePrincipalData) {
            if ($ServicePrincipalData -is [System.Collections.Generic.Dictionary[guid, pscustomobject]]) {
                $LookupCache.servicePrincipal = $ServicePrincipalData
            }
            else {
                $ServicePrincipalData | Add-AadObjectToLookupCache -Type servicePrincipal -LookupCache $LookupCache
            }
        }

        ## Get Service Principal Permissions
        if ($AppRoleAssignmentData) {
            $AppRoleAssignmentData | Process-AppRoleAssignment -LookupCache $LookupCache -UseLookupCacheOnly:$Offline
        }
        else {
            Write-Verbose "Getting servicePrincipals..."
            $listAppRoleAssignments = New-Object 'System.Collections.Generic.List[psobject]'
            Get-MsGraphResults 'servicePrincipals?$select=id,displayName,appOwnerOrganizationId,appRoles&$expand=appRoleAssignedTo' -Top 999 `
            | Extract-AppRoleAssignments -ListVariable $listAppRoleAssignments -PassThru `
            | Select-Object -Property "*" -ExcludeProperty 'appRoleAssignedTo', 'appRoleAssignedTo@odata.context' `
            | Add-AadObjectToLookupCache -Type servicePrincipal -LookupCache $LookupCache

            $listAppRoleAssignments | Process-AppRoleAssignment -LookupCache $LookupCache
            Remove-Variable listAppRoleAssignments
        }

        ## Get OAuth2 Permission Grants
        if ($OAuth2PermissionGrantData) {
            $OAuth2PermissionGrantData | Process-OAuth2PermissionGrant -LookupCache $LookupCache -UseLookupCacheOnly:$Offline
        }
        else {
            Write-Verbose "Getting oauth2PermissionGrants..."
            ## https://graph.microsoft.com/v1.0/oauth2PermissionGrants cannot be used for large tenants because it eventually fails with "Service is temorarily unavailable."
            #Get-MsGraphResults 'oauth2PermissionGrants' -Top 999
            $LookupCache.servicePrincipal.Keys | Get-MsGraphResults 'servicePrincipals/{0}/oauth2PermissionGrants' -Top 999 -TotalRequests $LookupCache.servicePrincipal.Count -DisableUniqueIdDeduplication `
            | Process-OAuth2PermissionGrant -LookupCache $LookupCache
        }

    }
    catch { if ($MyInvocation.CommandOrigin -eq 'Runspace') { Write-AppInsightsException $_.Exception }; throw }
    finally { Complete-AppInsightsRequest $MyInvocation.MyCommand.Name -Success $? }
}

# SIG # Begin signature block
# MIInuwYJKoZIhvcNAQcCoIInrDCCJ6gCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCsQmlQUcejnQst
# rT54P8+RqHNkTOK08hZqYgMjhma6CKCCDYUwggYDMIID66ADAgECAhMzAAACU+OD
# 3pbexW7MAAAAAAJTMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjEwOTAyMTgzMzAwWhcNMjIwOTAxMTgzMzAwWjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDLhxHwq3OhH+4J+SX4qS/VQG8HybccH7tnG+BUqrXubfGuDFYPZ29uCuHfQlO1
# lygLgMpJ4Geh6/6poQ5VkDKfVssn6aA1PCzIh8iOPMQ9Mju3sLF9Sn+Pzuaie4BN
# rp0MuZLDEXgVYx2WNjmzqcxC7dY9SC3znOh5qUy2vnmWygC7b9kj0d3JrGtjc5q5
# 0WfV3WLXAQHkeRROsJFBZfXFGoSvRljFFUAjU/zdhP92P+1JiRRRikVy/sqIhMDY
# +7tVdzlE2fwnKOv9LShgKeyEevgMl0B1Fq7E2YeBZKF6KlhmYi9CE1350cnTUoU4
# YpQSnZo0YAnaenREDLfFGKTdAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUlZpLWIccXoxessA/DRbe26glhEMw
# VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
# dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ2NzU5ODAfBgNVHSMEGDAW
# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw
# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx
# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB
# AKVY+yKcJVVxf9W2vNkL5ufjOpqcvVOOOdVyjy1dmsO4O8khWhqrecdVZp09adOZ
# 8kcMtQ0U+oKx484Jg11cc4Ck0FyOBnp+YIFbOxYCqzaqMcaRAgy48n1tbz/EFYiF
# zJmMiGnlgWFCStONPvQOBD2y/Ej3qBRnGy9EZS1EDlRN/8l5Rs3HX2lZhd9WuukR
# bUk83U99TPJyo12cU0Mb3n1HJv/JZpwSyqb3O0o4HExVJSkwN1m42fSVIVtXVVSa
# YZiVpv32GoD/dyAS/gyplfR6FI3RnCOomzlycSqoz0zBCPFiCMhVhQ6qn+J0GhgR
# BJvGKizw+5lTfnBFoqKZJDROz+uGDl9tw6JvnVqAZKGrWv/CsYaegaPePFrAVSxA
# yUwOFTkAqtNC8uAee+rv2V5xLw8FfpKJ5yKiMKnCKrIaFQDr5AZ7f2ejGGDf+8Tz
# OiK1AgBvOW3iTEEa/at8Z4+s1CmnEAkAi0cLjB72CJedU1LAswdOCWM2MDIZVo9j
# 0T74OkJLTjPd3WNEyw0rBXTyhlbYQsYt7ElT2l2TTlF5EmpVixGtj4ChNjWoKr9y
# TAqtadd2Ym5FNB792GzwNwa631BPCgBJmcRpFKXt0VEQq7UXVNYBiBRd+x4yvjqq
# 5aF7XC5nXCgjbCk7IXwmOphNuNDNiRq83Ejjnc7mxrJGMIIHejCCBWKgAwIBAgIK
# YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm
# aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw
# OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD
# VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG
# 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la
# UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc
# 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D
# dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+
# lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk
# kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6
# A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd
# X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL
# 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd
# sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3
# T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS
# 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI
# bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL
# BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD
# uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv
# c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF
# BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h
# cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA
# YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn
# 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7
# v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b
# pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/
# KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy
# CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp
# mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi
# hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb
# BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS
# oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL
# gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX
# cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGYwwghmIAgEBMIGVMH4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p
# Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAJT44Pelt7FbswAAAAA
# AlMwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw
# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIKIm
# dD27CP4DSyl45Kgt0cZjGUw+ZCvQqcDtJJ5utNLDMEIGCisGAQQBgjcCAQwxNDAy
# oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20wDQYJKoZIhvcNAQEBBQAEggEAsffFAtPmNO4AVcN8oVHGmNQ0zvNgrj4xK5Kp
# IE6bMWvQ+DGWB0N03xqJ/tLr6kCYPoB8jrcP4x6brY3tO6QXMmv9b32Gue5KoCpX
# 4tE/191d9Q5eJ7R8FU6JTX3+H+6p6zmMotbejXSvBrDEmL1KZa73HodX14tc5Axw
# eEB43rsPJS/+X4yFmPwGMrC9ioORnd6LXbhqBDOyMfhEB9q5pVHgGSounqg7Xpcz
# pKkxtQfUmwE46ikGBAnDzmaj6y7YrwtxsHEiCqMRMD5dpeycHUzq5aNKoPrmGc+C
# 2GV7BfZnZUBSmSTnxqLgKeZlxu+Ok4xmvSqawOp1wOG+zXpw5aGCFxYwghcSBgor
# BgEEAYI3AwMBMYIXAjCCFv4GCSqGSIb3DQEHAqCCFu8wghbrAgEDMQ8wDQYJYIZI
# AWUDBAIBBQAwggFZBgsqhkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGE
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCDHlxyCLChh+Tfl55vnt7ktjtlVT3N8F+b3
# UUvM0ab8vQIGYheZSe2GGBMyMDIyMDQyMTEyMDMxOC43NzFaMASAAgH0oIHYpIHV
# MIHSMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQL
# EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsT
# HVRoYWxlcyBUU1MgRVNOOkQwODItNEJGRC1FRUJBMSUwIwYDVQQDExxNaWNyb3Nv
# ZnQgVGltZS1TdGFtcCBTZXJ2aWNloIIRZTCCBxQwggT8oAMCAQICEzMAAAGP81Go
# 61py3cwAAQAAAY8wDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# UENBIDIwMTAwHhcNMjExMDI4MTkyNzQ2WhcNMjMwMTI2MTkyNzQ2WjCB0jELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9z
# b2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMg
# VFNTIEVTTjpEMDgyLTRCRkQtRUVCQTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgU2VydmljZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJlX
# Pv61zxcehZOPgqS67mw6y02t0LgB0VV7svs7MV8JKIJE9Gvl1rgWm8B8qo/EUYmU
# EL3b2zquTURMTnh4mgrZFLENNhEgHvQs/paATbbHwqvOOrt6LVhwiZapLw60q+5j
# AasHEWO3H4QBny75aTEqI6AJ5O0Xo/o3CZ2MSHjd+Bd4fScADWN+tKBmAiEu6Szq
# LFlfm8boPbok2WBP13JcmDRel3c2f8w/+FOacU+DGUJswRrw7PvHA3QP7LWX4/68
# votF1GDRT4bqnPlCpMJv1zRwfgg7BkJfmUtBnG1FLp+FT04RyZupkQMC+cvM6bVh
# CKHG03kUk5mZ1GtomB9hDgVe3go8dEnW+pC3NcXRUXJA3kBkeCdchcsm7mbFD/Md
# HTrBBKC0LjobipQy0BIOw+mcZmSZ0vAdN3sJP0qVS6rG+ulNqsheAcA7dbmZIxGe
# 34pyKFIEs+Ae31i2CHjtjgmMSBNF78LFaKfT70102bRj885h1O+dxmqysrjOqGv6
# mk82L6wH1G+ymIb1UCsRlD5C/fniojOxtKnpyQha182T8EVqHHAEd9z4TRLrs8ym
# RSeA3mkwi4P/LitEOEIxUXn+Z+B/tikCBIm2e8yHgV944LKyAm880ptEF90kVZmR
# //wKqfGMZMHKCNVggYs7/OM/XqsEQXUOB2HDW0DDAgMBAAGjggE2MIIBMjAdBgNV
# HQ4EFgQU8wbmdGuuSc7ioc6Fm9uX+zcjcbwwHwYDVR0jBBgwFoAUn6cVXQBeYl2D
# 9OXSZacbUzUZ6XIwXwYDVR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3Nv
# ZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUy
# MDIwMTAoMSkuY3JsMGwGCCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1l
# LVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADATBgNVHSUE
# DDAKBggrBgEFBQcDCDANBgkqhkiG9w0BAQsFAAOCAgEAzX/TqPc8oQuZ9YwvIlOz
# SWN/RYs44cWcCWyWP2LcJ+t6ZTJU0mgaXq2p+eun7kaIxiUr9xMGbPka7jlNk/2U
# Q8eFR3rCx7XJRPBpjDNakzGmTy/CNha0Zn+TqKeBqnMTXTRAgQpVWZp9CsxXTzKk
# Wuf9EegpoKiYdJrryJopIB7m76IbGrzxMsh0GveBw+PyzSnf2CGgiij8/UgOXwGu
# KYUOBL89mrmPUlJbiHeTCvR+XI1lcAcQr2AA/tQlvc+xrISZTY6gb1rSjuidAHpn
# 4Os9vIO6nOxv7Qra5R+P8tu8vrLbsFlzb8HbEndZbweDOtwLjJfWKemrv1xZJxsy
# Txep/7lkeiYUL84eNCC4Hk4S5mIX/M/mg2+K9jgSxtT9gemgk1tmEJLe06VJ8yNH
# Chq9tdwmyrRpPzjiB0rAzsWrJnhifhYlCQoldecId2mU/1U/z5C/ROIQwQMBrePR
# vPIEgjtMfY33Q2VnVhmxC15UpgNxD+Hk2Ku0a6JWNOBvHxrRdKc7mbuNwNvc2iPZ
# SK+bpSkc/BKEB1OnLtD8VMNAfR/HAJL0MWjLpkWf+Hwm6jW+E3D5D3FjiNuEGJb6
# W7U/ad9X5WBJZnOcIxqZQJMv55CXE9B2RDV3p8VrT77eIHKKqaXiTwN0v9Q+eyi9
# +uu3qSK9MldvdBNqLWWwFvAwggdxMIIFWaADAgECAhMzAAAAFcXna54Cm0mZAAAA
# AAAVMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBB
# dXRob3JpdHkgMjAxMDAeFw0yMTA5MzAxODIyMjVaFw0zMDA5MzAxODMyMjVaMHwx
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1p
# Y3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0BAQEFAAOC
# Ag8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5vQ7VgtP97pwHB9KpbE51yMo1V/YB
# f2xK4OK9uT4XYDP/XE/HZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY6GB9alKD
# RLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhuje3XD9gmU3w5YQJ6xKr9cmmvHaus
# 9ja+NSZk2pg7uhp7M62AW36MEBydUv626GIl3GoPz130/o5Tz9bshVZN7928jaTj
# kY+yOSxRnOlwaQ3KNi1wjjHINSi947SHJMPgyY9+tVSP3PoFVZhtaDuaRr3tpK56
# KTesy+uDRedGbsoy1cCGMFxPLOJiss254o2I5JasAUq7vnGpF1tnYN74kpEeHT39
# IM9zfUGaRnXNxF803RKJ1v2lIH1+/NmeRd+2ci/bfV+AutuqfjbsNkz2K26oElHo
# vwUDo9Fzpk03dJQcNIIP8BDyt0cY7afomXw/TNuvXsLz1dhzPUNOwTM5TI4CvEJo
# LhDqhFFG4tG9ahhaYQFzymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZki1ugpoMh
# XV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y1BzFa/ZcUlFdEtsluq9QBXpsxREd
# cu+N+VLEhReTwDwV2xo3xwgVGD94q0W29R6HXtqPnhZyacaue7e3PmriLq0CAwEA
# AaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUCBBYEFCqn
# Uv5kxJq+gpE8RjUpzxD/LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJlpxtTNRnp
# cjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIBFjNodHRw
# Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0w
# EwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEw
# CwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/o
# olxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNy
# b3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYt
# MjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5t
# aWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5j
# cnQwDQYJKoZIhvcNAQELBQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/ypb+pcFLY+
# TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27DzHkwo/7bNGhlBgi7ulmZzpTTd2Y
# urYeeNg2LpypglYAA7AFvonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM9W0jVOR4
# U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9AkvUCgvxm2EhIRXT0n4ECWOKz3+SmJ
# w7wXsFSFQrP8DJ6LGYnn8AtqgcKBGUIZUnWKNsIdw2FzLixre24/LAl4FOmRsqlb
# 30mjdAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2kQH2zsZ0/fZMcm8Qq3UwxTSwethQ
# /gpY3UA8x1RtnWN0SCyxTkctwRQEcb9k+SS+c23Kjgm9swFXSVRk2XPXfx5bRAGO
# WhmRaw2fpCjcZxkoJLo4S5pu+yFUa2pFEUep8beuyOiJXk+d0tBMdrVXVAmxaQFE
# fnyhYWxz/gq77EFmPWn9y8FBSX5+k77L+DvktxW/tM4+pTFRhLy/AsGConsXHRWJ
# jXD+57XQKBqJC4822rpM+Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU5nR0W2rR
# nj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/2XBjU02N7oJtpQUQwXEGahC0HVUz
# WLOhcGbyoYIC1DCCAj0CAQEwggEAoYHYpIHVMIHSMQswCQYDVQQGEwJVUzETMBEG
# A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj
# cm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBP
# cGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkQwODIt
# NEJGRC1FRUJBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl
# oiMKAQEwBwYFKw4DAhoDFQA+TS+CBHbnSAcHRqAmldFgW0GaaqCBgzCBgKR+MHwx
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1p
# Y3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA5gs/
# EjAiGA8yMDIyMDQyMTEwMjUyMloYDzIwMjIwNDIyMTAyNTIyWjB0MDoGCisGAQQB
# hFkKBAExLDAqMAoCBQDmCz8SAgEAMAcCAQACAhARMAcCAQACAiEyMAoCBQDmDJCS
# AgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSCh
# CjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAFoPMAG9/+enNeuBPvAE0Ltgx
# 5sDL2v1UfW2pLhhVJJLLXKgCD5/SeFDxA6SKxzH1pZZ4dZ3QsOteJGJfyDzOhL32
# FJLhgwABlzRsIQan4xdI1EB3Fm1bm9/QKmYH7CPpsUaNY60QA9acXvsr1GkqEbs+
# RTIn9WY2JxQpi4yJU6cxggQNMIIECQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEG
# A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj
# cm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFt
# cCBQQ0EgMjAxMAITMwAAAY/zUajrWnLdzAABAAABjzANBglghkgBZQMEAgEFAKCC
# AUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCCH
# x5WoMGQBISCEbqi/GN31xcriODf6hCd3gfMh16ZRxTCB+gYLKoZIhvcNAQkQAi8x
# geowgecwgeQwgb0EIJdyBU/ixsYLlY4ipttiLZjunRQ1lvJ0obujuoPMhccfMIGY
# MIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV
# BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG
# A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAGP81Go61py
# 3cwAAQAAAY8wIgQghKBT29LWVp7vwz7FXNxn2VhAc/A+gh2sO1anrduJh2AwDQYJ
# KoZIhvcNAQELBQAEggIAYVS4PowlcbSk+9cx5mSrCgzLO9SgWsij7GWNPntI19p7
# Bv1ANRd32KpgYObVuOXGV8W0FxSUDlAHIycAe0/RnOms9w4vrlkKuu53sRff1A78
# loHeo7+eOA5Wk5qSqXdhNr0LhBg8Rmhzhw3CRDgNSedU31EJEm5NPiuDTfg7qfsG
# DzwAxww1Z2Rb6ZeIfz3IZ4KQJklP8XtjAt7EbSH0rNgNox3UU+mahVcZRX/DY0lN
# 1kHz4NsPqEGKQf3/ss2cm4RixnZoO4UqAKkOkMKAg1+soqWP5Ey6dQKoKAGiUYNb
# Rzh0B9sIqPJpkg8v0scyrIsj2dykt6HZ4bi1Sw+D1k4EzkQPXQXHJ9PbMFt2e0Ql
# W8tzgTitxmTWe/K4d21AJIn89FzdxPMMWtGDAkS8/Zqyp22hHQHBu2TFaPt2d5a9
# u4+4hggMXSPcvZ9L9wh5zLG5hUoZJjIWUxR2d/iqblhuBiopg1KIifj6dqVSi/yp
# Ee/6FehCDHEga0PTuRmZS+S8zUi4MJQJoYi5g/5sqcmaJ7ydEYW2JcLhfPzPv1Fz
# 6AOaS1KOcwOt5tsaSYLla9iedXslKWvCHjiL63Dg0u4cpiVulzZkx0V3tqSbtjje
# mTR27AZHE4bmIZrrYmlZOLcz9CElU/XVsOOOLZod+JIdxrA6uUCaP0+56PekAoA=
# SIG # End signature block