Private/Get-AzureDependencies.ps1
|
function Get-AzureScopeParts { param([string]$Scope) $parts = [ordered]@{ scopeType = "Unknown" subscriptionId = $null resourceGroup = $null resourceId = $null } if ([string]::IsNullOrWhiteSpace($Scope)) { return [pscustomobject]$parts } if ($Scope -match "^/subscriptions/([^/]+)$") { $parts.scopeType = "Subscription" $parts.subscriptionId = $Matches[1] return [pscustomobject]$parts } if ($Scope -match "^/subscriptions/([^/]+)/resourceGroups/([^/]+)$") { $parts.scopeType = "ResourceGroup" $parts.subscriptionId = $Matches[1] $parts.resourceGroup = $Matches[2] return [pscustomobject]$parts } if ($Scope -match "^/subscriptions/([^/]+)/resourceGroups/([^/]+)/providers/.+$") { $parts.scopeType = "Resource" $parts.subscriptionId = $Matches[1] $parts.resourceGroup = $Matches[2] $parts.resourceId = $Scope return [pscustomobject]$parts } if ($Scope -match "^/providers/Microsoft\.Management/managementGroups/([^/]+)$") { $parts.scopeType = "ManagementGroup" } return [pscustomobject]$parts } function Get-AzureSubscriptionFilters { param([string]$SubscriptionIds) if ([string]::IsNullOrWhiteSpace($SubscriptionIds)) { $context = Get-AzContext if (-not $context -or -not $context.Subscription -or [string]::IsNullOrWhiteSpace([string]$context.Subscription.Id)) { throw "Azure context does not have a current subscription. Provide -SubscriptionIds or run Set-AzContext." } return @([string]$context.Subscription.Id) } return @($SubscriptionIds.Split(",") | ForEach-Object { $_.Trim() } | Where-Object { $_ }) } function Find-AzureResourceForScope { param( [string]$Scope, [hashtable]$ResourceById, [hashtable]$ResourceGroupByName, [object]$Subscription ) $scopeParts = Get-AzureScopeParts -Scope $Scope if ($scopeParts.scopeType -eq "Subscription") { return [pscustomobject]@{ dependencyType = "Subscription" resourceId = "/subscriptions/$($Subscription.Id)" resourceName = [string]$Subscription.Name resourceGroup = $null resourceType = "Microsoft.Resources/subscriptions" location = $null tags = $null } } if ($scopeParts.scopeType -eq "ResourceGroup") { $resourceGroupKey = [string]$scopeParts.resourceGroup $resourceGroup = $ResourceGroupByName[$resourceGroupKey] return [pscustomobject]@{ dependencyType = "ResourceGroup" resourceId = "/subscriptions/$($Subscription.Id)/resourceGroups/$resourceGroupKey" resourceName = $resourceGroupKey resourceGroup = $resourceGroupKey resourceType = "Microsoft.Resources/resourceGroups" location = if ($resourceGroup) { [string]$resourceGroup.Location } else { $null } tags = if ($resourceGroup) { $resourceGroup.Tags } else { $null } } } if ($scopeParts.scopeType -eq "Resource" -and $ResourceById.ContainsKey([string]$Scope)) { $resource = $ResourceById[[string]$Scope] return [pscustomobject]@{ dependencyType = "Resource" resourceId = [string]$resource.ResourceId resourceName = [string]$resource.Name resourceGroup = [string]$resource.ResourceGroupName resourceType = [string]$resource.ResourceType location = [string]$resource.Location tags = $resource.Tags } } return [pscustomobject]@{ dependencyType = $scopeParts.scopeType resourceId = [string]$Scope resourceName = $null resourceGroup = $scopeParts.resourceGroup resourceType = $null location = $null tags = $null } } function Test-ActivityLogMatchesServicePrincipal { param( [object]$Log, [object]$ServicePrincipal ) $objectId = [string]$ServicePrincipal.objectId $appId = [string]$ServicePrincipal.appId $displayName = [string]$ServicePrincipal.displayName $callerObjectId = [string]$Log.callerObjectId $callerAppId = [string]$Log.callerAppId $caller = [string]$Log.caller $callerName = [string]$Log.callerName if ($objectId -and $callerObjectId -and $callerObjectId.Equals($objectId, [System.StringComparison]::OrdinalIgnoreCase)) { return $true } if ($appId -and $callerAppId -and $callerAppId.Equals($appId, [System.StringComparison]::OrdinalIgnoreCase)) { return $true } if ($appId -and $caller -and $caller.Equals($appId, [System.StringComparison]::OrdinalIgnoreCase)) { return $true } if ($displayName -and $callerName -and $callerName.Equals($displayName, [System.StringComparison]::OrdinalIgnoreCase)) { return $true } return $false } function Test-AzureActivityLogMatchesScope { param( [object]$Log, [string]$Scope ) if ([string]::IsNullOrWhiteSpace($Scope)) { return $false } $normalizedScope = ([string]$Scope).TrimEnd("/") $candidateScopes = @( [string]$Log.authorizationScope, [string]$Log.resourceId ) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } foreach ($candidateScope in $candidateScopes) { $normalizedCandidateScope = ([string]$candidateScope).TrimEnd("/") if ($normalizedCandidateScope.Equals($normalizedScope, [System.StringComparison]::OrdinalIgnoreCase)) { return $true } if ($normalizedCandidateScope.StartsWith("$normalizedScope/", [System.StringComparison]::OrdinalIgnoreCase)) { return $true } } return $false } function New-AzureRbacScopeActivityEvidence { param( [object]$Subscription, [object]$Log, [object]$RoleAssignment, [object]$ServicePrincipal ) [pscustomobject]@{ subscriptionId = [string]$Subscription.Id subscriptionName = [string]$Subscription.Name rbacScope = [string]$RoleAssignment.Scope rbacRoleDefinitionName = [string]$RoleAssignment.RoleDefinitionName eventTimestamp = [string]$Log.eventTimestamp caller = [string]$Log.caller callerObjectId = [string]$Log.callerObjectId callerAppId = [string]$Log.callerAppId callerName = [string]$Log.callerName callerTenantId = [string]$Log.callerTenantId operationName = [string]$Log.operationName operationNameValue = [string]$Log.operationNameValue status = [string]$Log.status resourceGroupName = [string]$Log.resourceGroupName resourceId = [string]$Log.resourceId resourceType = [string]$Log.resourceType authorizationAction = [string]$Log.authorizationAction authorizationScope = [string]$Log.authorizationScope matchesInspectedServicePrincipal = [bool](Test-ActivityLogMatchesServicePrincipal -Log $Log -ServicePrincipal $ServicePrincipal) evidenceConfidence = "low" evidenceReason = "Activity logs show recent management-plane operations under an RBAC scope assigned to the inspected service principal; this is access context, not ownership proof." } } function Get-AzureRbacScopeActivityCallers { param( [object[]]$ActivityEvidence ) $groups = @($ActivityEvidence) | Group-Object -Property { $callerObjectId = [string]$_.callerObjectId $callerAppId = [string]$_.callerAppId $caller = [string]$_.caller $callerName = [string]$_.callerName if (-not [string]::IsNullOrWhiteSpace($callerObjectId)) { return "object:$callerObjectId" } if (-not [string]::IsNullOrWhiteSpace($callerAppId)) { return "app:$callerAppId" } if (-not [string]::IsNullOrWhiteSpace($caller)) { return "caller:$caller" } if (-not [string]::IsNullOrWhiteSpace($callerName)) { return "name:$callerName" } return "unknown" } foreach ($group in $groups) { $items = @($group.Group | Sort-Object eventTimestamp) $first = $items | Select-Object -First 1 $last = $items | Select-Object -Last 1 [pscustomobject]@{ callerKey = [string]$group.Name caller = [string]$last.caller callerObjectId = [string]$last.callerObjectId callerAppId = [string]$last.callerAppId callerName = [string]$last.callerName callerTenantId = [string]$last.callerTenantId eventCount = [int]$items.Count firstSeen = [string]$first.eventTimestamp lastSeen = [string]$last.eventTimestamp subscriptions = @($items | Select-Object -ExpandProperty subscriptionName -Unique) rbacScopes = @($items | Select-Object -ExpandProperty rbacScope -Unique) resourceIds = @($items | Select-Object -ExpandProperty resourceId -Unique | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) operationNames = @($items | Select-Object -ExpandProperty operationNameValue -Unique | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) matchesInspectedServicePrincipal = [bool](@($items | Where-Object matchesInspectedServicePrincipal).Count -gt 0) evidenceConfidence = "low" evidenceReason = "Caller performed recent management-plane operations under one or more RBAC scopes assigned to the inspected service principal." } } } function Get-AzureStorageDataReadServices { param([string]$RoleDefinitionName) switch -Regex ([string]$RoleDefinitionName) { "^Storage Blob Data (Reader|Contributor|Owner)$" { return @("Blob") } "^Storage Table Data (Reader|Contributor)$" { return @("Table") } "^Storage Queue Data (Reader|Contributor|Message Processor)$" { return @("Queue") } default { return @() } } } function Test-AzureStorageDataReadRole { param([string]$RoleDefinitionName) return @((Get-AzureStorageDataReadServices -RoleDefinitionName $RoleDefinitionName)).Count -gt 0 } function Get-AzureStorageDiagnosticServiceResourceId { param( [string]$StorageAccountResourceId, [string]$Service ) if ([string]::IsNullOrWhiteSpace($StorageAccountResourceId)) { return "" } switch ([string]$Service) { "Blob" { return "$($StorageAccountResourceId.TrimEnd("/"))/blobServices/default" } "Table" { return "$($StorageAccountResourceId.TrimEnd("/"))/tableServices/default" } "Queue" { return "$($StorageAccountResourceId.TrimEnd("/"))/queueServices/default" } default { return [string]$StorageAccountResourceId } } } function Test-AzureStorageDiagnosticLogEnabled { param( [object]$DiagnosticSetting, [string[]]$DataAccessCategories = @("StorageRead", "StorageWrite", "StorageDelete") ) $normalizedDataAccessCategories = @($DataAccessCategories | ForEach-Object { ([string]$_) -replace "[^A-Za-z0-9]", "" }) $logs = @( Get-ObjectProperty -Object $DiagnosticSetting -PropertyName "Logs" Get-ObjectProperty -Object $DiagnosticSetting -PropertyName "Log" Get-ObjectProperty -Object $DiagnosticSetting -PropertyName "EnabledLog" ) foreach ($log in $logs) { $enabled = [bool](Get-ObjectProperty -Object $log -PropertyName "Enabled") if (-not $enabled) { continue } $category = [string](Get-ObjectProperty -Object $log -PropertyName "Category") $categoryGroup = [string](Get-ObjectProperty -Object $log -PropertyName "CategoryGroup") $normalizedCategory = $category -replace "[^A-Za-z0-9]", "" if ( $DataAccessCategories -contains $category -or $normalizedDataAccessCategories -contains $normalizedCategory -or $categoryGroup.Equals("allLogs", [System.StringComparison]::OrdinalIgnoreCase) -or $categoryGroup.Equals("audit", [System.StringComparison]::OrdinalIgnoreCase) ) { return $true } } return $false } function ConvertTo-OwnerLensDiagnosticStatus { param( [object[]]$EnabledSettings, [object[]]$LogAnalyticsSettings, [object[]]$ExternalSettings ) if (@($LogAnalyticsSettings).Count -gt 0) { return "LogAnalytics" } if (@($ExternalSettings).Count -gt 0) { return "ExternalDestination" } if (@($EnabledSettings).Count -gt 0) { return "EnabledNoDestinationDetected" } return "NotConfigured" } function Get-OwnerLensDiagnosticSettingDestinationSummary { param( [Parameter(Mandatory = $true)] [object[]]$Settings, [Parameter(Mandatory = $true)] [scriptblock]$EnabledPredicate ) $enabledSettings = @($Settings | Where-Object $EnabledPredicate) $logAnalyticsSettings = @($enabledSettings | Where-Object { -not [string]::IsNullOrWhiteSpace([string](Get-ObjectProperty -Object $_ -PropertyName "WorkspaceId")) }) $externalSettings = @($enabledSettings | Where-Object { -not [string]::IsNullOrWhiteSpace([string](Get-ObjectProperty -Object $_ -PropertyName "StorageAccountId")) -or -not [string]::IsNullOrWhiteSpace([string](Get-ObjectProperty -Object $_ -PropertyName "EventHubAuthorizationRuleId")) -or -not [string]::IsNullOrWhiteSpace([string](Get-ObjectProperty -Object $_ -PropertyName "MarketplacePartnerId")) }) [pscustomobject]@{ enabledSettings = $enabledSettings logAnalyticsSettings = $logAnalyticsSettings externalSettings = $externalSettings status = ConvertTo-OwnerLensDiagnosticStatus ` -EnabledSettings $enabledSettings ` -LogAnalyticsSettings $logAnalyticsSettings ` -ExternalSettings $externalSettings diagnosticSettingNames = @($enabledSettings | ForEach-Object { [string](Get-ObjectProperty -Object $_ -PropertyName "Name") } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) workspaceIds = @($logAnalyticsSettings | ForEach-Object { [string](Get-ObjectProperty -Object $_ -PropertyName "WorkspaceId") } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Sort-Object -Unique) storageAccountIds = @($enabledSettings | ForEach-Object { [string](Get-ObjectProperty -Object $_ -PropertyName "StorageAccountId") } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Sort-Object -Unique) eventHubAuthorizationRuleIds = @($enabledSettings | ForEach-Object { [string](Get-ObjectProperty -Object $_ -PropertyName "EventHubAuthorizationRuleId") } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Sort-Object -Unique) marketplacePartnerIds = @($enabledSettings | ForEach-Object { [string](Get-ObjectProperty -Object $_ -PropertyName "MarketplacePartnerId") } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Sort-Object -Unique) } } function Get-AzureStorageDiagnosticSummary { param( [object]$StorageAccount, [string[]]$DataPlaneReadServices ) $storageAccountResourceId = [string]$StorageAccount.ResourceId $services = @($DataPlaneReadServices | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Sort-Object -Unique) $serviceRows = @() foreach ($service in $services) { $serviceResourceId = Get-AzureStorageDiagnosticServiceResourceId ` -StorageAccountResourceId $storageAccountResourceId ` -Service $service try { $settings = @(Get-AzDiagnosticSetting -ResourceId $serviceResourceId -ErrorAction Stop) $diagnosticSummary = Get-OwnerLensDiagnosticSettingDestinationSummary -Settings $settings -EnabledPredicate { Test-AzureStorageDiagnosticLogEnabled -DiagnosticSetting $_ } $serviceRows += [pscustomobject]@{ service = [string]$service resourceId = $serviceResourceId status = [string]$diagnosticSummary.status dataAccessLogEnabled = [bool](@($diagnosticSummary.enabledSettings).Count -gt 0) logAnalyticsEnabled = [bool](@($diagnosticSummary.logAnalyticsSettings).Count -gt 0) diagnosticSettingNames = @($diagnosticSummary.diagnosticSettingNames) workspaceIds = @($diagnosticSummary.workspaceIds) } } catch { $serviceRows += [pscustomobject]@{ service = [string]$service resourceId = $serviceResourceId status = "ReadFailed" dataAccessLogEnabled = $false logAnalyticsEnabled = $false diagnosticSettingNames = @() workspaceIds = @() error = [string]$_.Exception.Message } } } $logAnalyticsCount = @($serviceRows | Where-Object logAnalyticsEnabled).Count $loggedCount = @($serviceRows | Where-Object dataAccessLogEnabled).Count $failedCount = @($serviceRows | Where-Object status -eq "ReadFailed").Count [pscustomobject]@{ services = @($serviceRows) diagnosticLogEnabled = [bool]($loggedCount -gt 0) diagnosticLogAnalyticsEnabled = [bool]($logAnalyticsCount -gt 0) dataAccessVerificationStatus = if ($services.Count -eq 0) { "NoDataPlaneService" } elseif ($logAnalyticsCount -eq $services.Count) { "QueryableInLogAnalytics" } elseif ($logAnalyticsCount -gt 0) { "PartiallyQueryableInLogAnalytics" } elseif ($loggedCount -gt 0) { "ConfiguredOutsideLogAnalytics" } elseif ($failedCount -gt 0) { "DiagnosticSettingsReadFailed" } else { "NotConfigured" } dataAccessVerificationReason = if ($services.Count -eq 0) { "No storage data-plane service was derived from the assigned RBAC roles." } elseif ($logAnalyticsCount -eq $services.Count) { "Data-plane diagnostic logs are enabled to Log Analytics for every storage service covered by the inspected RBAC roles." } elseif ($logAnalyticsCount -gt 0) { "Data-plane diagnostic logs are enabled to Log Analytics for only some storage services covered by the inspected RBAC roles." } elseif ($loggedCount -gt 0) { "Data-plane diagnostic logs are enabled, but no Log Analytics destination was detected; requester review may require the configured external destination." } elseif ($failedCount -gt 0) { "Diagnostic settings could not be read for one or more storage services." } else { "No data-plane diagnostic logs were detected, so historical requester verification is not available from Azure Storage resource logs." } } } function Test-AzureActivityDiagnosticLogEnabled { param( [object]$DiagnosticSetting ) $logs = @( Get-ObjectProperty -Object $DiagnosticSetting -PropertyName "Logs" Get-ObjectProperty -Object $DiagnosticSetting -PropertyName "Log" Get-ObjectProperty -Object $DiagnosticSetting -PropertyName "EnabledLog" ) foreach ($log in $logs) { $enabled = [bool](Get-ObjectProperty -Object $log -PropertyName "Enabled") if (-not $enabled) { continue } $category = [string](Get-ObjectProperty -Object $log -PropertyName "Category") $categoryGroup = [string](Get-ObjectProperty -Object $log -PropertyName "CategoryGroup") if ( -not [string]::IsNullOrWhiteSpace($category) -or $categoryGroup.Equals("allLogs", [System.StringComparison]::OrdinalIgnoreCase) -or $categoryGroup.Equals("audit", [System.StringComparison]::OrdinalIgnoreCase) ) { return $true } } return $false } function Get-AzureActivityDiagnosticSummary { param( [object]$Subscription ) $subscriptionResourceId = "/subscriptions/$($Subscription.Id)" try { $settings = @(Get-AzDiagnosticSetting -ResourceId $subscriptionResourceId -ErrorAction Stop) $diagnosticSummary = Get-OwnerLensDiagnosticSettingDestinationSummary -Settings $settings -EnabledPredicate { Test-AzureActivityDiagnosticLogEnabled -DiagnosticSetting $_ } return [pscustomobject]@{ subscriptionId = [string]$Subscription.Id subscriptionName = [string]$Subscription.Name resourceId = $subscriptionResourceId status = [string]$diagnosticSummary.status activityLogEnabled = [bool](@($diagnosticSummary.enabledSettings).Count -gt 0) logAnalyticsEnabled = [bool](@($diagnosticSummary.logAnalyticsSettings).Count -gt 0) diagnosticSettingNames = @($diagnosticSummary.diagnosticSettingNames) workspaceIds = @($diagnosticSummary.workspaceIds) storageAccountIds = @($diagnosticSummary.storageAccountIds) eventHubAuthorizationRuleIds = @($diagnosticSummary.eventHubAuthorizationRuleIds) marketplacePartnerIds = @($diagnosticSummary.marketplacePartnerIds) } } catch { return [pscustomobject]@{ subscriptionId = [string]$Subscription.Id subscriptionName = [string]$Subscription.Name resourceId = $subscriptionResourceId status = "ReadFailed" activityLogEnabled = $false logAnalyticsEnabled = $false diagnosticSettingNames = @() workspaceIds = @() storageAccountIds = @() eventHubAuthorizationRuleIds = @() marketplacePartnerIds = @() error = [string]$_.Exception.Message } } } function Get-AzureDependencies { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [object]$ServicePrincipal, [string]$SubscriptionIds = "", [ValidateRange(1, 3650)] [int]$ActivityDays = 30, [ValidateRange(1, 1000000)] [int]$MaxActivityRecords = 5000, [string]$LogAnalyticsWorkspaceId = "", [ValidateRange(1, 1000000)] [int]$MaxBlobReadRecords = 5000, [string[]]$BlobReadOperationNames = @("GetBlob", "PutBlob", "PutBlock", "PutBlockList", "AppendBlock", "CopyBlob"), [switch]$SkipActivityLogs ) $subscriptionFilters = Get-AzureSubscriptionFilters -SubscriptionIds $SubscriptionIds $enabledSubscriptions = Get-AzSubscription | Where-Object State -eq "Enabled" $selectedSubscriptions = @() foreach ($filter in $subscriptionFilters) { $subscription = $enabledSubscriptions | Where-Object { $_.Id -eq $filter -or $_.Name -eq $filter } | Select-Object -First 1 if (-not $subscription) { throw "Subscription not found or not enabled: $filter" } if (-not ($selectedSubscriptions | Where-Object Id -eq $subscription.Id)) { $selectedSubscriptions += $subscription } } $roleAssignments = @() $coAssignedRoleCandidates = @() $resourceDependencies = @() $activityEvidence = @() $rbacScopeActivityEvidence = @() $blobReadEvidence = @() $activityDiagnosticSettings = @() $storageAccountsWithRbac = @{} $activityStartTime = (Get-Date).AddDays(-$ActivityDays) $servicePrincipalObjectId = [string]$ServicePrincipal.objectId $candidateRoleAssignmentIds = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) $queriedCoAssignedScopes = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) $originalContext = Get-AzContext foreach ($subscription in $selectedSubscriptions) { try { Set-AzContext -SubscriptionId $subscription.Id -ErrorAction Stop | Out-Null $activityDiagnosticSettings += Get-AzureActivityDiagnosticSummary -Subscription $subscription $resources = @(Get-AzResource) $resourceGroups = @(Get-AzResourceGroup) $resourceById = @{} foreach ($resource in $resources) { if (-not [string]::IsNullOrWhiteSpace([string]$resource.ResourceId)) { $resourceById[[string]$resource.ResourceId] = $resource } } $resourceGroupByName = @{} foreach ($resourceGroup in $resourceGroups) { if (-not [string]::IsNullOrWhiteSpace([string]$resourceGroup.ResourceGroupName)) { $resourceGroupByName[[string]$resourceGroup.ResourceGroupName] = $resourceGroup } } $assignments = @(Get-AzRoleAssignment -ObjectId $servicePrincipalObjectId -ErrorAction Stop) foreach ($assignment in $assignments) { $roleAssignments += [pscustomobject]@{ subscriptionId = [string]$subscription.Id subscriptionName = [string]$subscription.Name roleAssignmentId = [string]$assignment.RoleAssignmentId scope = [string]$assignment.Scope principalId = [string]$assignment.ObjectId principalType = [string]$assignment.ObjectType principalDisplayName = [string]$assignment.DisplayName roleDefinitionId = [string]$assignment.RoleDefinitionId roleDefinitionName = [string]$assignment.RoleDefinitionName isStorageDataReadRole = Test-AzureStorageDataReadRole -RoleDefinitionName ([string]$assignment.RoleDefinitionName) condition = [string]$assignment.Condition conditionVersion = [string]$assignment.ConditionVersion } $resourceDependencies += Find-AzureResourceForScope ` -Scope ([string]$assignment.Scope) ` -ResourceById $resourceById ` -ResourceGroupByName $resourceGroupByName ` -Subscription $subscription $dataReadServices = @(Get-AzureStorageDataReadServices -RoleDefinitionName ([string]$assignment.RoleDefinitionName)) if ($dataReadServices.Count -gt 0) { foreach ($storageAccount in @(Get-AzureStorageAccountsForScope -Scope ([string]$assignment.Scope) -Resources $resources)) { $storageAccountResourceId = [string]$storageAccount.ResourceId if ([string]::IsNullOrWhiteSpace($storageAccountResourceId)) { continue } if (-not $storageAccountsWithRbac.ContainsKey($storageAccountResourceId)) { $storageAccountsWithRbac[$storageAccountResourceId] = [ordered]@{ resource = $storageAccount dataPlaneReadServices = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) dataPlaneReadRoleNames = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) rbacScopes = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) } } foreach ($service in $dataReadServices) { $storageAccountsWithRbac[$storageAccountResourceId].dataPlaneReadServices.Add([string]$service) | Out-Null } $storageAccountsWithRbac[$storageAccountResourceId].dataPlaneReadRoleNames.Add([string]$assignment.RoleDefinitionName) | Out-Null $storageAccountsWithRbac[$storageAccountResourceId].rbacScopes.Add([string]$assignment.Scope) | Out-Null } } $assignmentScope = [string]$assignment.Scope if (-not $queriedCoAssignedScopes.Add($assignmentScope)) { continue } $sameScopeAssignments = @(Get-AzRoleAssignment -Scope $assignmentScope -ErrorAction Stop) foreach ($sameScopeAssignment in $sameScopeAssignments) { if (-not ([string]$sameScopeAssignment.Scope).Equals([string]$assignment.Scope, [System.StringComparison]::OrdinalIgnoreCase)) { continue } $candidatePrincipalId = [string]$sameScopeAssignment.ObjectId if ([string]::IsNullOrWhiteSpace($candidatePrincipalId)) { continue } if ($candidatePrincipalId.Equals($servicePrincipalObjectId, [System.StringComparison]::OrdinalIgnoreCase)) { continue } $candidateRoleAssignmentId = [string]$sameScopeAssignment.RoleAssignmentId if (-not [string]::IsNullOrWhiteSpace($candidateRoleAssignmentId)) { if (-not $candidateRoleAssignmentIds.Add($candidateRoleAssignmentId)) { continue } } $coAssignedRoleCandidates += [pscustomobject]@{ subscriptionId = [string]$subscription.Id subscriptionName = [string]$subscription.Name roleAssignmentId = $candidateRoleAssignmentId scope = [string]$sameScopeAssignment.Scope principalId = $candidatePrincipalId principalType = [string]$sameScopeAssignment.ObjectType principalName = [string]$sameScopeAssignment.SignInName principalDisplayName = [string]$sameScopeAssignment.DisplayName roleDefinitionId = [string]$sameScopeAssignment.RoleDefinitionId roleDefinitionName = [string]$sameScopeAssignment.RoleDefinitionName isStorageDataReadRole = Test-AzureStorageDataReadRole -RoleDefinitionName ([string]$sameScopeAssignment.RoleDefinitionName) } } } if (-not $SkipActivityLogs) { $logs = Get-AzureActivityLogs ` -SubscriptionId ([string]$subscription.Id) ` -StartTime $activityStartTime ` -MaxRecord $MaxActivityRecords foreach ($log in @($logs)) { foreach ($assignment in $assignments) { if (Test-AzureActivityLogMatchesScope -Log $log -Scope ([string]$assignment.Scope)) { $rbacScopeActivityEvidence += New-AzureRbacScopeActivityEvidence ` -Subscription $subscription ` -Log $log ` -RoleAssignment $assignment ` -ServicePrincipal $ServicePrincipal } } if (Test-ActivityLogMatchesServicePrincipal -Log $log -ServicePrincipal $ServicePrincipal) { $activityEvidence += [pscustomobject]@{ subscriptionId = [string]$subscription.Id subscriptionName = [string]$subscription.Name eventTimestamp = [string]$log.eventTimestamp caller = [string]$log.caller callerObjectId = [string]$log.callerObjectId callerAppId = [string]$log.callerAppId callerName = [string]$log.callerName operationName = [string]$log.operationName operationNameValue = [string]$log.operationNameValue status = [string]$log.status resourceGroupName = [string]$log.resourceGroupName resourceId = [string]$log.resourceId resourceType = [string]$log.resourceType authorizationAction = [string]$log.authorizationAction authorizationScope = [string]$log.authorizationScope evidenceConfidence = "low" evidenceReason = "Activity logs show recent use by this service principal identifier; this is access evidence, not ownership proof." } } } } } finally { if ($originalContext) { try { Set-AzContext -Context $originalContext -ErrorAction Stop | Out-Null } catch { Write-Warning "Failed to restore the original Azure context after OwnerLens collection." } } } } if (-not [string]::IsNullOrWhiteSpace($LogAnalyticsWorkspaceId)) { $blobReadEvidence = @(Get-StorageBlobReadLogs ` -WorkspaceId $LogAnalyticsWorkspaceId ` -StorageAccounts @($storageAccountsWithRbac.Values) ` -ServicePrincipal $ServicePrincipal ` -StartTime $activityStartTime ` -MaxRecord $MaxBlobReadRecords ` -BlobReadOperationNames $BlobReadOperationNames) } return [pscustomobject]@{ requestedSubscriptions = @($subscriptionFilters) subscriptions = @($selectedSubscriptions | ForEach-Object { [pscustomobject]@{ subscriptionId = [string]$_.Id subscriptionName = [string]$_.Name tenantId = [string]$_.TenantId } }) roleAssignments = @($roleAssignments | Sort-Object subscriptionName, scope, roleDefinitionName) coAssignedRoleCandidates = @($coAssignedRoleCandidates | Sort-Object subscriptionName, scope, principalDisplayName, roleDefinitionName) resourceDependencies = @($resourceDependencies | Sort-Object resourceId -Unique | Sort-Object dependencyType, resourceGroup, resourceName) activityEvidence = @($activityEvidence | Sort-Object eventTimestamp, resourceId) rbacScopeActivityEvidence = @($rbacScopeActivityEvidence | Sort-Object eventTimestamp, rbacScope, resourceId) rbacScopeActivityCallers = @(Get-AzureRbacScopeActivityCallers -ActivityEvidence @($rbacScopeActivityEvidence) | Sort-Object @{ Expression = "eventCount"; Descending = $true }, lastSeen) activityDiagnosticSettings = @($activityDiagnosticSettings | Sort-Object subscriptionName, resourceId) blobReadEvidence = @($blobReadEvidence | Sort-Object eventTimestamp, storageAccountName, uri) blobReadCallers = @(Get-StorageBlobReadCallers -BlobReadEvidence @($blobReadEvidence) | Sort-Object @{ Expression = "blobAccessCount"; Descending = $true }, lastSeen) blobReadObjects = @(Get-StorageBlobReadObjects -BlobReadEvidence @($blobReadEvidence) | Sort-Object @{ Expression = "blobReadCount"; Descending = $true }, lastReadAt) storageAccountsWithRbac = @($storageAccountsWithRbac.Values | ForEach-Object { $storageAccount = $_.resource $diagnosticSummary = Get-AzureStorageDiagnosticSummary ` -StorageAccount $storageAccount ` -DataPlaneReadServices @($_.dataPlaneReadServices) [pscustomobject]@{ resourceId = [string]$storageAccount.ResourceId name = [string]$storageAccount.Name resourceGroup = [string]$storageAccount.ResourceGroupName location = [string]$storageAccount.Location dataPlaneReadServices = @($_.dataPlaneReadServices | Sort-Object) dataPlaneReadRoleNames = @($_.dataPlaneReadRoleNames | Sort-Object) rbacScopes = @($_.rbacScopes | Sort-Object) diagnosticSettings = @($diagnosticSummary.services) diagnosticLogEnabled = [bool]$diagnosticSummary.diagnosticLogEnabled diagnosticLogAnalyticsEnabled = [bool]$diagnosticSummary.diagnosticLogAnalyticsEnabled dataAccessVerificationStatus = [string]$diagnosticSummary.dataAccessVerificationStatus dataAccessVerificationReason = [string]$diagnosticSummary.dataAccessVerificationReason } } | Sort-Object name) logAnalyticsWorkspaceId = [string]$LogAnalyticsWorkspaceId maxBlobReadRecords = $MaxBlobReadRecords activityStartTime = $activityStartTime.ToUniversalTime().ToString("o") maxActivityRecords = $MaxActivityRecords activitySkipped = [bool]$SkipActivityLogs } } # SIG # Begin signature block # MII9MQYJKoZIhvcNAQcCoII9IjCCPR4CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCZA+z0inBM9GDD # akozxaj3nrKi44LIujIQf6GSa3E4xqCCIfYwggXMMIIDtKADAgECAhBUmNLR1FsZ # lUgTecgRwIeZMA0GCSqGSIb3DQEBDAUAMHcxCzAJBgNVBAYTAlVTMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jvc29mdCBJZGVu # dGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAy # MDAeFw0yMDA0MTYxODM2MTZaFw00NTA0MTYxODQ0NDBaMHcxCzAJBgNVBAYTAlVT # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jv # c29mdCBJZGVudGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRo # b3JpdHkgMjAyMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALORKgeD # Bmf9np3gx8C3pOZCBH8Ppttf+9Va10Wg+3cL8IDzpm1aTXlT2KCGhFdFIMeiVPvH # or+Kx24186IVxC9O40qFlkkN/76Z2BT2vCcH7kKbK/ULkgbk/WkTZaiRcvKYhOuD # PQ7k13ESSCHLDe32R0m3m/nJxxe2hE//uKya13NnSYXjhr03QNAlhtTetcJtYmrV # qXi8LW9J+eVsFBT9FMfTZRY33stuvF4pjf1imxUs1gXmuYkyM6Nix9fWUmcIxC70 # ViueC4fM7Ke0pqrrBc0ZV6U6CwQnHJFnni1iLS8evtrAIMsEGcoz+4m+mOJyoHI1 # vnnhnINv5G0Xb5DzPQCGdTiO0OBJmrvb0/gwytVXiGhNctO/bX9x2P29Da6SZEi3 # W295JrXNm5UhhNHvDzI9e1eM80UHTHzgXhgONXaLbZ7LNnSrBfjgc10yVpRnlyUK # xjU9lJfnwUSLgP3B+PR0GeUw9gb7IVc+BhyLaxWGJ0l7gpPKWeh1R+g/OPTHU3mg # trTiXFHvvV84wRPmeAyVWi7FQFkozA8kwOy6CXcjmTimthzax7ogttc32H83rwjj # O3HbbnMbfZlysOSGM1l0tRYAe1BtxoYT2v3EOYI9JACaYNq6lMAFUSw0rFCZE4e7 # swWAsk0wAly4JoNdtGNz764jlU9gKL431VulAgMBAAGjVDBSMA4GA1UdDwEB/wQE # AwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTIftJqhSobyhmYBAcnz1AQ # T2ioojAQBgkrBgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQwFAAOCAgEAr2rd5hnn # LZRDGU7L6VCVZKUDkQKL4jaAOxWiUsIWGbZqWl10QzD0m/9gdAmxIR6QFm3FJI9c # Zohj9E/MffISTEAQiwGf2qnIrvKVG8+dBetJPnSgaFvlVixlHIJ+U9pW2UYXeZJF # xBA2CFIpF8svpvJ+1Gkkih6PsHMNzBxKq7Kq7aeRYwFkIqgyuH4yKLNncy2RtNwx # AQv3Rwqm8ddK7VZgxCwIo3tAsLx0J1KH1r6I3TeKiW5niB31yV2g/rarOoDXGpc8 # FzYiQR6sTdWD5jw4vU8w6VSp07YEwzJ2YbuwGMUrGLPAgNW3lbBeUU0i/OxYqujY # lLSlLu2S3ucYfCFX3VVj979tzR/SpncocMfiWzpbCNJbTsgAlrPhgzavhgplXHT2 # 6ux6anSg8Evu75SjrFDyh+3XOjCDyft9V77l4/hByuVkrrOj7FjshZrM77nq81YY # uVxzmq/FdxeDWds3GhhyVKVB0rYjdaNDmuV3fJZ5t0GNv+zcgKCf0Xd1WF81E+Al # GmcLfc4l+gcK5GEh2NQc5QfGNpn0ltDGFf5Ozdeui53bFv0ExpK91IjmqaOqu/dk # ODtfzAzQNb50GQOmxapMomE2gj4d8yu8l13bS3g7LfU772Aj6PXsCyM2la+YZr9T # 03u4aUoqlmZpxJTG9F9urJh4iIAGXKKy7aIwggaoMIIEkKADAgECAhMzAATfN8Fd # CkCAOd/lAAAABN83MA0GCSqGSIb3DQEBDAUAMFoxCzAJBgNVBAYTAlVTMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29mdCBJ # RCBWZXJpZmllZCBDUyBFT0MgQ0EgMDQwHhcNMjYwODI0MDgwNzU1WhcNMjYwODI3 # MDgwNzU1WjBpMQswCQYDVQQGEwJQTDEXMBUGA1UECB4OAUEA8wBkAHoAawBpAGUx # DzANBgNVBAcTBldpZWx1bjEXMBUGA1UEChMOc29mdDRwbSBzcCB6b28xFzAVBgNV # BAMTDnNvZnQ0cG0gc3Agem9vMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKC # AYEAsUB/A4IgTtKleM7nBJSDDNsBT3BEKv/mr4dnsVBvBZkNkBifW3Ofxb4609Vg # fPIXAJpmhke5LoS085d62kfncFq3L5lyEk+UZyKlU0XGOToUkyIwXJ9ZMRcZzHUq # VuzSEgf2niOdtZLGbAJA3j27caB15YiJXNXmcdpFtNOTdpFkNnlVnBq+IzAWHD43 # XXp/E/NuEP+yQbtANzSHY0QOeOaSQEzPIm/JGmbXtYSrStuGTKJg6WM5VIESIhZe # jfpiuTgde/pKVy7tPktOoqzyhALoiplucnn32fGJxHWYX3AvApukJViJobPPHUrh # mQOIK5Vvcq0ED3eNR2L3oOTzJdZcM6K1LhEadLpniex5z6xi4ff7NTisGdyJa3RA # 0xxCxwXjreDHnFsEPExMdQjX625GOj9M5KbbxaPGWnRps4cLrU2gtqH8wcF3PxSE # gab5oKyXlwE5ymXwh0OeIqgH4ahmVts+Q6EnSzUOhh/Y33Q0voBDrJH4iSzKZiAO # MMcbAgMBAAGjggHWMIIB0jAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA9 # BgNVHSUENjA0BgorBgEEAYI3YQEABggrBgEFBQcDAwYcKwYBBAGCN2GCycz3dYGA # 0Y0Tg469piSB0sq0aDAdBgNVHQ4EFgQUad/TRiMm/CKCInTGHVoP2MIOwzAwHwYD # VR0jBBgwFoAUmvFUd3UMhxY3RqCs3nn59H/BeOkwZwYDVR0fBGAwXjBcoFqgWIZW # aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIw # SUQlMjBWZXJpZmllZCUyMENTJTIwRU9DJTIwQ0ElMjAwNC5jcmwwdAYIKwYBBQUH # AQEEaDBmMGQGCCsGAQUFBzAChlhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp # b3BzL2NlcnRzL01pY3Jvc29mdCUyMElEJTIwVmVyaWZpZWQlMjBDUyUyMEVPQyUy # MENBJTIwMDQuY3J0MFQGA1UdIARNMEswSQYEVR0gADBBMD8GCCsGAQUFBwIBFjNo # dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5o # dG0wDQYJKoZIhvcNAQEMBQADggIBAF8p+KjsdCBVGNRuYw0+ZJbOhVz3n1vXUnbE # CHXjQOq8+j4p2dvA+5NdPKwbSiIcnwzQ26B1TOLqIT5lIwlsoFnXpopgIduf5Tc5 # P49mv+hNL2F1Wg3yJPPo59SMvUa08uX3k2KItoOiUpmpo1/BoPfasb6MJgTu235m # Z3MHCp1gXSqTgNSIyJR0U1Lf1+6IGlvmOHyRHFAz3y1ttrQe/BUGEF6WHFXsEbkU # KVhnYTk+e/G6wpiEHcGyYyZ+HW67jM9Eb9J7EeHt/NptwoHAL9tAEfP0xDlbWgMe # 6U9DyBeYqeGSCqoUHi2MD77y1g2yoG44SnzQCWHmrQVViBcdVyjU2TpmVI/9+hhk # X6wnlaSd8qU2ha/cmUmKevNr9wspd1H0yYeBwj+3oMSol8rcsa5p0w3rb1iHmzG/ # aSrA/E+ghWy4wXGayJ3jdpxjO+mCk6Qd7jEmUsh+pRd6w0zbE7CgznlYyLV5qfPa # eCtD1yXjpQIpa3E7srlMhZd8MFcLCmdKajZNMO83tTXMqnhOEnxxB6dHJ+diDdrG # 3egnxVUSLJRezkNLBzYwpw3FvJa7E7WpM/lSzi7MGp/Nr+KIsGwQ3M04gJIc6pU1 # n8wj85D9ACJnWKwkiPl647BeI94Ttu5SUXi+G+9588W8rlTvSyq0df0GeGdrsuZT # Y/mNoLE+MIIGqDCCBJCgAwIBAgITMwAE3zfBXQpAgDnf5QAAAATfNzANBgkqhkiG # 9w0BAQwFADBaMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSswKQYDVQQDEyJNaWNyb3NvZnQgSUQgVmVyaWZpZWQgQ1MgRU9DIENB # IDA0MB4XDTI2MDgyNDA4MDc1NVoXDTI2MDgyNzA4MDc1NVowaTELMAkGA1UEBhMC # UEwxFzAVBgNVBAgeDgFBAPMAZAB6AGsAaQBlMQ8wDQYDVQQHEwZXaWVsdW4xFzAV # BgNVBAoTDnNvZnQ0cG0gc3Agem9vMRcwFQYDVQQDEw5zb2Z0NHBtIHNwIHpvbzCC # AaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBALFAfwOCIE7SpXjO5wSUgwzb # AU9wRCr/5q+HZ7FQbwWZDZAYn1tzn8W+OtPVYHzyFwCaZoZHuS6EtPOXetpH53Ba # ty+ZchJPlGcipVNFxjk6FJMiMFyfWTEXGcx1Klbs0hIH9p4jnbWSxmwCQN49u3Gg # deWIiVzV5nHaRbTTk3aRZDZ5VZwaviMwFhw+N116fxPzbhD/skG7QDc0h2NEDnjm # kkBMzyJvyRpm17WEq0rbhkyiYOljOVSBEiIWXo36Yrk4HXv6Slcu7T5LTqKs8oQC # 6IqZbnJ599nxicR1mF9wLwKbpCVYiaGzzx1K4ZkDiCuVb3KtBA93jUdi96Dk8yXW # XDOitS4RGnS6Z4nsec+sYuH3+zU4rBnciWt0QNMcQscF463gx5xbBDxMTHUI1+tu # Rjo/TOSm28Wjxlp0abOHC61NoLah/MHBdz8UhIGm+aCsl5cBOcpl8IdDniKoB+Go # ZlbbPkOhJ0s1DoYf2N90NL6AQ6yR+IksymYgDjDHGwIDAQABo4IB1jCCAdIwDAYD # VR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwPQYDVR0lBDYwNAYKKwYBBAGCN2EB # AAYIKwYBBQUHAwMGHCsGAQQBgjdhgsnM93WBgNGNE4OOvaYkgdLKtGgwHQYDVR0O # BBYEFGnf00YjJvwigiJ0xh1aD9jCDsMwMB8GA1UdIwQYMBaAFJrxVHd1DIcWN0ag # rN55+fR/wXjpMGcGA1UdHwRgMF4wXKBaoFiGVmh0dHA6Ly93d3cubWljcm9zb2Z0 # LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMElEJTIwVmVyaWZpZWQlMjBDUyUy # MEVPQyUyMENBJTIwMDQuY3JsMHQGCCsGAQUFBwEBBGgwZjBkBggrBgEFBQcwAoZY # aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQl # MjBJRCUyMFZlcmlmaWVkJTIwQ1MlMjBFT0MlMjBDQSUyMDA0LmNydDBUBgNVHSAE # TTBLMEkGBFUdIAAwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMA0GCSqGSIb3DQEBDAUAA4IC # AQBfKfio7HQgVRjUbmMNPmSWzoVc959b11J2xAh140DqvPo+KdnbwPuTXTysG0oi # HJ8M0NugdUzi6iE+ZSMJbKBZ16aKYCHbn+U3OT+PZr/oTS9hdVoN8iTz6OfUjL1G # tPLl95NiiLaDolKZqaNfwaD32rG+jCYE7tt+ZmdzBwqdYF0qk4DUiMiUdFNS39fu # iBpb5jh8kRxQM98tbba0HvwVBhBelhxV7BG5FClYZ2E5PnvxusKYhB3BsmMmfh1u # u4zPRG/SexHh7fzabcKBwC/bQBHz9MQ5W1oDHulPQ8gXmKnhkgqqFB4tjA++8tYN # sqBuOEp80Alh5q0FVYgXHVco1Nk6ZlSP/foYZF+sJ5WknfKlNoWv3JlJinrza/cL # KXdR9MmHgcI/t6DEqJfK3LGuadMN629Yh5sxv2kqwPxPoIVsuMFxmsid43acYzvp # gpOkHe4xJlLIfqUXesNM2xOwoM55WMi1eanz2ngrQ9cl46UCKWtxO7K5TIWXfDBX # CwpnSmo2TTDvN7U1zKp4ThJ8cQenRyfnYg3axt3oJ8VVEiyUXs5DSwc2MKcNxbyW # uxO1qTP5Us4uzBqfza/iiLBsENzNOICSHOqVNZ/MI/OQ/QAiZ1isJIj5euOwXiPe # E7buUlF4vhvvefPFvK5U70sqtHX9Bnhna7LmU2P5jaCxPjCCBygwggUQoAMCAQIC # EzMAAAAXJ0UJC4uHr8YAAAAAABcwDQYJKoZIhvcNAQEMBQAwYzELMAkGA1UEBhMC # VVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjE0MDIGA1UEAxMrTWlj # cm9zb2Z0IElEIFZlcmlmaWVkIENvZGUgU2lnbmluZyBQQ0EgMjAyMTAeFw0yNjAz # MjYxODExMzFaFw0zMTAzMjYxODExMzFaMFoxCzAJBgNVBAYTAlVTMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29mdCBJRCBW # ZXJpZmllZCBDUyBFT0MgQ0EgMDQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK # AoICAQCCx2T+Aw9mKgGVzJ+Tq0PMn49G3itIsYpbx7ClLSRHFe1RELdPcZ1sIqWO # hsSfy6yyqEapClGH9Je9FXA1cQgZvvpQbkg+QInVLr/0EPrVBCwrM96lbRI2PxNe # CwXG9LsyW2hG6KQgintDmNCBo4zpDIr377plVdSliZm6UB7rHwmvBnR02QT6tnrq # Wq2ihzB6lRJVTEzuh0OafzIMeMnYM0+x+ve5EOLHdfiq+HXiMf9Jb7YLHtYgyHIi # JA7bTWLqFSLGaTh7ZlbxbsLXA91OOroEpv7OjzFuu3tkpC9FflA4Dp2Euq4+qPmx # UqfGp+TX0gLRJp9NJOzzILjcTD3rkFFFbxUv1xyg6avivFDLtoKBhM2Td138umE1 # pNOacanuSYtPHIeQHmB6haFi64avLBLwTTAm/Rbit860cFXR72wq+5Qh4hSmezHq # KXERWPpVBe+APrJ4Iqc+aPeMmIkoCWZQO22HnLNFUFSXjiwyIbgvlH/LIAJEqTaf # TzxDZgKhlLU7zr6gwsq3WNpcYQI6NuxWnwh3VVDDyF7onQqKs5Ll7bleVN0Y8Vvq # gE45ppyBbvwqN/Run5fMCCRz3aYMY0kZhKO92eP7t4zHqZ5bQMAgZ0tE2Pz/jb0w # iykUF/PcoOqqk3vVLiRDYst6vd3GEMNzMpUUvQcvBG46+COIbwIDAQABo4IB3DCC # AdgwDgYDVR0PAQH/BAQDAgGGMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBSa # 8VR3dQyHFjdGoKzeefn0f8F46TBUBgNVHSAETTBLMEkGBFUdIAAwQTA/BggrBgEF # BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9z # aXRvcnkuaHRtMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMBIGA1UdEwEB/wQI # MAYBAf8CAQAwHwYDVR0jBBgwFoAU2UEpsA8PY2zvadf1zSmepEhqMOYwcAYDVR0f # BGkwZzBloGOgYYZfaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwv # TWljcm9zb2Z0JTIwSUQlMjBWZXJpZmllZCUyMENvZGUlMjBTaWduaW5nJTIwUENB # JTIwMjAyMS5jcmwwfQYIKwYBBQUHAQEEcTBvMG0GCCsGAQUFBzAChmFodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMElEJTIw # VmVyaWZpZWQlMjBDb2RlJTIwU2lnbmluZyUyMFBDQSUyMDIwMjEuY3J0MA0GCSqG # SIb3DQEBDAUAA4ICAQCQdVoZ/U0m38l2iKaZFlsxavptpoOLyaR1a9ZK2TSF1kOn # FJhMDse6KkCgsveoiEjXTVc6Xt86IKHn76Nk5qZB0BXv2iMRQ2giAJmYvZcmstoZ # qfB2M3Kd5wnJhUJOtF/b6HsqSelY6nhrF06zor1lDmDQixBZcLB9zR1+RKQso1je # kNxYuUk+HaN3k1S57qk0O//YbkwU0mELCW04N5vICMZx5T5c7Nq/7uLvbVhCdD7f # 2bZpA4U7vOkB1ooB4AaER3pjoJ0Mad5LFyi6Na9p9Zu/hrLeOjU5FItS5Yxsqvlf # XxAThJ176CmkYstKRmytSHZ7JhKRfV6e9Zftk/ODb/CK4pGVAVqsOf4337bQGrOH # HCQ3IvN9gmnUuDh8JdvbheoWPHxIN1GB5sUiY584tXN7xdD8LCSsRqJvQ8e7a3gZ # WTgViugRs1QWq+N0G9Nje6JHlN1CjJehge+H5PGktJja+juGEr0P+ukSkcL6qaZx # FQTh3SDI71lvW++3bl/Ezd6SO8N9Udw+reoyvRHCyTiSsplZQSBTVJdPmo3qCpGu # yHFtPo5CBn3/FPTiqJd3M9BHoqKd0G9Kmg6fGcAvFwnLNXA2kov727wRljL3ypfq # L7iAT/Ynpxul6RwHRlcOf9dDGg1RRvr92NP/CWVXIb68geR2rvU/NsfmtjF1wDCC # B54wggWGoAMCAQICEzMAAAAHh6M0o3uljhwAAAAAAAcwDQYJKoZIhvcNAQEMBQAw # dzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjFI # MEYGA1UEAxM/TWljcm9zb2Z0IElkZW50aXR5IFZlcmlmaWNhdGlvbiBSb290IENl # cnRpZmljYXRlIEF1dGhvcml0eSAyMDIwMB4XDTIxMDQwMTIwMDUyMFoXDTM2MDQw # MTIwMTUyMFowYzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjE0MDIGA1UEAxMrTWljcm9zb2Z0IElEIFZlcmlmaWVkIENvZGUgU2ln # bmluZyBQQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALLw # wK8ZiCji3VR6TElsaQhVCbRS/3pK+MHrJSj3Zxd3KU3rlfL3qrZilYKJNqztA9OQ # acr1AwoNcHbKBLbsQAhBnIB34zxf52bDpIO3NJlfIaTE/xrweLoQ71lzCHkD7A4A # s1Bs076Iu+mA6cQzsYYH/Cbl1icwQ6C65rU4V9NQhNUwgrx9rGQ//h890Q8JdjLL # w0nV+ayQ2Fbkd242o9kH82RZsH3HEyqjAB5a8+Ae2nPIPc8sZU6ZE7iRrRZywRmr # KDp5+TcmJX9MRff241UaOBs4NmHOyke8oU1TYrkxh+YeHgfWo5tTgkoSMoayqoDp # HOLJs+qG8Tvh8SnifW2Jj3+ii11TS8/FGngEaNAWrbyfNrC69oKpRQXY9bGH6jn9 # NEJv9weFxhTwyvx9OJLXmRGbAUXN1U9nf4lXezky6Uh/cgjkVd6CGUAf0K+Jw+GE # /5VpIVbcNr9rNE50Sbmy/4RTCEGvOq3GhjITbCa4crCzTTHgYYjHs1NbOc6brH+e # KpWLtr+bGecy9CrwQyx7S/BfYJ+ozst7+yZtG2wR461uckFu0t+gCwLdN0A6cFtS # RtR8bvxVFyWwTtgMMFRuBa3vmUOTnfKLsLefRaQcVTgRnzeLzdpt32cdYKp+dhr2 # ogc+qM6K4CBI5/j4VFyC4QFeUP2YAidLtvpXRRo3AgMBAAGjggI1MIICMTAOBgNV # HQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNlBKbAPD2Ns # 72nX9c0pnqRIajDmMFQGA1UdIARNMEswSQYEVR0gADBBMD8GCCsGAQUFBwIBFjNo # dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5o # dG0wGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwDwYDVR0TAQH/BAUwAwEB/zAf # BgNVHSMEGDAWgBTIftJqhSobyhmYBAcnz1AQT2ioojCBhAYDVR0fBH0wezB5oHeg # dYZzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0 # JTIwSWRlbnRpdHklMjBWZXJpZmljYXRpb24lMjBSb290JTIwQ2VydGlmaWNhdGUl # MjBBdXRob3JpdHklMjAyMDIwLmNybDCBwwYIKwYBBQUHAQEEgbYwgbMwgYEGCCsG # AQUFBzAChnVodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01p # Y3Jvc29mdCUyMElkZW50aXR5JTIwVmVyaWZpY2F0aW9uJTIwUm9vdCUyMENlcnRp # ZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAyMC5jcnQwLQYIKwYBBQUHMAGGIWh0dHA6 # Ly9vbmVvY3NwLm1pY3Jvc29mdC5jb20vb2NzcDANBgkqhkiG9w0BAQwFAAOCAgEA # fyUqnv7Uq+rdZgrbVyNMul5skONbhls5fccPlmIbzi+OwVdPQ4H55v7VOInnmezQ # EeW4LqK0wja+fBznANbXLB0KrdMCbHQpbLvG6UA/Xv2pfpVIE1CRFfNF4XKO8XYE # a3oW8oVH+KZHgIQRIwAbyFKQ9iyj4aOWeAzwk+f9E5StNp5T8FG7/VEURIVWArbA # zPt9ThVN3w1fAZkF7+YU9kbq1bCR2YD+MtunSQ1Rft6XG7b4e0ejRA7mB2IoX5hN # h3UEauY0byxNRG+fT2MCEhQl9g2i2fs6VOG19CNep7SquKaBjhWmirYyANb0RJSL # WjinMLXNOAga10n8i9jqeprzSMU5ODmrMCJE12xS/NWShg/tuLjAsKP6SzYZ+1Ry # 358ZTFcx0FS/mx2vSoU8s8HRvy+rnXqyUJ9HBqS0DErVLjQwK8VtsBdekBmdTbQV # oCgPCqr+PDPB3xajYnzevs7eidBsM71PINK2BoE2UfMwxCCX3mccFgx6UsQeRSdV # VVNSyALQe6PT12418xon2iDGE81OGCreLzDcMAZnrUAx4XQLUz6ZTl65yPUiOh3k # 7Yww94lDf+8oG2oZmDh5O1Qe38E+M3vhKwmzIeoB1dVLlz4i3IpaDcR+iuGjH2Td # aC1ZOmBXiCRKJLj4DT2uhJ04ji+tHD6n58vhavFIrmcxghqRMIIajQIBATBxMFox # CzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzAp # BgNVBAMTIk1pY3Jvc29mdCBJRCBWZXJpZmllZCBDUyBFT0MgQ0EgMDQCEzMABN83 # wV0KQIA53+UAAAAE3zcwDQYJYIZIAWUDBAIBBQCgXjAQBgorBgEEAYI3AgEMMQIw # ADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAvBgkqhkiG9w0BCQQxIgQgINHG # KOP0PzD4/TZIUQmZzBcBBFCw2ZHPtoFEPCIX4g8wDQYJKoZIhvcNAQEBBQAEggGA # T2QHE3xmXgMatZWWzJrZ7UEF1755dXs2TTOrVnLxwvxnulrzko2yX2QTBbTnmVkl # Nv9TyBAH4Vy71XxEkVSOFxohpt4JWeV/t7ics/L9hq0ZZklr1en1n7OVyjUePX1G # ofEJjjsXzAz542ysAQIoJ2FS/BsihTjcctBgWFTYd08+TFrE5t+dhQ981qp1HIo3 # 34chCv1wOSK6mnsoXQuhY30WV7XEtysAf3lwx9l+YJDx6w5hFZltPoa9GtNECBLP # 3/V3FjrfbMVc3TgPuAanKdW8g+sw6t3qvkf9r1rIWpfyjTF/Pd6O8zTdMP5BI7xP # dxim6+MZua93g5HqMZhDH5g5noNJK/au7RSkgmGgCGC7jlC932pMKF0CSO0e7gQK # 8tp1ODTXdeRrQXkAWLlj6DACEVcsZRhX9546KwO0pRHAoVqYAYAj0/msN9hpiANb # rjqzyND4nIeY6AHBhxB7kig+nQSy1+Z2sOew/XMfh72vsXlGLFjlcSqBMAJoL1hE # oYIYETCCGA0GCisGAQQBgjcDAwExghf9MIIX+QYJKoZIhvcNAQcCoIIX6jCCF+YC # AQMxDzANBglghkgBZQMEAgEFADCCAWIGCyqGSIb3DQEJEAEEoIIBUQSCAU0wggFJ # AgEBBgorBgEEAYRZCgMBMDEwDQYJYIZIAWUDBAIBBQAEILbzq12aWrTjDZd2gyJo # XnNBxUS1vPSb0TV/rzXzwJV2AgZqhI0bXPEYEzIwMjYwODI1MTIxNTExLjk5MVow # BIACAfSggeGkgd4wgdsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJzAlBgNV # BAsTHm5TaGllbGQgVFNTIEVTTjo3ODAwLTA1RTAtRDk0NzE1MDMGA1UEAxMsTWlj # cm9zb2Z0IFB1YmxpYyBSU0EgVGltZSBTdGFtcGluZyBBdXRob3JpdHmggg8hMIIH # gjCCBWqgAwIBAgITMwAAAAXlzw//Zi7JhwAAAAAABTANBgkqhkiG9w0BAQwFADB3 # MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMUgw # RgYDVQQDEz9NaWNyb3NvZnQgSWRlbnRpdHkgVmVyaWZpY2F0aW9uIFJvb3QgQ2Vy # dGlmaWNhdGUgQXV0aG9yaXR5IDIwMjAwHhcNMjAxMTE5MjAzMjMxWhcNMzUxMTE5 # MjA0MjMxWjBhMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lc3RhbXBp # bmcgQ0EgMjAyMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJ5851Jj # /eDFnwV9Y7UGIqMcHtfnlzPREwW9ZUZHd5HBXXBvf7KrQ5cMSqFSHGqg2/qJhYqO # QxwuEQXG8kB41wsDJP5d0zmLYKAY8Zxv3lYkuLDsfMuIEqvGYOPURAH+Ybl4SJEE # Snt0MbPEoKdNihwM5xGv0rGofJ1qOYSTNcc55EbBT7uq3wx3mXhtVmtcCEr5ZKTk # KKE1CxZvNPWdGWJUPC6e4uRfWHIhZcgCsJ+sozf5EeH5KrlFnxpjKKTavwfFP6Xa # GZGWUG8TZaiTogRoAlqcevbiqioUz1Yt4FRK53P6ovnUfANjIgM9JDdJ4e0qiDRm # 5sOTiEQtBLGd9Vhd1MadxoGcHrRCsS5rO9yhv2fjJHrmlQ0EIXmp4DhDBieKUGR+ # eZ4CNE3ctW4uvSDQVeSp9h1SaPV8UWEfyTxgGjOsRpeexIveR1MPTVf7gt8hY64X # NPO6iyUGsEgt8c2PxF87E+CO7A28TpjNq5eLiiunhKbq0XbjkNoU5JhtYUrlmAbp # xRjb9tSreDdtACpm3rkpxp7AQndnI0Shu/fk1/rE3oWsDqMX3jjv40e8KN5YsJBn # czyWB4JyeeFMW3JBfdeAKhzohFe8U5w9WuvcP1E8cIxLoKSDzCCBOu0hWdjzKNu8 # Y5SwB1lt5dQhABYyzR3dxEO/T1K/BVF3rV69AgMBAAGjggIbMIICFzAOBgNVHQ8B # Af8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFGtpKDo1L0hjQM97 # 2K9J6T7ZPdshMFQGA1UdIARNMEswSQYEVR0gADBBMD8GCCsGAQUFBwIBFjNodHRw # Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0w # EwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEw # DwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTIftJqhSobyhmYBAcnz1AQT2io # ojCBhAYDVR0fBH0wezB5oHegdYZzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3Br # aW9wcy9jcmwvTWljcm9zb2Z0JTIwSWRlbnRpdHklMjBWZXJpZmljYXRpb24lMjBS # b290JTIwQ2VydGlmaWNhdGUlMjBBdXRob3JpdHklMjAyMDIwLmNybDCBlAYIKwYB # BQUHAQEEgYcwgYQwgYEGCCsGAQUFBzAChnVodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMElkZW50aXR5JTIwVmVyaWZpY2F0 # aW9uJTIwUm9vdCUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAyMC5jcnQw # DQYJKoZIhvcNAQEMBQADggIBAF+Idsd+bbVaFXXnTHho+k7h2ESZJRWluLE0Oa/p # O+4ge/XEizXvhs0Y7+KVYyb4nHlugBesnFqBGEdC2IWmtKMyS1OWIviwpnK3aL5J # edwzbeBF7POyg6IGG/XhhJ3UqWeWTO+Czb1c2NP5zyEh89F72u9UIw+IfvM9lzDm # c2O2END7MPnrcjWdQnrLn1Ntday7JSyrDvBdmgbNnCKNZPmhzoa8PccOiQljjTW6 # GePe5sGFuRHzdFt8y+bN2neF7Zu8hTO1I64XNGqst8S+w+RUdie8fXC1jKu3m9KG # IqF4aldrYBamyh3g4nJPj/LR2CBaLyD+2BuGZCVmoNR/dSpRCxlot0i79dKOChmo # ONqbMI8m04uLaEHAv4qwKHQ1vBzbV/nG89LDKbRSSvijmwJwxRxLLpMQ/u4xXxFf # R4f/gksSkbJp7oqLwliDm/h+w0aJ/U5ccnYhYb7vPKNMN+SZDWycU5ODIRfyoGl5 # 9BsXR/HpRGtiJquOYGmvA/pk5vC1lcnbeMrcWD/26ozePQ/TWfNXKBOmkFpvPE8C # H+EeGGWzqTCjdAsno2jzTeNSxlx3glDGJgcdz5D/AAxw9Sdgq/+rY7jjgs7X6fqP # TXPmaCAJKVHAP19oEjJIBwD1LyHbaEgBxFCogYSOiUIr0Xqcr1nJfiWG2GwYe6Zo # AF1bMIIHlzCCBX+gAwIBAgITMwAAAFck05XgounJMQAAAAAAVzANBgkqhkiG9w0B # AQwFADBhMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lc3RhbXBpbmcg # Q0EgMjAyMDAeFw0yNTEwMjMyMDQ2NTNaFw0yNjEwMjIyMDQ2NTNaMIHbMQswCQYD # VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe # MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3Nv # ZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046 # NzgwMC0wNUUwLUQ5NDcxNTAzBgNVBAMTLE1pY3Jvc29mdCBQdWJsaWMgUlNBIFRp # bWUgU3RhbXBpbmcgQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEAsWylCpMIfbizJLY1kPXO2cmX2HRWvRbAmeKSZ5ex7/jCymdV7Eap+Ic2 # iqRtWDkKKe5gL6JV80wtn5C2qHJLPxUYFKNG3UkHkAI21MoCN+YWnhT8K/YuPib6 # +6970jdbeFKIiZMWwd5hnpX9J3jeteuEdXbp/DfFBK15JuD3JOzWuF2suQCPgqYj # QPk/gpq+3KCKtXJRbXSCSJ9YtITU2IHwmfdE7l2PfZ154w041po+fDeTj0gJOzcV # /Jv56Q0M+w19jAKo/I5PEzrLV1IPQnmP4or1X4RbJXk8ONXyOOfXOxK2VLpNxgkl # K1yAezbFP2uzqihaXkW1h9GQLGENKESnezwgdRaLNNaYtm8AT/pZHYJ35mZVqkZd # MIckpQHJk/F1fSLyDKeKtH4TC4cc3ESKUMgItq07ZZm74JCsfhmrQ1ijVNDi1Sln # +QBamgC7WviZbkQnceQRq9DY+6hANwOrasAZUiVr2kPuj1jHDOXzUG4O9QTK70P/ # oXSqZAN1oTv3UfF8JTGmAxg+l1ZPOz50MY96HBDw/3bI/wBGNvLk6fLVnrxGN5B5 # unF/lYvjjWbIUdyBPVQnPOKXu08SRHbY19M1HoWX6PNZv+vzSeqVeWWHKdKjC3Gj # VjbbGpi+JLbiyaKRSwEqo49tJLvu69cQ7dWsbksai4TURnVj2mMCAwEAAaOCAcsw # ggHHMB0GA1UdDgQWBBSOg8leLTUOAglIZ+bjXpiD7RKSpzAfBgNVHSMEGDAWgBRr # aSg6NS9IY0DPe9ivSek+2T3bITBsBgNVHR8EZTBjMGGgX6BdhltodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBQdWJsaWMlMjBS # U0ElMjBUaW1lc3RhbXBpbmclMjBDQSUyMDIwMjAuY3JsMHkGCCsGAQUFBwEBBG0w # azBpBggrBgEFBQcwAoZdaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j # ZXJ0cy9NaWNyb3NvZnQlMjBQdWJsaWMlMjBSU0ElMjBUaW1lc3RhbXBpbmclMjBD # QSUyMDIwMjAuY3J0MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUH # AwgwDgYDVR0PAQH/BAQDAgeAMGYGA1UdIARfMF0wUQYMKwYBBAGCN0yDfQEBMEEw # PwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9j # cy9SZXBvc2l0b3J5Lmh0bTAIBgZngQwBBAIwDQYJKoZIhvcNAQEMBQADggIBAHJ1 # wHY86Zk5SUBDPY25d/u9YJVaaNa71uxjX4cyO/XJ4uPENCSOwkRTnNogPLxTD0Fg # 3z4TFf/2T/0IFSxdtWVtTjhzrn+WLInzeRawUhTCFVrPBJKEWVshm+Ig7/nB7JbJ # N88+ltImBbL5kT1StBLfG6UksAcDbNSQww90CUXhGueBxlnSvjkAX1ohiN16y1bB # 2s0rvQx8Csepl2CuBefTfDrMGzW/tzNx5YaK2D8OWweqTWZcGlJO4YjZNI83cTrQ # ghfHl/8AXOHj8cWL3wEFltQQs2xeRYAb3Kdnl7oIWKKXWaBYJY5P3QPsiC+DTMp7 # ejdYKTrb396f3gr+wL/Ms5/Z3vIWZPJJv18qNw40fUNveRnwzMQnx8dM2bGuXXQZ # 5y7P8aXT4HJMo349qZtn4XQwiUE/DDp++MUL0kgjvd/Deo7Xr371PFPPYb4TboZh # jV1x9+wCHDoOpNCBt+VuXU78ytJdKzQ1Jv2cEP1F9H9/wSLsMDUvWME7u9mGElOP # DZPMVr8AuBEuLdbTSEdaLwsZBplzxLBcgxhZ/Cs30yBhuE3QhqT1YDZ2pa56RexP # A2SasPcToT6gJgJ6E06BmZ2zQTNvWOjs5XQqHbYuXcoeDcwe2UaC7EDOGD8GmLE9 # LiqtQsuQCM7v7I2xR+sPZT2Ax/85HjIkM+3MzTK1MYIHQzCCBz8CAQEweDBhMQsw # CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYD # VQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lc3RhbXBpbmcgQ0EgMjAyMAIT # MwAAAFck05XgounJMQAAAAAAVzANBglghkgBZQMEAgEFAKCCBJwwEQYLKoZIhvcN # AQkQAg8xAgUAMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0B # CQUxDxcNMjYwODI1MTIxNTExWjAvBgkqhkiG9w0BCQQxIgQgG6NeIXzseGLvNtZ8 # ERQon+0Oz9p44OalmjwHeFY0JcswgbkGCyqGSIb3DQEJEAIvMYGpMIGmMIGjMIGg # BCD1PJ9ktQVuTGWIbKLO4f1VUOlUU29ARCEpDZmFTHjbUjB8MGWkYzBhMQswCQYD # VQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQD # EylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lc3RhbXBpbmcgQ0EgMjAyMAITMwAA # AFck05XgounJMQAAAAAAVzCCA14GCyqGSIb3DQEJEAISMYIDTTCCA0mhggNFMIID # QTCCAikCAQEwggEJoYHhpIHeMIHbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z # MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046NzgwMC0wNUUwLUQ5NDcxNTAzBgNV # BAMTLE1pY3Jvc29mdCBQdWJsaWMgUlNBIFRpbWUgU3RhbXBpbmcgQXV0aG9yaXR5 # oiMKAQEwBwYFKw4DAhoDFQD9LzE5nEJRAUE2Ss3xaKKPXHnLw6BnMGWkYzBhMQsw # CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYD # VQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lc3RhbXBpbmcgQ0EgMjAyMDAN # BgkqhkiG9w0BAQsFAAIFAO43nVMwIhgPMjAyNjA4MjUwNDQ5MjNaGA8yMDI2MDgy # NjA0NDkyM1owdDA6BgorBgEEAYRZCgQBMSwwKjAKAgUA7jedUwIBADAHAgEAAgIJ # SzAHAgEAAgISMjAKAgUA7jju0wIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEE # AYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBCwUAA4IB # AQBxiTbeYb8vPMql1BmzWgFm6xJBfZ3v+MlU5GCV9jayikf7QT1IG0PHHOJRIXjx # R8IsOgpxzaeJz0h6m2bDeNkGdc+pRFf5gYx8Ld9fwgrylYLsBZYdv4tqWrPdmX/H # cGTVhwhn2zEc1A5p+0tSAyQkadNaPTOzIM5nImG06A+MB1LsErWnmHBdnSFtEj4F # 6sGi5ugpphM5USC0cOC51gA9x0M0xEDNCxXrAqXL+6ti2EqdDebBQX9MAsMqBYTI # GkmRPnQy7B7+DyqOYwu4CGvtTGotYPwfuetA1FI8o2wd2VQ4NN8Zup2d5r0Q3Jxz # tiS7/mtLMLNG3BjeFiZlZNDiMA0GCSqGSIb3DQEBAQUABIICAGKaS+7oC5NdiTHB # LqN91VgRzsgSMzJPj83FGDHRcqby0fdKgsvh1DWf0ywgNsQWntgw8Y4AGaCcmXVY # 6AEY84G1ev0KJ/Pz/4dzXZukhLCstUBVqCcewhLtwRYgQR7345U6vCDGnv8PyQtP # qsIu9///7u5sAC4qRLJVhGCcN5Y6/3Sp0xrkv0TpT6P0u9xEFemJmQEY0BsgUeHg # +qjUzYHt6NJeJ3IvGf9HyZeY3YwOuHfNzLClSNlcSN9USn/bESKqcxCUfAU+sYNI # tqyKAlheQDBexYf3sxthCczEUUYXVOh4p5V9gYJ7vMURJaxy8ZL4IkhCIfwm3smq # NiHYN3xionDwK0xHJSK+XW+6X6WgBRj4S1finvKXFUW6OtdIG88QoK6aZsFLCrD7 # 1y657Ujvev/YTtjfhOSL4ChctlhTTVazywkReMPJUuUziO8wyRvavt09gXq8b/+Z # yrr44F6vSqPBInm6XPvpU+zH5OJK7O19GkCCilHVWGzcdFQWc1imVVlfYRvcNlp3 # g5pfManx372IN4WozRPgm4bS0P2MTDpbxVjs5ExcGuzbWep7Wq1m6HrUYFgM24qM # Fn/xUPO2ZPLpTbzLxldbHfGX4dtcPS1ngTio6rxMSOSzOCAE0hXxpF2YB0jtkVkM # z7ovlGi5DewLrLJnY6Jc4gFuuIYc # SIG # End signature block |