Skip to content

Commit 73e3906

Browse files
authored
SecOps - 41209 - User and Entity Behavior Analytics (UEBA) is enabled in Microsoft Sentinel (#1445)
2 parents 52f8263 + 61065b4 commit 73e3906

3 files changed

Lines changed: 342 additions & 0 deletions

File tree

src/powershell/private/tests-shared/Get-SentinelWorkspaceData.ps1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ resources
121121

122122
$sentinelOnboarded = $false
123123
$permissionError = $false
124+
$onboardingError = $false
124125
try {
125126
$sentinelPath = "$($workspace.workspaceId)/providers/Microsoft.SecurityInsights/onboardingStates/default?api-version=2024-03-01"
126127
$response = Invoke-ZtAzureRequest -Path $sentinelPath -FullResponse -ErrorAction Stop
@@ -134,11 +135,13 @@ resources
134135
}
135136
default {
136137
Write-PSFMessage "Sentinel onboarding check for workspace '$($workspace.workspaceName)' returned unexpected status $($response.StatusCode)." -Tag Test -Level Warning
138+
$onboardingError = $true
137139
}
138140
}
139141
}
140142
catch {
141143
Write-PSFMessage "Error checking Sentinel onboarding for workspace '$($workspace.workspaceName)': $_" -Tag Test -Level Warning
144+
$onboardingError = $true
142145
}
143146

144147
$results += [PSCustomObject]@{
@@ -149,6 +152,7 @@ resources
149152
WorkspaceId = $workspace.workspaceId
150153
SentinelOnboarded = $sentinelOnboarded
151154
PermissionError = $permissionError
155+
OnboardingError = $onboardingError
152156
}
153157
}
154158

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
User and Entity Behavior Analytics (UEBA) builds machine-learning behavioral profiles for every user, host, IP address, and application observed in the workspace by analyzing sign-in logs, audit logs, security events, AAD service principal sign-ins, and supported third-party sources. UEBA produces enriched entity pages, anomaly events (BehaviorAnalytics table), and dynamic baselines that downstream analytics rules and Fusion correlate to surface high-fidelity incidents. Without UEBA, Sentinel detection is rule-deterministic — it can match what threat hunters anticipate (a documented IOC, a known TTP) but it cannot detect "this user just did something that user has never done before, from a country that user has never signed in from, against a resource only DA accounts touch". The detection gap is in low-and-slow credential abuse, insider threat, and adversary-in-the-middle session theft, where the threat actor has valid credentials and only a behavioral baseline can flag the deviation. Enabling UEBA also activates IdentityInfo synchronization, which materializes an enriched identity-context table that nearly all modern Sentinel built-in analytics rules join against; without UEBA enabled, a sizeable share of out-of-the-box rules silently produce no results. Enablement is a single workspace-level setting toggled via the Microsoft.SecurityInsights/settings/Ueba resource.
2+
3+
**Remediation action**
4+
5+
- [Enable User and Entity Behavior Analytics (UEBA)](https://learn.microsoft.com/azure/sentinel/enable-entity-behavior-analytics)
6+
- [Identify advanced threats with UEBA](https://learn.microsoft.com/azure/sentinel/identify-threats-with-entity-behavior-analytics)
7+
8+
<!--- Results --->
9+
%TestResult%
Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
<#
2+
.SYNOPSIS
3+
User and Entity Behavior Analytics (UEBA) is enabled in Microsoft Sentinel
4+
5+
.DESCRIPTION
6+
Verifies UEBA configuration on every Sentinel-onboarded Log Analytics workspace by querying
7+
the Microsoft.SecurityInsights/settings/Ueba and settings/EntityAnalytics resources via ARM.
8+
Reports per-workspace UEBA state, configured data sources, onboarding date, and entity providers.
9+
10+
.NOTES
11+
Test ID: 41209
12+
Workshop Task: SECOPS_103
13+
Pillar: SecOps
14+
Category: Security information and event management
15+
Required API: Azure Resource Manager (management.azure.com)
16+
#>
17+
18+
function Test-Assessment-41209 {
19+
[ZtTest(
20+
Category = 'Security information and event management',
21+
ImplementationCost = 'Low',
22+
Service = ('Azure'),
23+
MinimumLicense = ('Consumption-based: Microsoft Sentinel'),
24+
Pillar = 'SecOps',
25+
RiskLevel = 'Medium',
26+
SfiPillar = 'Monitor and detect cyberthreats',
27+
TenantType = ('Workforce'),
28+
TestId = 41209,
29+
Title = 'User and Entity Behavior Analytics (UEBA) is enabled in Microsoft Sentinel',
30+
UserImpact = 'Low'
31+
)]
32+
[CmdletBinding()]
33+
param()
34+
35+
#region Data Collection
36+
Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose
37+
$activity = 'Checking UEBA configuration in Microsoft Sentinel workspaces'
38+
39+
# Q1 + Q2 + onboarding check via shared helper.
40+
# Returns 'Forbidden' on ARG 401/403 (Investigate).
41+
# Returns $null on unexpected ARG failure (Investigate).
42+
# Returns 'NoSubscriptions' when no enabled subscriptions are accessible (Skip).
43+
# Returns 'NoWorkspaces' when no Log Analytics workspaces exist in scope (Skip).
44+
$allWorkspaces = Get-SentinelWorkspaceData -Activity $activity
45+
46+
if ($null -eq $allWorkspaces) {
47+
$params = @{
48+
TestId = '41209'
49+
Title = 'User and Entity Behavior Analytics (UEBA) is enabled in Microsoft Sentinel'
50+
Status = $false
51+
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.'
52+
CustomStatus = 'Investigate'
53+
}
54+
Add-ZtTestResultDetail @params
55+
return
56+
}
57+
58+
if ($allWorkspaces -eq 'Forbidden') {
59+
$params = @{
60+
TestId = '41209'
61+
Title = 'User and Entity Behavior Analytics (UEBA) is enabled in Microsoft Sentinel'
62+
Status = $false
63+
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.'
64+
CustomStatus = 'Investigate'
65+
}
66+
Add-ZtTestResultDetail @params
67+
return
68+
}
69+
70+
if ($allWorkspaces -eq 'NoSubscriptions') {
71+
Write-PSFMessage 'No enabled subscriptions found — skipping Sentinel UEBA check.' -Tag Test -Level VeryVerbose
72+
Add-ZtTestResultDetail -SkippedBecause NotApplicable
73+
return
74+
}
75+
76+
if ($allWorkspaces -eq 'NoWorkspaces') {
77+
Write-PSFMessage 'No Log Analytics workspaces found across accessible subscriptions — skipping Sentinel UEBA check.' -Tag Test -Level VeryVerbose
78+
Add-ZtTestResultDetail -SkippedBecause NotApplicable
79+
return
80+
}
81+
82+
$checkableWorkspaces = @($allWorkspaces | Where-Object { -not $_.PermissionError })
83+
$forbiddenWorkspaces = @($allWorkspaces | Where-Object { $_.PermissionError })
84+
$onboardingErrorWorkspaces = @($allWorkspaces | Where-Object { $_.OnboardingError })
85+
$onboardedWorkspaces = @($checkableWorkspaces | Where-Object { $_.SentinelOnboarded })
86+
87+
if ($onboardedWorkspaces.Count -eq 0) {
88+
if ($forbiddenWorkspaces.Count -gt 0 -or $onboardingErrorWorkspaces.Count -gt 0) {
89+
# Either 401/403 (PermissionError) or 5xx/exception (OnboardingError) on the Q3 check —
90+
# cannot confirm whether those workspaces have Sentinel onboarded; state is unknown.
91+
$params = @{
92+
TestId = '41209'
93+
Title = 'User and Entity Behavior Analytics (UEBA) is enabled in Microsoft Sentinel'
94+
Status = $false
95+
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.'
96+
CustomStatus = 'Investigate'
97+
}
98+
Add-ZtTestResultDetail @params
99+
}
100+
else {
101+
# Spec: no Sentinel-onboarded workspaces with full visibility — Skipped.
102+
Write-PSFMessage 'No Sentinel-onboarded workspaces found — skipping Sentinel UEBA check.' -Tag Test -Level VeryVerbose
103+
Add-ZtTestResultDetail -SkippedBecause NotApplicable
104+
}
105+
return
106+
}
107+
108+
# Q1 (spec): Read the Sentinel UEBA setting for each Sentinel-onboarded workspace.
109+
# Q2 (spec): Read the EntityAnalytics setting (controls IdentityInfo synchronisation, a UEBA prerequisite).
110+
# Both use api-version 2024-10-01-preview; there is no GA version for the settings endpoint.
111+
# -FullResponse is required so that HTTP 404 (UEBA never enabled → Fail) can be distinguished from
112+
# HTTP 200 with empty dataSources (UEBA enabled but not yet analysing any source → Investigate).
113+
$uebaApiVersion = '2024-10-01-preview'
114+
$uebaResponseByWorkspace = @{}
115+
$entityAnalyticsResponseByWorkspace = @{}
116+
117+
foreach ($workspace in $onboardedWorkspaces) {
118+
Write-ZtProgress -Activity $activity -Status "Reading UEBA and EntityAnalytics settings for $($workspace.WorkspaceName) in $($workspace.SubscriptionName)"
119+
120+
# Q1: Ueba setting.
121+
$uebaPath = "$($workspace.WorkspaceId)/providers/Microsoft.SecurityInsights/settings/Ueba?api-version=$uebaApiVersion"
122+
try {
123+
$uebaResponseByWorkspace[$workspace.WorkspaceId] = Invoke-ZtAzureRequest -Path $uebaPath -FullResponse -ErrorAction Stop
124+
}
125+
catch {
126+
# Unexpected connection-level error — cannot determine UEBA state for this workspace.
127+
$uebaResponseByWorkspace[$workspace.WorkspaceId] = $null
128+
Write-PSFMessage "Error reading UEBA setting for workspace '$($workspace.WorkspaceName)' in '$($workspace.SubscriptionName)': $_" -Tag Test -Level Warning
129+
}
130+
131+
# Q2: EntityAnalytics setting.
132+
# A 404 here means entityProviders were never explicitly set — not a hard error per spec.
133+
$entityAnalyticsPath = "$($workspace.WorkspaceId)/providers/Microsoft.SecurityInsights/settings/EntityAnalytics?api-version=$uebaApiVersion"
134+
try {
135+
$entityAnalyticsResponseByWorkspace[$workspace.WorkspaceId] = Invoke-ZtAzureRequest -Path $entityAnalyticsPath -FullResponse -ErrorAction Stop
136+
}
137+
catch {
138+
$entityAnalyticsResponseByWorkspace[$workspace.WorkspaceId] = $null
139+
Write-PSFMessage "Error reading EntityAnalytics setting for workspace '$($workspace.WorkspaceName)' in '$($workspace.SubscriptionName)': $_" -Tag Test -Level Warning
140+
}
141+
}
142+
143+
#endregion Data Collection
144+
145+
#region Assessment Logic
146+
147+
$workspaceResults = foreach ($workspace in $onboardedWorkspaces) {
148+
$uebaResponse = $uebaResponseByWorkspace[$workspace.WorkspaceId]
149+
$entityAnalyticsResponse = $entityAnalyticsResponseByWorkspace[$workspace.WorkspaceId]
150+
151+
# Evaluate Q1 — UEBA setting.
152+
# $null = state unknown (connection error or unexpected HTTP error); $true/$false = known state.
153+
$uebaPresent = $null
154+
$dataSources = @()
155+
$onboardDateTime = $null
156+
$rowStatus = 'Investigate'
157+
158+
if ($null -eq $uebaResponse) {
159+
# Unexpected connection error — state unknown; $uebaPresent stays $null.
160+
$rowStatus = 'Investigate'
161+
}
162+
else {
163+
switch ([int]$uebaResponse.StatusCode) {
164+
200 {
165+
try {
166+
$uebaSetting = $uebaResponse.Content | ConvertFrom-Json -ErrorAction Stop
167+
$uebaPresent = $true
168+
$dataSources = @($uebaSetting.properties.dataSources | Where-Object { $_ })
169+
$onboardDateTime = $uebaSetting.properties.onboardDateTime
170+
# Pass when at least one supported data source is configured; Investigate when empty.
171+
$rowStatus = if ($dataSources.Count -gt 0) { 'Pass' } else { 'Investigate' }
172+
}
173+
catch {
174+
# Malformed or empty response body — cannot parse UEBA setting; retain unknown state.
175+
Write-PSFMessage "Failed to parse UEBA setting response for workspace '$($workspace.WorkspaceName)': $_" -Tag Test -Level Warning
176+
$uebaPresent = $null
177+
$rowStatus = 'Investigate'
178+
}
179+
}
180+
404 {
181+
# UEBA has never been enabled on this workspace — definitively off.
182+
$uebaPresent = $false
183+
$rowStatus = 'Fail'
184+
}
185+
default {
186+
# 401, 403, 5xx — API returned but state is unknown; $uebaPresent stays $null.
187+
$rowStatus = 'Investigate'
188+
}
189+
}
190+
}
191+
192+
# Evaluate Q2 — EntityAnalytics setting (informational; does not affect Pass/Fail).
193+
# $null = state unknown (connection error or unexpected HTTP error).
194+
# $false = definitively not configured (404: entityProviders never explicitly set per spec).
195+
$entityAnalyticsPresent = $null
196+
$entityProviders = @()
197+
198+
if ($null -ne $entityAnalyticsResponse) {
199+
switch ([int]$entityAnalyticsResponse.StatusCode) {
200+
200 {
201+
try {
202+
$eaSetting = $entityAnalyticsResponse.Content | ConvertFrom-Json -ErrorAction Stop
203+
$entityAnalyticsPresent = $true
204+
$entityProviders = @($eaSetting.properties.entityProviders | Where-Object { $_ })
205+
}
206+
catch {
207+
# Malformed or empty response body — EntityAnalytics state stays $null (unknown).
208+
Write-PSFMessage "Failed to parse EntityAnalytics setting response for workspace '$($workspace.WorkspaceName)': $_" -Tag Test -Level Warning
209+
$entityAnalyticsPresent = $null
210+
}
211+
}
212+
404 {
213+
# Spec: entityProviders were never explicitly set — definitively not configured.
214+
$entityAnalyticsPresent = $false
215+
}
216+
# 401, 403, 5xx → state unknown; $entityAnalyticsPresent stays $null.
217+
}
218+
}
219+
# $null response (connection error) → $entityAnalyticsPresent stays $null.
220+
221+
[PSCustomObject]@{
222+
SubscriptionName = $workspace.SubscriptionName
223+
SubscriptionId = $workspace.SubscriptionId
224+
WorkspaceName = $workspace.WorkspaceName
225+
ResourceGroup = $workspace.ResourceGroup
226+
WorkspaceId = $workspace.WorkspaceId
227+
UebaPresent = $uebaPresent
228+
DataSources = $dataSources
229+
OnboardDateTime = $onboardDateTime
230+
EntityAnalyticsPresent = $entityAnalyticsPresent
231+
EntityProviders = $entityProviders
232+
RowStatus = $rowStatus
233+
}
234+
}
235+
$workspaceResults = @($workspaceResults)
236+
237+
$failItems = @($workspaceResults | Where-Object { $_.RowStatus -eq 'Fail' })
238+
$investigateItems = @($workspaceResults | Where-Object { $_.RowStatus -eq 'Investigate' })
239+
240+
# Overall result: Fail takes priority over Investigate; Pass only when every workspace passes
241+
# and no workspace had an unresolvable Q3 onboarding error (incomplete scan cannot report clean).
242+
$passed = $failItems.Count -eq 0 -and $investigateItems.Count -eq 0 -and $onboardingErrorWorkspaces.Count -eq 0
243+
$customStatus = $null
244+
245+
if ($failItems.Count -gt 0) {
246+
$testResultMarkdown = "❌ UEBA is not enabled in the Sentinel workspace.`n`n%TestResult%"
247+
}
248+
elseif ($investigateItems.Count -gt 0 -or $onboardingErrorWorkspaces.Count -gt 0) {
249+
$customStatus = 'Investigate'
250+
$testResultMarkdown = "⚠️ The UEBA setting returned an unexpected response, or UEBA is enabled but no supported data sources are configured.`n`n%TestResult%"
251+
}
252+
else {
253+
$testResultMarkdown = "✅ UEBA is enabled in the Sentinel workspace.`n`n%TestResult%"
254+
}
255+
256+
#endregion Assessment Logic
257+
258+
#region Report Generation
259+
260+
$azContext = Get-AzContext -ErrorAction SilentlyContinue
261+
$portalHost = if ($azContext -and $azContext.Environment.Name -eq 'AzureUSGovernment') { 'https://portal.azure.us' } else { 'https://portal.azure.com' }
262+
$portalSentinelLink = "$portalHost/#view/HubsExtension/BrowseResource/resourceType/microsoft.securityinsightsarg%2Fsentinel"
263+
$tableTitle = 'UEBA configuration per workspace'
264+
265+
$formatTemplate = @'
266+
267+
268+
## [{0}]({1})
269+
270+
| Subscription | Workspace | UEBA enabled | Data sources | Onboarded since | Entity analytics | Entity providers | Status |
271+
| :----------- | :-------- | :----------- | :----------- | :-------------- | :--------------- | :--------------- | :----- |
272+
{2}
273+
'@
274+
275+
$tableRows = ''
276+
$maxDisplay = 10
277+
$statusPriority = @{ Fail = 0; Investigate = 1; Pass = 2 }
278+
$displayResults = @($workspaceResults | Sort-Object { $statusPriority[$_.RowStatus] }, SubscriptionName, WorkspaceName)
279+
$hasMoreItems = $false
280+
if ($workspaceResults.Count -gt $maxDisplay) {
281+
$displayResults = @($displayResults | Select-Object -First $maxDisplay)
282+
$hasMoreItems = $true
283+
}
284+
285+
foreach ($result in $displayResults) {
286+
$subLink = "$portalHost/#resource/subscriptions/$($result.SubscriptionId)"
287+
$sentinelId = "/subscriptions/$($result.SubscriptionId)/resourcegroups/$($result.ResourceGroup)/providers/microsoft.securityinsightsarg/sentinel/$($result.WorkspaceName)"
288+
$uebaLink = "$portalHost/#view/Microsoft_Azure_Security_Insights/MainMenuBlade/~/EntityBehavior/id/$($sentinelId -replace '/', '%2F')"
289+
$subMd = "[$(Get-SafeMarkdown $result.SubscriptionName)]($subLink)"
290+
$wsMd = "[$(Get-SafeMarkdown $result.WorkspaceName)]($uebaLink)"
291+
292+
# dataSources and entityProviders are API-controlled enum values — no Get-SafeMarkdown.
293+
# $null fields mean state unknown (API error) → render as '—'; $true/$false → Yes/No.
294+
$uebaEnabledMd = if ($null -eq $result.UebaPresent) { '' } elseif ($result.UebaPresent) { '✅ Yes' } else { '❌ No' }
295+
$dataSourcesMd = if ($result.DataSources.Count -gt 0) { $result.DataSources -join ', ' } else { '' }
296+
$onboardedSinceMd = if ($result.OnboardDateTime) { $result.OnboardDateTime } else { '' }
297+
$eaEnabledMd = if ($null -eq $result.EntityAnalyticsPresent) { '' } elseif ($result.EntityAnalyticsPresent) { '✅ Yes' } else { '❌ No' }
298+
$entityProvidersMd = if ($result.EntityProviders.Count -gt 0) { $result.EntityProviders -join ', ' } else { '' }
299+
$statusDisplay = switch ($result.RowStatus) {
300+
'Pass' { '✅ Pass' }
301+
'Fail' { '❌ Fail' }
302+
'Investigate' { '⚠️ Investigate' }
303+
}
304+
305+
$tableRows += "| $subMd | $wsMd | $uebaEnabledMd | $dataSourcesMd | $onboardedSinceMd | $eaEnabledMd | $entityProvidersMd | $statusDisplay |`n"
306+
}
307+
308+
if ($hasMoreItems) {
309+
$remainingCount = $workspaceResults.Count - $maxDisplay
310+
$tableRows += "`n... and $remainingCount more. [View all in Microsoft Sentinel]($portalSentinelLink)`n"
311+
}
312+
313+
$mdInfo = $formatTemplate -f $tableTitle, $portalSentinelLink, $tableRows
314+
$testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo
315+
316+
#endregion Report Generation
317+
318+
$params = @{
319+
TestId = '41209'
320+
Title = 'User and Entity Behavior Analytics (UEBA) is enabled in Microsoft Sentinel'
321+
Status = $passed
322+
Result = $testResultMarkdown
323+
}
324+
if ($customStatus) {
325+
$params.CustomStatus = $customStatus
326+
}
327+
328+
Add-ZtTestResultDetail @params
329+
}

0 commit comments

Comments
 (0)