Public/PrivilegedAccess/Export-EntraOpsClassificationDirectoryRolesFromMsftDocs.ps1
|
function Export-EntraOpsClassificationDirectoryRolesFromMsftDocs { <# .SYNOPSIS Get a JSON file with all classified Entra ID Directory roles by parsing the public Microsoft Docs reference. .DESCRIPTION Parses the "Microsoft Entra built-in roles" permissions reference markdown from the MicrosoftDocs/entra-docs repository and all of its include pages. For every role it reads the role template ID and privileged label from the "All roles" table, the role description text below the metadata header of the include page, and the list of role actions from the include page table. The role actions are classified with the EntraOps classification (Classification_AadResources.json) in the same way as Export-EntraOpsClassificationDirectoryRoles.ps1 and the result is exported in the schema of Classification_EntraIdDirectoryRoles.json. This script does not require a connection to Microsoft Graph, it only needs internet access to raw.githubusercontent.com. .PARAMETER PermissionsReferenceUri Raw URI of the permissions-reference.md file. Default points to the main branch of MicrosoftDocs/entra-docs. .PARAMETER ClassificationFilePath Path to the EntraOps classification source file. Default is "./EntraOps_Classification/Classification_AadResources.json". .PARAMETER OutputFilePath Path of the JSON file to write. Default is "./Classification/Classification_EntraIdDirectoryRolesFromMsftDocs.json". .PARAMETER SingleClassification Use the highest tier level classification only for each role definition. Default is $True. .EXAMPLE Export all classified Entra ID Directory roles parsed from Microsoft Docs. Export-EntraOpsClassificationDirectoryRolesFromMsftDocs #> [cmdletbinding()] param ( [Parameter(Mandatory = $false)] [string]$PermissionsReferenceUri = "https://raw.githubusercontent.com/MicrosoftDocs/entra-docs/refs/heads/main/docs/identity/role-based-access-control/permissions-reference.md" , [Parameter(Mandatory = $false)] [string]$ClassificationFilePath = "./EntraOps_Classification/Classification_AadResources.json" , [Parameter(Mandatory = $false)] [string]$OutputFilePath = "./Classification/Classification_EntraIdDirectoryRolesFromMsftDocs.json" , [Parameter(Mandatory = $false)] $SingleClassification = $True ) # Define sensitive role definitions without actions to classify $ControlPlaneRolesWithoutRoleActions = @( 'd29b2b05-8046-44ba-8758-1e26182fcf32', # Directory Synchronization Accounts 'a92aed5d-d78a-4d16-b381-09adb37eb3b0', # On Premises Directory Sync Account '9f06204d-73c1-4d4c-880a-6edb90606fd8', # Azure AD Joined Device Local Administrator 'db506228-d27e-4b7d-95e5-295956d6615f' # Agent ID Administrator is sensitive but has no corresponding role action ) $ManagementPlaneRolesWithoutRoleActions = @( '3f04f91a-4ad7-4bd3-bcfa-49882ea1a88a', # Purview Workload Content Administrator 'e07494ad-1654-4dd2-922e-6f81a71bf00f', # Purview Workload Content Reader '02d5655b-c1cf-4e5f-98da-5fb919085bf6' # Purview Workload Content Writer ) #region Helper functions function Get-RawWebContent { param ([Parameter(Mandatory = $true)][string]$Uri) try { return (Invoke-WebRequest -Uri $Uri -UseBasicParsing -ErrorAction Stop).Content } catch { Write-Warning "Unable to download '$Uri': $($_.Exception.Message)" return $null } } #endregion # Get EntraOps Classification if (-not (Test-Path -Path $ClassificationFilePath)) { throw "Classification file '$ClassificationFilePath' not found." } $Classification = Get-Content -Path $ClassificationFilePath | ConvertFrom-Json -Depth 10 # Build base URI for the include pages (same folder as the reference markdown + /includes/) $BaseUri = $PermissionsReferenceUri.Substring(0, $PermissionsReferenceUri.LastIndexOf("/")) $IncludesBaseUri = "$BaseUri/includes" Write-Output "Downloading permissions reference markdown from $PermissionsReferenceUri" $ReferenceMarkdown = Get-RawWebContent -Uri $PermissionsReferenceUri if ([string]::IsNullOrEmpty($ReferenceMarkdown)) { throw "Unable to download the permissions reference markdown." } $ReferenceLines = $ReferenceMarkdown -split "`r?`n" #region Parse the "All roles" table to map role name, template ID and privileged flag Write-Output "Parsing the 'All roles' table for role template IDs and privileged labels" $RoleTableRegex = '^\s*>?\s*\|\s*\[(?<name>[^\]]+)\]\(#(?<anchor>[^\)]+)\)\s*\|\s*(?<desc>.*?)\s*\|\s*(?<id>[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})\s*\|\s*$' $RolesFromTable = @{} foreach ($Line in $ReferenceLines) { $Match = [regex]::Match($Line, $RoleTableRegex) if ($Match.Success) { $Anchor = $Match.Groups['anchor'].Value.Trim() $RolesFromTable[$Anchor] = [PSCustomObject]@{ RoleName = ($Match.Groups['name'].Value -replace '\s+', ' ').Trim() RoleId = $Match.Groups['id'].Value.Trim() Anchor = $Anchor isPrivileged = ($Match.Groups['desc'].Value -match 'Privileged label|privileged-roles-permissions') } } } Write-Output "Found $($RolesFromTable.Count) roles in the 'All roles' table" #endregion #region Parse INCLUDE references to map roles to their include pages # Example: [!INCLUDE [entra-backup-administrator](includes/entra-backup-administrator.md)] $IncludeRegex = '\[!INCLUDE\s*\[[^\]]+\]\(includes/(?<file>[^\)]+\.md)\)\]' $IncludeFiles = New-Object System.Collections.Generic.List[string] foreach ($Match in [regex]::Matches($ReferenceMarkdown, $IncludeRegex)) { $FileName = $Match.Groups['file'].Value.Trim() if (-not $IncludeFiles.Contains($FileName)) { $IncludeFiles.Add($FileName) | Out-Null } } Write-Output "Found $($IncludeFiles.Count) include pages referenced in the markdown" #endregion $DirectoryRoles = New-Object System.Collections.Generic.List[object] foreach ($IncludeFile in $IncludeFiles) { $Anchor = [System.IO.Path]::GetFileNameWithoutExtension($IncludeFile) # Only process roles that are listed with a template ID in the "All roles" table (built-in roles) if (-not $RolesFromTable.ContainsKey($Anchor)) { Write-Verbose "Skipping include page '$IncludeFile' because it has no template ID in the 'All roles' table" continue } $RoleMetadata = $RolesFromTable[$Anchor] $IncludeUri = "$IncludesBaseUri/$IncludeFile" Write-Output "Processing role '$($RoleMetadata.RoleName)' ($IncludeFile)" $IncludeContent = Get-RawWebContent -Uri $IncludeUri if ([string]::IsNullOrEmpty($IncludeContent)) { Write-Warning "Skipping role '$($RoleMetadata.RoleName)' because the include page could not be downloaded" continue } $IncludeLines = $IncludeContent -split "`r?`n" #region Extract the description (text below the metadata header, before the autogenerated content) $FrontmatterDelimiters = 0 $DescriptionLines = New-Object System.Collections.Generic.List[string] foreach ($IncludeLine in $IncludeLines) { $Trimmed = $IncludeLine.Trim() # Skip the YAML frontmatter delimited by --- if ($FrontmatterDelimiters -lt 2) { if ($Trimmed -eq '---') { $FrontmatterDelimiters++ } continue } # Stop when the autogenerated actions table starts if ($Trimmed -match 'autogenerated content starts here' -or $Trimmed -match '^>?\s*\[!div' -or $Trimmed -match '^>?\s*\|\s*Actions\s*\|') { break } $DescriptionLines.Add($IncludeLine) | Out-Null } $RichDescription = ($DescriptionLines -join "`n").Trim() #endregion #region Extract the role actions from the include page table $RolePermissionActions = New-Object System.Collections.Generic.List[string] foreach ($IncludeLine in $IncludeLines) { # Normalize blockquote prefix $TableLine = ($IncludeLine -replace '^\s*>\s?', '').Trim() if (-not $TableLine.StartsWith('|')) { continue } $Cells = $TableLine.Trim('|').Split('|') if ($Cells.Count -lt 1) { continue } $Action = $Cells[0].Trim() # Skip header and separator rows and anything that is not an actual action if ($Action -eq '' -or $Action -eq 'Actions' -or $Action -match '^[-: ]+$') { continue } if ($Action -notmatch '^[a-zA-Z0-9]+\.[a-zA-Z]') { continue } if (-not $RolePermissionActions.Contains($Action)) { $RolePermissionActions.Add($Action) | Out-Null } } #endregion #region Classify the role actions (same logic as Export-EntraOpsClassificationDirectoryRoles.ps1) $ClassifiedDirectoryRolePermissions = New-Object System.Collections.ArrayList foreach ($RolePermission in $RolePermissionActions) { $EntraRolePermissionTierLevelClassification = $Classification | Where-Object { $_.TierLevelDefinition.RoleDefinitionActions -contains $($RolePermission) } | Select-Object EAMTierLevelName, EAMTierLevelTagValue $EntraRolePermissionServiceClassification = $Classification | Select-Object -ExpandProperty TierLevelDefinition | Where-Object { $_.RoleDefinitionActions -contains $($RolePermission) } | Select-Object Service if ($EntraRolePermissionTierLevelClassification.Count -gt 1 -and $EntraRolePermissionServiceClassification.Count -gt 1) { Write-Warning "Multiple Tier Level Classification found for $($RolePermission)" } if ($null -eq $EntraRolePermissionTierLevelClassification) { $EntraRolePermissionTierLevelClassification = [PSCustomObject]@{ "EAMTierLevelName" = "Unclassified" "EAMTierLevelTagValue" = "Unclassified" } } if ($null -eq $EntraRolePermissionServiceClassification) { $EntraRolePermissionServiceClassification = [PSCustomObject]@{ "Service" = "Unclassified" } } $ClassifiedDirectoryRolePermission = ( [PSCustomObject]@{ "AuthorizedResourceAction" = $RolePermission "Category" = $EntraRolePermissionServiceClassification.Service "EAMTierLevelName" = $EntraRolePermissionTierLevelClassification.EAMTierLevelName "EAMTierLevelTagValue" = $EntraRolePermissionTierLevelClassification.EAMTierLevelTagValue } ) $ClassifiedDirectoryRolePermissions.Add($ClassifiedDirectoryRolePermission) | Out-Null } $ClassifiedDirectoryRolePermissions = $ClassifiedDirectoryRolePermissions | Sort-Object EAMTierLevelTagValue, Category, AuthorizedResourceAction if ($SingleClassification -eq $True) { $RoleDefinitionClassification = ($ClassifiedDirectoryRolePermissions | Select-Object -ExcludeProperty AuthorizedResourceAction, Category -Unique | Sort-Object EAMTierLevelTagValue | Select-Object -First 1) } else { $FilteredRoleClassifications = ($ClassifiedDirectoryRolePermissions | Select-Object -ExcludeProperty AuthorizedResourceAction -Unique | Sort-Object EAMTierLevelTagValue, Category) $RoleDefinitionClassification = [System.Collections.Generic.List[object]]::new() $RoleDefinitionClassification.Add($FilteredRoleClassifications) } if ($ControlPlaneRolesWithoutRoleActions -contains $RoleMetadata.RoleId) { $RoleDefinitionClassification = [PSCustomObject]@{ "EAMTierLevelName" = "ControlPlane" "EAMTierLevelTagValue" = "0" } } if ($ManagementPlaneRolesWithoutRoleActions -contains $RoleMetadata.RoleId) { $RoleDefinitionClassification = [PSCustomObject]@{ "EAMTierLevelName" = "ManagementPlane" "EAMTierLevelTagValue" = "1" } } if ($null -eq $RoleDefinitionClassification) { $RoleDefinitionClassification = [PSCustomObject]@{ "EAMTierLevelName" = "Unclassified" "EAMTierLevelTagValue" = "Unclassified" } } #endregion # Derive the role categories from the distinct service classifications of the role actions. # Match the Microsoft Graph representation: a scalar string with multiple values comma-delimited. $Categories = @($ClassifiedDirectoryRolePermissions | Select-Object -ExpandProperty Category -Unique | Where-Object { $_ -ne "Unclassified" } | Sort-Object) if ($Categories.Count -eq 0) { $Categories = "Unclassified" } else { $Categories = $Categories -join ',' } $DirectoryRoles.Add( [PSCustomObject]@{ "RoleId" = $RoleMetadata.RoleId "RoleName" = $RoleMetadata.RoleName "isPrivileged" = $RoleMetadata.isPrivileged "AssignmentMode" = $null "InheritsPermissionsFrom" = $null "Categories" = $Categories "RichDescription" = $RichDescription "RolePermissions" = @($ClassifiedDirectoryRolePermissions) "Classification" = $RoleDefinitionClassification } ) | Out-Null } $DirectoryRoles = $DirectoryRoles | Sort-Object RoleName, RoleId $OutputDirectory = Split-Path -Path $OutputFilePath -Parent if (-not [string]::IsNullOrEmpty($OutputDirectory) -and -not (Test-Path -Path $OutputDirectory)) { New-Item -Path $OutputDirectory -ItemType Directory -Force | Out-Null } Write-Output "Exporting $($DirectoryRoles.Count) classified directory roles to $OutputFilePath" $DirectoryRoles | ConvertTo-Json -Depth 10 | Out-File $OutputFilePath -Force } # SIG # Begin signature block # MIIniAYJKoZIhvcNAQcCoIIneTCCJ3UCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCID2zRtKohMZGM # mHfeYQziwuuASXgHyAWvfBBO9wJepaCCIDwwggXJMIIEsaADAgECAhAbtY8lKt8j # 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 # ARUwLwYJKoZIhvcNAQkEMSIEIM0plsF/WuDtm6x6cfr0zgorlJgVB5lOpBCsq5ut # NcJeMA0GCSqGSIb3DQEBAQUABIIBgJoj0bItgeKo0HkaEx+86N6LCYSPJwIOieZa # 04bx0MhVr+NvKweEnQ2ZUf27JDZGgWmMpWmIXSbF+Wp4tt/0OoBjfb9dYOonooQ4 # EFVT+mKbIhk/c9YO8n1cbyFUmwLrYea59qk5iSyATxHG3SD6CwY/q2Clge3uj/gO # K/9sYN/ZeDFcBvStedMIgOVrmmlwwXbc6vcaxsW7llL0JWff0i2lrEzyUhRtCjsr # UyDZ5TUeH4TET818rR9/SLUoOYl25h80bRjJZmhF3b5arAVVcwy4wQGMxfU90Fqk # rOrKfHoTcJ/rAs3TJPaljPBnIbo7e3C8kJ5wvW9UbcgMMLgEtZ3ML+9dt0kvrZ7i # zZW7wYaIdusftk8/9EYP7/0+z4prp5/i2BVntM3jlVOFsT4aJVl0yks9V2kvvfnG # NiLT8FxwqoCJcY4Yc7Qrdob2Kp3iLhxbMtDMz8NFCKkkmrA5R5l+B4TppFGG7wOZ # sVzuya5u2sN8a47qUurtb+cEbQANi6GCBAIwggP+BgkqhkiG9w0BCQYxggPvMIID # 6wIBATBqMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0 # ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBUaW1lc3RhbXBpbmcgMjAyMSBDQQIQ # KPB3wRw2vf5fdDJHcCcuAzANBglghkgBZQMEAgIFAKCCAVYwGgYJKoZIhvcNAQkD # MQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNjEwMTMwOTQxMDNaMDcG # CyqGSIb3DQEJEAIvMSgwJjAkMCIEIIW+kOEK0kONfMkotq9IsJqyCBd87PiwEmxY # 05EFJcQ8MD8GCSqGSIb3DQEJBDEyBDB/VcoSzRhEmqmZuHd8CIa9YJ93eXdEWDiU # ZFWbit9Xln41yEO8U4MyLv6Xs9FrXcMwgZ8GCyqGSIb3DQEJEAIMMYGPMIGMMIGJ # MIGGBBRXFGhBDKha80JO+RZKUTYQ9NONmDBuMFqkWDBWMQswCQYDVQQGEwJQTDEh # MB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMSQwIgYDVQQDExtDZXJ0 # dW0gVGltZXN0YW1waW5nIDIwMjEgQ0ECECjwd8EcNr3+X3QyR3AnLgMwDQYJKoZI # hvcNAQEBBQAEggIAtyjxa7AMin7d5/PyFhR9DHrGtZrzo65vmTWY2iDXtitbD4Sc # m6PbYsPLeO1qfVno8HrhJsD6qAOyxOjnCMvb7uNRBWXfFfSrWTV6bedWDfyG4J4I # /B0hj2uinyk7pMHs7ehPUXxPOUKKm41ySClAr1LZTyW9cZ6LW7MseAGGzjFJTyjW # th7zAVcgm3PP/daAjkEamMT5zv71ede3jaqzOzsQlSVLK9EcxgzP11O9hqo9CEVB # JrmmDvaL3iMLCJHhKK5k9JILyHP7RY4vqFz6VuY5Um08+1Vm4ygxwwDwUXvOwQyy # iWhjlRwu6yUmWzjOYP0jcLjeJ3MdxgWot6AO4Q5jXfzl200CRzd1FreHkEP/ThbH # 7POa3mkBHyZ4SSECIGWkYJA5G7UR98FV+KHISqacu6/5IBtyheBZTWJ9o7SsOWB3 # rFbYdyvm0XzY4mT6PzLe2GUHDuS9yWlnubyCSb97DFOm1A/zpnA/sj+4NB6PHp2F # 1tq+gVinRqX9AhIQcDAzr0L6Bma7yBtfE4MYxZHf3SltAQEnTZ400i06KdQXlxlu # k5woNmc0+SIjMUE9/yNh8Jtby6QdMadnwzl0jqVq3H+2Kh/+WrRC6eQ42Qaofrbg # OaQPGnKMxU1FoDBFxw3Ejd+feXmiRsu0UyQr3A2QsRU4L2xxWGunu52oImw= # SIG # End signature block |