tests/Test-Assessment.61023.ps1
|
<#
.SYNOPSIS Checks that the Agent 365 data connector is installed on at least one Microsoft Sentinel-onboarded workspace. .DESCRIPTION Verifies that the A365 Observability solution ("azuresentinel.azure-sentinel-solution-a365observability") is installed from the Content Hub on at least one Microsoft Sentinel-onboarded Log Analytics workspace. The connector brings AI agent telemetry — including agent invocations, tool calls, and grounding source access — into Microsoft Sentinel, enabling detection of prompt-injection and data-exfiltration attacks that occur entirely within the agent execution layer. .NOTES Test ID: 61023 Category: AI Threat Detection Pillar: AI Required API: Azure Resource Manager (management.azure.com) #> function Test-Assessment-61023 { [ZtTest( Category = 'AI Threat Detection', ImplementationCost = 'Low', MinimumLicense = ('AGENT_365', 'Consumption-based: Microsoft Sentinel'), CompatibleLicense = ('AGENT_365'), Service = ('Azure'), Pillar = 'AI', RiskLevel = 'High', SfiPillar = 'Monitor and detect cyberthreats', TenantType = ('Workforce'), TestId = 61023, Title = 'Agent 365 data connector is enabled on the Microsoft Sentinel workspace', UserImpact = 'Low' )] [CmdletBinding()] param() #region Data Collection Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose $activity = 'Checking Agent 365 Content Hub solution on Sentinel workspaces' $a365ContentId = 'azuresentinel.azure-sentinel-solution-a365observability' $a365DisplayName = 'Agent 365' $a365DisplayName2 = 'A365 Observability' # Q1 + Q2 + onboarding check. # Returns 'Forbidden' on ARG 401/403, $null on unexpected failure, # 'NoSubscriptions' / 'NoWorkspaces' when nothing is in scope. $allWorkspaces = Get-SentinelWorkspaceData -Activity $activity if ($null -eq $allWorkspaces) { $params = @{ TestId = '61023' Title = 'Agent 365 data connector is enabled on the Microsoft Sentinel workspace' 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 = '61023' Title = 'Agent 365 data connector is enabled on the Microsoft Sentinel workspace' Status = $false Result = '⚠️ Azure Resource Graph returned insufficient permissions when enumerating subscriptions or workspaces. Ensure you have at least Reader access to the Azure subscriptions being tested.' CustomStatus = 'Investigate' } Add-ZtTestResultDetail @params return } # No enabled subscriptions accessible to the caller. if ($allWorkspaces -eq 'NoSubscriptions') { Write-PSFMessage 'No enabled subscriptions found — skipping Agent 365 connector check.' -Tag Test -Level VeryVerbose Add-ZtTestResultDetail -SkippedBecause NotApplicable return } # No Log Analytics workspaces across accessible subscriptions. if ($allWorkspaces -eq 'NoWorkspaces') { Write-PSFMessage 'No Log Analytics workspaces found across accessible subscriptions — skipping Agent 365 connector check.' -Tag Test -Level VeryVerbose Add-ZtTestResultDetail -SkippedBecause NotApplicable return } $checkableWorkspaces = @($allWorkspaces | Where-Object { -not $_.PermissionError }) $forbiddenWorkspaces = @($allWorkspaces | Where-Object { $_.PermissionError }) $onboardedWorkspaces = @($checkableWorkspaces | Where-Object { $_.SentinelOnboarded }) if ($onboardedWorkspaces.Count -eq 0) { if ($forbiddenWorkspaces.Count -gt 0) { # Auth errors on the Sentinel onboarding check mean we cannot confirm whether # those workspaces are onboarded — we cannot rule out a passing workspace exists. $params = @{ TestId = '61023' Title = 'Agent 365 data connector is enabled on the Microsoft Sentinel workspace' Status = $false Result = '⚠️ One or more Log Analytics workspaces returned 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' } } else { # Full visibility, no workspace has Sentinel onboarded. # Full visibility, no workspace has Sentinel onboarded — nothing to evaluate. $params = @{ TestId = '61023' Title = 'Agent 365 data connector is enabled on the Microsoft Sentinel workspace' Status = $false Result = '❌ No Sentinel-onboarded workspace found in tenant.' } } Add-ZtTestResultDetail @params return } $workspaceResults = @() foreach ($workspace in $onboardedWorkspaces) { $packageName = 'Not found' $rowStatus = 'Fail' # Q3: List installed Content Hub solutions and look for the Agent 365 / A365 Observability package. Write-ZtProgress -Activity $activity -Status "Checking content packages on $($workspace.WorkspaceName) in subscription $($workspace.SubscriptionName)" $packagesPath = "$($workspace.WorkspaceId)/providers/Microsoft.SecurityInsights/contentPackages?api-version=2024-09-01" try { $packages = @(Invoke-ZtAzureRequest -Path $packagesPath -ErrorAction Stop) $a365Package = $packages | Where-Object { $_.properties.contentId -eq $a365ContentId -or ($_.properties.displayName -and ( $_.properties.displayName -ieq $a365DisplayName -or $_.properties.displayName -ieq $a365DisplayName2 )) } | Select-Object -First 1 if ($a365Package) { $packageName = $a365Package.properties.displayName $rowStatus = 'Pass' } } catch { $packageName = 'Error' $rowStatus = 'Investigate' Write-PSFMessage "Error querying content packages for workspace '$($workspace.WorkspaceName)' in subscription '$($workspace.SubscriptionName)': $_" -Tag Test -Level Warning } $workspaceResults += [PSCustomObject]@{ SubscriptionName = $workspace.SubscriptionName SubscriptionId = $workspace.SubscriptionId WorkspaceName = $workspace.WorkspaceName ResourceGroup = $workspace.ResourceGroup WorkspaceId = $workspace.WorkspaceId PackageName = $packageName RowStatus = $rowStatus } } #endregion Data Collection #region Assessment Logic $passedItems = @($workspaceResults | Where-Object { $_.RowStatus -eq 'Pass' }) $investigateItems = @($workspaceResults | Where-Object { $_.RowStatus -eq 'Investigate' }) $passed = $passedItems.Count -gt 0 $customStatus = $null if (-not $passed -and $investigateItems.Count -gt 0) { $customStatus = 'Investigate' $testResultMarkdown = "⚠️ The Content Hub API returned an authorization (401/403) or transient (5xx) error on at least one workspace, so the Agent 365 connector state could not be determined for those workspaces. Re-run after verifying Microsoft Sentinel Reader on each affected workspace's resource group.`n`n%TestResult%" } elseif ($passed) { $testResultMarkdown = "✅ The Agent 365 data connector is installed and connected on at least one Sentinel-onboarded workspace.`n`n%TestResult%" } else { $testResultMarkdown = "❌ The Agent 365 connector is not installed on any Sentinel-onboarded workspace. Install the solution from the Microsoft Sentinel Content Hub to gain visibility into AI agent activity.`n`n%TestResult%" } #endregion Assessment Logic #region Report Generation $portalSentinelLink = 'https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/microsoft.securityinsightsarg%2Fsentinel' $tableTitle = 'Sentinel data connectors per workspace' $formatTemplate = @' ## [{0}]({1}) | Subscription | Workspace | Content package | 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 = "https://portal.azure.com/#resource/subscriptions/$($result.SubscriptionId)" $sentinelId = "/subscriptions/$($result.SubscriptionId)/resourcegroups/$($result.ResourceGroup)/providers/microsoft.securityinsightsarg/sentinel/$($result.WorkspaceName)" $workspaceLink = "https://portal.azure.com/#view/Microsoft_Azure_Security_Insights/MainMenuBlade/~/DataConnectors/id/$($sentinelId -replace '/', '%2F')" $subMd = "[$(Get-SafeMarkdown $result.SubscriptionName)]($subLink)" $workspaceMd = "[$(Get-SafeMarkdown $result.WorkspaceName)]($workspaceLink)" $packageMd = $result.PackageName $statusDisplay = switch ($result.RowStatus) { 'Pass' { '✅ Pass' } 'Fail' { '❌ Fail' } 'Investigate' { '⚠️ Investigate' } } $tableRows += "| $subMd | $workspaceMd | $packageMd | $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 = '61023' Title = 'Agent 365 data connector is enabled on the Microsoft Sentinel workspace' Status = $passed Result = $testResultMarkdown } if ($customStatus) { $params.CustomStatus = $customStatus } Add-ZtTestResultDetail @params } |