tests/Test-Assessment.41209.ps1
|
<#
.SYNOPSIS User and Entity Behavior Analytics (UEBA) is enabled in Microsoft Sentinel .DESCRIPTION Verifies UEBA configuration on every Sentinel-onboarded Log Analytics workspace by querying the Microsoft.SecurityInsights/settings/Ueba and settings/EntityAnalytics resources via ARM. Reports per-workspace UEBA state, configured data sources, onboarding date, and entity providers. .NOTES Test ID: 41209 Workshop Task: SECOPS_103 Pillar: SecOps Category: Security information and event management Required API: Azure Resource Manager (management.azure.com) #> function Test-Assessment-41209 { [ZtTest( Category = 'Security information and event management', ImplementationCost = 'Low', Service = ('Azure'), MinimumLicense = ('Consumption-based: Microsoft Sentinel'), Pillar = 'SecOps', RiskLevel = 'Medium', SfiPillar = 'Monitor and detect cyberthreats', TenantType = ('Workforce'), TestId = 41209, Title = 'User and Entity Behavior Analytics (UEBA) is enabled in Microsoft Sentinel', UserImpact = 'Low' )] [CmdletBinding()] param() #region Data Collection Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose $activity = 'Checking UEBA configuration in Microsoft Sentinel workspaces' # Q1 + Q2 + onboarding check via shared helper. # Returns 'Forbidden' on ARG 401/403 (Investigate). # Returns $null on unexpected ARG failure (Investigate). # Returns 'NoSubscriptions' when no enabled subscriptions are accessible (Skip). # Returns 'NoWorkspaces' when no Log Analytics workspaces exist in scope (Skip). $allWorkspaces = Get-SentinelWorkspaceData -Activity $activity if ($null -eq $allWorkspaces) { $params = @{ TestId = '41209' Title = 'User and Entity Behavior Analytics (UEBA) is enabled in Microsoft Sentinel' Status = $false Result = '⚠️ Azure Resource Graph returned an unexpected error while querying subscriptions or Log Analytics workspaces. This is likely a transient issue, please re-run the assessment.' CustomStatus = 'Investigate' } Add-ZtTestResultDetail @params return } if ($allWorkspaces -eq 'Forbidden') { $params = @{ TestId = '41209' Title = 'User and Entity Behavior Analytics (UEBA) is enabled in Microsoft Sentinel' Status = $false Result = '⚠️ Azure Resource Graph returned insufficient permissions when querying subscriptions or workspaces. Ensure you have at least Reader access to the Azure subscriptions being tested.' CustomStatus = 'Investigate' } Add-ZtTestResultDetail @params return } if ($allWorkspaces -eq 'NoSubscriptions') { Write-PSFMessage 'No enabled subscriptions found — skipping Sentinel UEBA check.' -Tag Test -Level VeryVerbose Add-ZtTestResultDetail -SkippedBecause NotApplicable return } if ($allWorkspaces -eq 'NoWorkspaces') { Write-PSFMessage 'No Log Analytics workspaces found across accessible subscriptions — skipping Sentinel UEBA check.' -Tag Test -Level VeryVerbose Add-ZtTestResultDetail -SkippedBecause NotApplicable return } $checkableWorkspaces = @($allWorkspaces | Where-Object { -not $_.PermissionError }) $forbiddenWorkspaces = @($allWorkspaces | Where-Object { $_.PermissionError }) $onboardingErrorWorkspaces = @($allWorkspaces | Where-Object { $_.OnboardingError }) $onboardedWorkspaces = @($checkableWorkspaces | Where-Object { $_.SentinelOnboarded }) if ($onboardedWorkspaces.Count -eq 0) { if ($forbiddenWorkspaces.Count -gt 0 -or $onboardingErrorWorkspaces.Count -gt 0) { # Either 401/403 (PermissionError) or 5xx/exception (OnboardingError) on the Q3 check — # cannot confirm whether those workspaces have Sentinel onboarded; state is unknown. $params = @{ TestId = '41209' Title = 'User and Entity Behavior Analytics (UEBA) is enabled in Microsoft Sentinel' Status = $false Result = '⚠️ One or more Log Analytics workspaces returned an error or insufficient permissions when checking Sentinel onboarding state. No Sentinel-onboarded workspace was confirmed among accessible workspaces — the overall state cannot be determined. Ensure Microsoft Sentinel Reader is granted on all workspaces and re-run the assessment.' CustomStatus = 'Investigate' } Add-ZtTestResultDetail @params } else { # Spec: no Sentinel-onboarded workspaces with full visibility — Skipped. Write-PSFMessage 'No Sentinel-onboarded workspaces found — skipping Sentinel UEBA check.' -Tag Test -Level VeryVerbose Add-ZtTestResultDetail -SkippedBecause NotApplicable } return } # Q1 (spec): Read the Sentinel UEBA setting for each Sentinel-onboarded workspace. # Q2 (spec): Read the EntityAnalytics setting (controls IdentityInfo synchronisation, a UEBA prerequisite). # Both use api-version 2024-10-01-preview; there is no GA version for the settings endpoint. # -FullResponse is required so that HTTP 404 (UEBA never enabled → Fail) can be distinguished from # HTTP 200 with empty dataSources (UEBA enabled but not yet analysing any source → Investigate). $uebaApiVersion = '2024-10-01-preview' $uebaResponseByWorkspace = @{} $entityAnalyticsResponseByWorkspace = @{} foreach ($workspace in $onboardedWorkspaces) { Write-ZtProgress -Activity $activity -Status "Reading UEBA and EntityAnalytics settings for $($workspace.WorkspaceName) in $($workspace.SubscriptionName)" # Q1: Ueba setting. $uebaPath = "$($workspace.WorkspaceId)/providers/Microsoft.SecurityInsights/settings/Ueba?api-version=$uebaApiVersion" try { $uebaResponseByWorkspace[$workspace.WorkspaceId] = Invoke-ZtAzureRequest -Path $uebaPath -FullResponse -ErrorAction Stop } catch { # Unexpected connection-level error — cannot determine UEBA state for this workspace. $uebaResponseByWorkspace[$workspace.WorkspaceId] = $null Write-PSFMessage "Error reading UEBA setting for workspace '$($workspace.WorkspaceName)' in '$($workspace.SubscriptionName)': $_" -Tag Test -Level Warning } # Q2: EntityAnalytics setting. # A 404 here means entityProviders were never explicitly set — not a hard error per spec. $entityAnalyticsPath = "$($workspace.WorkspaceId)/providers/Microsoft.SecurityInsights/settings/EntityAnalytics?api-version=$uebaApiVersion" try { $entityAnalyticsResponseByWorkspace[$workspace.WorkspaceId] = Invoke-ZtAzureRequest -Path $entityAnalyticsPath -FullResponse -ErrorAction Stop } catch { $entityAnalyticsResponseByWorkspace[$workspace.WorkspaceId] = $null Write-PSFMessage "Error reading EntityAnalytics setting for workspace '$($workspace.WorkspaceName)' in '$($workspace.SubscriptionName)': $_" -Tag Test -Level Warning } } #endregion Data Collection #region Assessment Logic $workspaceResults = foreach ($workspace in $onboardedWorkspaces) { $uebaResponse = $uebaResponseByWorkspace[$workspace.WorkspaceId] $entityAnalyticsResponse = $entityAnalyticsResponseByWorkspace[$workspace.WorkspaceId] # Evaluate Q1 — UEBA setting. # $null = state unknown (connection error or unexpected HTTP error); $true/$false = known state. $uebaPresent = $null $dataSources = @() $onboardDateTime = $null $rowStatus = 'Investigate' if ($null -eq $uebaResponse) { # Unexpected connection error — state unknown; $uebaPresent stays $null. $rowStatus = 'Investigate' } else { switch ([int]$uebaResponse.StatusCode) { 200 { try { $uebaSetting = $uebaResponse.Content | ConvertFrom-Json -ErrorAction Stop $uebaPresent = $true $dataSources = @($uebaSetting.properties.dataSources | Where-Object { $_ }) $onboardDateTime = $uebaSetting.properties.onboardDateTime # Pass when at least one supported data source is configured; Investigate when empty. $rowStatus = if ($dataSources.Count -gt 0) { 'Pass' } else { 'Investigate' } } catch { # Malformed or empty response body — cannot parse UEBA setting; retain unknown state. Write-PSFMessage "Failed to parse UEBA setting response for workspace '$($workspace.WorkspaceName)': $_" -Tag Test -Level Warning $uebaPresent = $null $rowStatus = 'Investigate' } } 404 { # UEBA has never been enabled on this workspace — definitively off. $uebaPresent = $false $rowStatus = 'Fail' } default { # 401, 403, 5xx — API returned but state is unknown; $uebaPresent stays $null. $rowStatus = 'Investigate' } } } # Evaluate Q2 — EntityAnalytics setting (informational; does not affect Pass/Fail). # $null = state unknown (connection error or unexpected HTTP error). # $false = definitively not configured (404: entityProviders never explicitly set per spec). $entityAnalyticsPresent = $null $entityProviders = @() if ($null -ne $entityAnalyticsResponse) { switch ([int]$entityAnalyticsResponse.StatusCode) { 200 { try { $eaSetting = $entityAnalyticsResponse.Content | ConvertFrom-Json -ErrorAction Stop $entityAnalyticsPresent = $true $entityProviders = @($eaSetting.properties.entityProviders | Where-Object { $_ }) } catch { # Malformed or empty response body — EntityAnalytics state stays $null (unknown). Write-PSFMessage "Failed to parse EntityAnalytics setting response for workspace '$($workspace.WorkspaceName)': $_" -Tag Test -Level Warning $entityAnalyticsPresent = $null } } 404 { # Spec: entityProviders were never explicitly set — definitively not configured. $entityAnalyticsPresent = $false } # 401, 403, 5xx → state unknown; $entityAnalyticsPresent stays $null. } } # $null response (connection error) → $entityAnalyticsPresent stays $null. [PSCustomObject]@{ SubscriptionName = $workspace.SubscriptionName SubscriptionId = $workspace.SubscriptionId WorkspaceName = $workspace.WorkspaceName ResourceGroup = $workspace.ResourceGroup WorkspaceId = $workspace.WorkspaceId UebaPresent = $uebaPresent DataSources = $dataSources OnboardDateTime = $onboardDateTime EntityAnalyticsPresent = $entityAnalyticsPresent EntityProviders = $entityProviders RowStatus = $rowStatus } } $workspaceResults = @($workspaceResults) $failItems = @($workspaceResults | Where-Object { $_.RowStatus -eq 'Fail' }) $investigateItems = @($workspaceResults | Where-Object { $_.RowStatus -eq 'Investigate' }) # Overall result: Fail takes priority over Investigate; Pass only when every workspace passes # and no workspace had an unresolvable Q3 onboarding error (incomplete scan cannot report clean). $passed = $failItems.Count -eq 0 -and $investigateItems.Count -eq 0 -and $onboardingErrorWorkspaces.Count -eq 0 $customStatus = $null if ($failItems.Count -gt 0) { $testResultMarkdown = "❌ UEBA is not enabled in the Sentinel workspace.`n`n%TestResult%" } elseif ($investigateItems.Count -gt 0 -or $onboardingErrorWorkspaces.Count -gt 0) { $customStatus = 'Investigate' $testResultMarkdown = "⚠️ The UEBA setting returned an unexpected response, or UEBA is enabled but no supported data sources are configured.`n`n%TestResult%" } else { $testResultMarkdown = "✅ UEBA is enabled in the Sentinel workspace.`n`n%TestResult%" } #endregion Assessment Logic #region Report Generation $azContext = Get-AzContext -ErrorAction SilentlyContinue $portalHost = if ($azContext -and $azContext.Environment.Name -eq 'AzureUSGovernment') { 'https://portal.azure.us' } else { 'https://portal.azure.com' } $portalSentinelLink = "$portalHost/#view/HubsExtension/BrowseResource/resourceType/microsoft.securityinsightsarg%2Fsentinel" $tableTitle = 'UEBA configuration per workspace' $formatTemplate = @' ## [{0}]({1}) | Subscription | Workspace | UEBA enabled | Data sources | Onboarded since | Entity analytics | Entity providers | Status | | :----------- | :-------- | :----------- | :----------- | :-------------- | :--------------- | :--------------- | :----- | {2} '@ $tableRows = '' $maxDisplay = 10 $statusPriority = @{ Fail = 0; Investigate = 1; Pass = 2 } $displayResults = @($workspaceResults | Sort-Object { $statusPriority[$_.RowStatus] }, SubscriptionName, WorkspaceName) $hasMoreItems = $false if ($workspaceResults.Count -gt $maxDisplay) { $displayResults = @($displayResults | Select-Object -First $maxDisplay) $hasMoreItems = $true } foreach ($result in $displayResults) { $subLink = "$portalHost/#resource/subscriptions/$($result.SubscriptionId)" $sentinelId = "/subscriptions/$($result.SubscriptionId)/resourcegroups/$($result.ResourceGroup)/providers/microsoft.securityinsightsarg/sentinel/$($result.WorkspaceName)" $uebaLink = "$portalHost/#view/Microsoft_Azure_Security_Insights/MainMenuBlade/~/EntityBehavior/id/$($sentinelId -replace '/', '%2F')" $subMd = "[$(Get-SafeMarkdown $result.SubscriptionName)]($subLink)" $wsMd = "[$(Get-SafeMarkdown $result.WorkspaceName)]($uebaLink)" # dataSources and entityProviders are API-controlled enum values — no Get-SafeMarkdown. # $null fields mean state unknown (API error) → render as '—'; $true/$false → Yes/No. $uebaEnabledMd = if ($null -eq $result.UebaPresent) { '—' } elseif ($result.UebaPresent) { '✅ Yes' } else { '❌ No' } $dataSourcesMd = if ($result.DataSources.Count -gt 0) { $result.DataSources -join ', ' } else { '—' } $onboardedSinceMd = if ($result.OnboardDateTime) { $result.OnboardDateTime } else { '—' } $eaEnabledMd = if ($null -eq $result.EntityAnalyticsPresent) { '—' } elseif ($result.EntityAnalyticsPresent) { '✅ Yes' } else { '❌ No' } $entityProvidersMd = if ($result.EntityProviders.Count -gt 0) { $result.EntityProviders -join ', ' } else { '—' } $statusDisplay = switch ($result.RowStatus) { 'Pass' { '✅ Pass' } 'Fail' { '❌ Fail' } 'Investigate' { '⚠️ Investigate' } } $tableRows += "| $subMd | $wsMd | $uebaEnabledMd | $dataSourcesMd | $onboardedSinceMd | $eaEnabledMd | $entityProvidersMd | $statusDisplay |`n" } if ($hasMoreItems) { $remainingCount = $workspaceResults.Count - $maxDisplay $tableRows += "`n... and $remainingCount more. [View all in Microsoft Sentinel]($portalSentinelLink)`n" } $mdInfo = $formatTemplate -f $tableTitle, $portalSentinelLink, $tableRows $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo #endregion Report Generation $params = @{ TestId = '41209' Title = 'User and Entity Behavior Analytics (UEBA) is enabled in Microsoft Sentinel' Status = $passed Result = $testResultMarkdown } if ($customStatus) { $params.CustomStatus = $customStatus } Add-ZtTestResultDetail @params } |