Skip to content

Commit 53248f3

Browse files
committed
Feat: add MDO/Email & collaboration alerts API
simpler Refactor API request in Invoke-ExecOffice365AlertsList to simplify URI construction Add Invoke-ExecSetOffice365Alert function add AllTenants support rename to MDO
1 parent 72aba48 commit 53248f3

File tree

3 files changed

+206
-0
lines changed

3 files changed

+206
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
function Push-ExecMdoAlertsListAllTenants {
2+
<#
3+
.FUNCTIONALITY
4+
Entrypoint
5+
#>
6+
param($Item)
7+
8+
$Tenant = Get-Tenants -TenantFilter $Item.customerId
9+
$domainName = $Tenant.defaultDomainName
10+
$Table = Get-CIPPTable -TableName 'cachealertsandincidents'
11+
12+
try {
13+
# Get MDO alerts using the specific endpoint and filter
14+
$Alerts = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/security/alerts_v2?`$filter=serviceSource eq 'microsoftDefenderForOffice365'" -tenantid $domainName
15+
16+
foreach ($Alert in $Alerts) {
17+
$GUID = (New-Guid).Guid
18+
$GraphRequest = @{
19+
MdoAlert = [string]($Alert | ConvertTo-Json -Depth 10)
20+
RowKey = [string]$GUID
21+
PartitionKey = 'MdoAlert'
22+
Tenant = [string]$domainName
23+
}
24+
Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null
25+
}
26+
27+
} catch {
28+
$GUID = (New-Guid).Guid
29+
$AlertText = ConvertTo-Json -InputObject @{
30+
Tenant = $domainName
31+
displayName = "Could not connect to Tenant: $($_.Exception.Message)"
32+
id = ''
33+
severity = 'CIPP'
34+
status = 'Failed'
35+
createdDateTime = (Get-Date).ToString('s')
36+
category = 'Unknown'
37+
description = 'Could not connect'
38+
serviceSource = 'microsoftDefenderForOffice365'
39+
}
40+
$GraphRequest = @{
41+
MdoAlert = [string]$AlertText
42+
RowKey = [string]$GUID
43+
PartitionKey = 'MdoAlert'
44+
Tenant = [string]$domainName
45+
}
46+
Add-CIPPAzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null
47+
}
48+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using namespace System.Net
2+
3+
function Invoke-ExecMDOAlertsList {
4+
<#
5+
.FUNCTIONALITY
6+
Entrypoint
7+
.ROLE
8+
Security.Alert.Read
9+
#>
10+
[CmdletBinding()]
11+
param($Request, $TriggerMetadata)
12+
13+
$APIName = $Request.Params.CIPPEndpoint
14+
$Headers = $Request.Headers
15+
Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug'
16+
17+
# Interact with query parameters or the body of the request.
18+
$TenantFilter = $Request.Query.tenantFilter
19+
20+
try {
21+
$GraphRequest = if ($TenantFilter -ne 'AllTenants') {
22+
# Single tenant functionality
23+
New-GraphGetRequest -uri "https://graph.microsoft.com/beta/security/alerts_v2?`$filter=serviceSource eq 'microsoftDefenderForOffice365'" -tenantid $TenantFilter
24+
} else {
25+
# AllTenants functionality
26+
$Table = Get-CIPPTable -TableName cachealertsandincidents
27+
$PartitionKey = 'MdoAlert'
28+
$Filter = "PartitionKey eq '$PartitionKey'"
29+
$Rows = Get-CIPPAzDataTableEntity @Table -filter $Filter | Where-Object -Property Timestamp -GT (Get-Date).AddMinutes(-30)
30+
$QueueReference = '{0}-{1}' -f $TenantFilter, $PartitionKey
31+
$RunningQueue = Invoke-ListCippQueue -Reference $QueueReference | Where-Object { $_.Status -notmatch 'Completed' -and $_.Status -notmatch 'Failed' }
32+
# If a queue is running, we will not start a new one
33+
if ($RunningQueue) {
34+
$Metadata = [PSCustomObject]@{
35+
QueueMessage = 'Still loading data for all tenants. Please check back in a few more minutes'
36+
QueueId = $RunningQueue.RowKey
37+
}
38+
} elseif (!$Rows -and !$RunningQueue) {
39+
# If no rows are found and no queue is running, we will start a new one
40+
$TenantList = Get-Tenants -IncludeErrors
41+
$Queue = New-CippQueueEntry -Name 'MDO Alerts - All Tenants' -Link '/security/reports/mdo-alerts?customerId=AllTenants' -Reference $QueueReference -TotalTasks ($TenantList | Measure-Object).Count
42+
$Metadata = [PSCustomObject]@{
43+
QueueMessage = 'Loading data for all tenants. Please check back in a few minutes'
44+
QueueId = $Queue.RowKey
45+
}
46+
$InputObject = [PSCustomObject]@{
47+
OrchestratorName = 'MdoAlertsOrchestrator'
48+
QueueFunction = @{
49+
FunctionName = 'GetTenants'
50+
QueueId = $Queue.RowKey
51+
TenantParams = @{
52+
IncludeErrors = $true
53+
}
54+
DurableName = 'ExecMdoAlertsListAllTenants'
55+
}
56+
SkipLog = $true
57+
}
58+
Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) | Out-Null
59+
} else {
60+
$Metadata = [PSCustomObject]@{
61+
QueueId = $RunningQueue.RowKey ?? $null
62+
}
63+
$Alerts = $Rows
64+
foreach ($alert in $Alerts) {
65+
ConvertFrom-Json -InputObject $alert.MdoAlert -Depth 10
66+
}
67+
}
68+
}
69+
} catch {
70+
$Body = Get-NormalizedError -Message $_.Exception.Message
71+
$StatusCode = [HttpStatusCode]::Forbidden
72+
}
73+
if (!$Body) {
74+
$StatusCode = [HttpStatusCode]::OK
75+
$Body = [PSCustomObject]@{
76+
Results = @($GraphRequest)
77+
Metadata = $Metadata
78+
}
79+
}
80+
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
81+
StatusCode = $StatusCode
82+
Body = $Body
83+
})
84+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using namespace System.Net
2+
3+
function Invoke-ExecSetMdoAlert {
4+
<#
5+
.FUNCTIONALITY
6+
Entrypoint
7+
.ROLE
8+
Security.Incident.ReadWrite
9+
#>
10+
[CmdletBinding()]
11+
param($Request, $TriggerMetadata)
12+
13+
$APIName = $Request.Params.CIPPEndpoint
14+
$Headers = $Request.Headers
15+
Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug'
16+
17+
# Interact with query parameters or the body of the request.
18+
$TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter
19+
$AlertId = $Request.Query.GUID ?? $Request.Body.GUID
20+
$Status = $Request.Query.Status ?? $Request.Body.Status
21+
$Assigned = $Request.Query.Assigned ?? $Request.Body.Assigned ?? ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Headers.'x-ms-client-principal')) | ConvertFrom-Json).userDetails
22+
$Classification = $Request.Query.Classification ?? $Request.Body.Classification
23+
$Determination = $Request.Query.Determination ?? $Request.Body.Determination
24+
$Result = ''
25+
$AssignBody = @{}
26+
27+
try {
28+
# Set received status
29+
if ($null -ne $Status) {
30+
$AssignBody.status = $Status
31+
$Result += 'Set status for incident ' + $AlertId + ' to ' + $Status
32+
}
33+
34+
# Set received classification and determination
35+
if ($null -ne $Classification) {
36+
if ($null -eq $Determination) {
37+
# Maybe some poindexter tries to send a classification without a determination
38+
throw
39+
}
40+
41+
$AssignBody.classification = $Classification
42+
$AssignBody.determination = $Determination
43+
$Result += 'Set classification & determination for incident ' + $AlertId + ' to ' + $Classification + ' ' + $Determination
44+
}
45+
46+
# Set received assignee
47+
if ($null -ne $Assigned) {
48+
$AssignBody.assignedTo = $Assigned
49+
if ($null -eq $Status) {
50+
$Result += 'Set assigned for incident ' + $AlertId + ' to ' + $Assigned
51+
}
52+
}
53+
54+
# Convert hashtable to JSON
55+
$AssignBodyJson = $AssignBody | ConvertTo-Json -Compress
56+
57+
$null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/security/alerts_v2/$AlertId" -type PATCH -tenantid $TenantFilter -body $AssignBodyJson -asApp $true
58+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantFilter -message $Result -Sev 'Info'
59+
60+
$StatusCode = [HttpStatusCode]::OK
61+
} catch {
62+
$ErrorMessage = Get-CippException -Exception $_
63+
$Result = "Failed to update incident $AlertId : $($ErrorMessage.NormalizedError)"
64+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantFilter -message $Result -Sev 'Error' -LogData $ErrorMessage
65+
$StatusCode = [HttpStatusCode]::InternalServerError
66+
}
67+
68+
# Associate values to output bindings by calling 'Push-OutputBinding'.
69+
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
70+
StatusCode = $StatusCode
71+
Body = @{'Results' = $Result }
72+
})
73+
74+
}

0 commit comments

Comments
 (0)