Scripts/Get-SecurityAlerts.ps1
Function Get-SecurityAlerts { <# .SYNOPSIS Retrieves security alerts. .DESCRIPTION Retrieves security alerts from Microsoft Graph, choosing between Get-MgSecurityAlert and Get-MgSecurityAlertV2 based on the authentication type used. .PARAMETER OutputDir OutputDir is the parameter specifying the output directory. Default: Output\SecurityAlerts .PARAMETER Encoding Encoding is the parameter specifying the encoding of the CSV output file. Default: UTF8 .PARAMETER LogLevel Specifies the level of logging: None: No logging Minimal: Critical errors only Standard: Normal operational logging Default: Standard .PARAMETER AlertId AlertId is the parameter specifying a specific alert ID to retrieve. Default: All alerts will be retrieved if not specified. .PARAMETER DaysBack Number of days to look back for alerts. Default: 90 .PARAMETER Filter Custom filter string to apply to the alert retrieval. Default: None .EXAMPLE Get-SecurityAlerts Retrieves all security alerts from the past 30 days. .EXAMPLE Get-SecurityAlerts -AlertId "123456-abcdef-7890" Retrieves a specific security alert by ID. .EXAMPLE Get-SecurityAlerts -DaysBack 7 Retrieves security alerts from the past 7 days. .EXAMPLE Get-SecurityAlerts -Filter "severity eq 'high'" Retrieves high severity security alerts. #> [CmdletBinding()] param( [string]$OutputDir = "Output\SecurityAlerts", [string]$Encoding = "UTF8", [string]$AlertId, [int]$DaysBack = 90, [string]$Filter, [ValidateSet('None', 'Minimal', 'Standard')] [string]$LogLevel = 'Standard' ) Set-LogLevel -Level ([LogLevel]::$LogLevel) $date = Get-Date -Format "yyyyMMddHHmm" Write-LogFile -Message "=== Starting Security Alerts Collection ===" -Color "Cyan" -Level Standard $requiredScopes = @("SecurityEvents.Read.All") $graphAuth = Get-GraphAuthType -RequiredScopes $RequiredScopes if (!(Test-Path $OutputDir)) { New-Item -ItemType Directory -Force -Path $OutputDir > $null } else { if (!(Test-Path -Path $OutputDir)) { Write-Error "[Error] Custom directory invalid: $OutputDir exiting script" -ErrorAction Stop Write-LogFile -Message "[Error] Custom directory invalid: $OutputDir exiting script" -Level Minimal } } Write-LogFile -Message "[INFO] Analyzing security alerts..." -Level Standard try { # Choose the appropriate cmdlet based on auth type if ($graphAuth.Type -eq "Application") { Write-LogFile -Message "[INFO] Using application authentication - selecting Get-MgSecurityAlertV2" -Level Standard $cmdlet = "Get-MgSecurityAlertV2" } else { Write-LogFile -Message "[INFO] Using delegated authentication - selecting Get-MgSecurityAlert" -Level Standard $cmdlet = "Get-MgSecurityAlert" } $params = @{} if ($AlertId) { Write-LogFile -Message "[INFO] Retrieving specific alert: $AlertId" -Level Standard $params.Add("AlertId", $AlertId) } else { if ($DaysBack -gt 0) { $startDate = (Get-Date).AddDays(-$DaysBack).ToString("yyyy-MM-ddT00:00:00Z") if ($Filter) { $timeFilter = "createdDateTime ge $startDate" $params.Add("Filter", "($Filter) and ($timeFilter)") Write-LogFile -Message "[INFO] Using combined filter: $($params.Filter)" -Level Standard } else { $params.Add("Filter", "createdDateTime ge $startDate") Write-LogFile -Message "[INFO] Filtering alerts from $startDate" -Level Standard } } elseif ($Filter) { $params.Add("Filter", $Filter) Write-LogFile -Message "[INFO] Using custom filter: $Filter" -Level Standard } $params.Add("All", $true) } if ($cmdlet -eq "Get-MgSecurityAlert") { if ($AlertId) { $alerts = Get-MgSecurityAlert -AlertId $AlertId } else { $alerts = Get-MgSecurityAlert @params } } else { if ($AlertId) { $alerts = Get-MgSecurityAlertV2 -AlertId $AlertId } else { $alerts = Get-MgSecurityAlertV2 @params } } $alertSummary = @{ TotalAlerts = 0 SeverityHigh = 0 SeverityMedium = 0 SeverityLow = 0 SeverityInformational = 0 StatusNew = 0 StatusInProgress = 0 StatusResolved = 0 StatusDismissed = 0 StatusUnknown = 0 } $formattedAlerts = $alerts | ForEach-Object { $alertSummary.TotalAlerts++ switch ($_.Severity) { "high" { $alertSummary.SeverityHigh++ } "medium" { $alertSummary.SeverityMedium++ } "low" { $alertSummary.SeverityLow++ } "informational" { $alertSummary.SeverityInformational++ } } switch ($_.Status) { "new" { $alertSummary.StatusNew++ } "inProgress" { $alertSummary.StatusInProgress++ } "resolved" { $alertSummary.StatusResolved++ } "dismissed" { $alertSummary.StatusDismissed++ } default { $alertSummary.StatusUnknown++ } } # Extract affected users, handling both null and populated UserStates $affectedUsers = "" if ($_.UserStates -and $_.UserStates.Count -gt 0) { $userDetails = @() foreach ($userState in $_.UserStates) { if ($userState.UserPrincipalName) { $userInfo = $userState.UserPrincipalName if ($userState.LogonIP) { $userInfo += "/$($userState.LogonIP)" } else { $userInfo += "/null" } $userDetails += $userInfo } elseif ($userState.Name) { $userDetails += "$($userState.Name)/null" } } $affectedUsers = $userDetails -join "; " } $affectedHosts = "" if ($_.HostStates -and $_.HostStates.Count -gt 0) { $hostDetails = @() foreach ($hostState in $_.HostStates) { $hostInfo = "" if ($hostState.NetBiosName) { $hostInfo = $hostState.NetBiosName } elseif ($hostState.PrivateHostName) { $hostInfo = $hostState.PrivateHostName } else { $hostInfo = "Unknown" } if ($hostState.PrivateIpAddress) { $hostInfo += "/$($hostState.PrivateIpAddress)" } else { $hostInfo += "/null" } $hostDetails += $hostInfo } $affectedHosts = $hostDetails -join "; " } $sourceURLs = ($_.SourceMaterials) -join "; " $cloudApps = ($_.CloudAppStates | ForEach-Object { "$($_.Name): $($_.InstanceName)" }) -join "; " $comments = ($_.Comments | ForEach-Object { if ($_.CreatedBy.User.DisplayName) { "$($_.Comment) - $($_.CreatedBy.User.DisplayName)" } else { $_.Comment } }) -join "; " [PSCustomObject]@{ Id = $_.Id Title = $_.Title Category = $_.Category Severity = $_.Severity Status = $_.Status CreatedDateTime = $_.CreatedDateTime EventDateTime = $_.EventDateTime LastModifiedDateTime = $_.LastModifiedDateTime AssignedTo = $_.AssignedTo Description = $_.Description DetectionSource = $_.DetectionSource AffectedUser = $affectedUsers AffectedHost = $affectedHosts AzureTenantId = $_.AzureTenantId AzureSubscriptionId = $_.AzureSubscriptionId Confidence = $_.Confidence ActivityGroupName = $_.ActivityGroupName ClosedDateTime = $_.ClosedDateTime Feedback = $_.Feedback LastEventDateTime = $_.LastEventDateTime SourceURL = $sourceURLs CloudAppStates = $cloudApps Comments = $comments Tags = ($_.Tags -join ", ") Vendor = $_.VendorInformation.Vendor Provider = $_.VendorInformation.Provider SubProvider = $_.VendorInformation.SubProvider ProviderVersion = $_.VendorInformation.ProviderVersion IncidentIds = ($_.IncidentIds -join ", ") } } $filePath = "$OutputDir\$($date)-SecurityAlerts.csv" $formattedAlerts | Export-Csv -Path $filePath -NoTypeInformation -Encoding $Encoding Write-LogFile -Message "`nSecurity Alert Analysis Results:" -Color "Cyan" -Level Standard Write-LogFile -Message "Total Alerts: $($alertSummary.TotalAlerts)" -Level Standard Write-LogFile -Message "`nSeverity Distribution:" -Level Standard Write-LogFile -Message " - High: $($alertSummary.SeverityHigh)" -Level Standard Write-LogFile -Message " - Medium: $($alertSummary.SeverityMedium)" -Level Standard Write-LogFile -Message " - Low: $($alertSummary.SeverityLow)" -Level Standard Write-LogFile -Message " - Informational: $($alertSummary.SeverityInformational)" -Level Standard Write-LogFile -Message "`nStatus Distribution:" -Level Standard Write-LogFile -Message " - New: $($alertSummary.StatusNew)" -Level Standard Write-LogFile -Message " - In Progress: $($alertSummary.StatusInProgress)" -Level Standard Write-LogFile -Message " - Resolved: $($alertSummary.StatusResolved)" -Level Standard Write-LogFile -Message " - Dismissed: $($alertSummary.StatusDismissed)" -Level Standard Write-LogFile -Message " - Unknown: $($alertSummary.StatusUnknown)" -Level Standard Write-LogFile -Message "`nExported File:" -Color "Cyan" -Level Standard Write-LogFile -Message " - File: $filePath" -Level Standard } catch { Write-LogFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" -Level Minimal throw } } |