Scripts/Get-RiskyEvents.ps1

function Get-RiskyUsers {
<#
    .SYNOPSIS
    Retrieves the risky users.
 
    .DESCRIPTION
    Retrieves the risky users from the Entra ID Identity Protection, which marks an account as being at risk based on the pattern of activity for the account.
 
    .PARAMETER OutputDir
    OutputDir is the parameter specifying the output directory.
    Default: Output\RiskyEvents
 
    .PARAMETER Encoding
    Encoding is the parameter specifying the encoding of the CSV output file.
    Default: UTF8
 
    .PARAMETER UserIds
    An array of User IDs to retrieve risky user information for.
    If not specified, retrieves all risky users.
 
    .PARAMETER LogLevel
    Specifies the level of logging:
    None: No logging
    Minimal: Critical errors only
    Standard: Normal operational logging
    Debug: Verbose logging for debugging purposes
    Default: Standard
     
    .EXAMPLE
    Get-RiskyUsers
    Retrieves all risky users.
     
    .EXAMPLE
    Get-RiskyUsers -Encoding utf32
    Retrieves all risky users and exports the output to a CSV file with UTF-32 encoding.
         
    .EXAMPLE
    Get-RiskyUsers -OutputDir C:\Windows\Temp
    Retrieves all risky users and saves the output to the C:\Windows\Temp folder.
 
    .EXAMPLE
    Get-RiskyUsers -UserIds "user-id-1","user-id-2"
    Retrieves risky user information for the specified User IDs.
#>

    [CmdletBinding()]
    param(
        [string]$OutputDir = "Output\RiskyEvents",
        [string]$Encoding = "UTF8",
        [string[]]$UserIds,
        [ValidateSet('None', 'Minimal', 'Standard', 'Debug')]
        [string]$LogLevel = 'Standard'
    )

    Set-LogLevel -Level ([LogLevel]::$LogLevel)
    $isDebugEnabled = $script:LogLevel -eq [LogLevel]::Debug

    Write-LogFile -Message "=== Starting Risky Users Collection ===" -Color "Cyan" -Level Standard

    if ($isDebugEnabled) {
        Write-LogFile -Message "[DEBUG] PowerShell Version: $($PSVersionTable.PSVersion)" -Level Debug
        Write-LogFile -Message "[DEBUG] Input parameters:" -Level Debug
        Write-LogFile -Message "[DEBUG] OutputDir: '$OutputDir'" -Level Debug
        Write-LogFile -Message "[DEBUG] Encoding: '$Encoding'" -Level Debug
        Write-LogFile -Message "[DEBUG] UserIds: '$($UserIds -join ', ')'" -Level Debug
        Write-LogFile -Message "[DEBUG] UserIds count: $($UserIds.Count)" -Level Debug
        Write-LogFile -Message "[DEBUG] LogLevel: '$LogLevel'" -Level Debug
        
        $graphModules = Get-Module -Name Microsoft.Graph* -ErrorAction SilentlyContinue
        if ($graphModules) {
            Write-LogFile -Message "[DEBUG] Microsoft Graph Modules loaded:" -Level Debug
            foreach ($module in $graphModules) {
                Write-LogFile -Message "[DEBUG] - $($module.Name) v$($module.Version)" -Level Debug
            }
        } else {
            Write-LogFile -Message "[DEBUG] No Microsoft Graph modules loaded" -Level Debug
        }
    }

    $requiredScopes = @("IdentityRiskEvent.Read.All","IdentityRiskyUser.Read.All")
    $graphAuth = Get-GraphAuthType -RequiredScopes $RequiredScopes

    if ($isDebugEnabled) {
        Write-LogFile -Message "[DEBUG] Graph authentication details:" -Level Debug
        Write-LogFile -Message "[DEBUG] Required scopes: $($requiredScopes -join ', ')" -Level Debug
        Write-LogFile -Message "[DEBUG] Authentication type: $($graphAuth.AuthType)" -Level Debug
        Write-LogFile -Message "[DEBUG] Current scopes: $($graphAuth.Scopes -join ', ')" -Level Debug
        if ($graphAuth.MissingScopes.Count -gt 0) {
            Write-LogFile -Message "[DEBUG] Missing scopes: $($graphAuth.MissingScopes -join ', ')" -Level Debug
        } else {
            Write-LogFile -Message "[DEBUG] Missing scopes: None" -Level Debug
        }
    }
    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
        }
    }

    $results = @()
    $count = 0
    $riskSummary = @{
        High = 0
        Medium = 0
        Low = 0
        None = 0
        AtRisk = 0
        NotAtRisk = 0
        Remediated = 0
        Dismissed = 0
    }
    
    try {
        $results = @()
        $baseUri = "https://graph.microsoft.com/v1.0/identityProtection/riskyUsers"

        if ($UserIds) {
            if ($isDebugEnabled) {
                Write-LogFile -Message "[DEBUG] Processing scenario: Specific users" -Level Debug
                Write-LogFile -Message "[DEBUG] Users to process: $($UserIds -join ', ')" -Level Debug
            }
            foreach ($userId in $UserIds) {
                $encodedUserId = [System.Web.HttpUtility]::UrlEncode($userId)
                $uri = "$baseUri`?`$filter=userPrincipalName eq '$encodedUserId'"
                Write-LogFile -Message "[INFO] Retrieving risky user for UPN: $userId" -Level Standard
        
                try {
                    $response = Invoke-MgGraphRequest -Method GET -Uri $uri
        
                    if ($isDebugEnabled) {
                        Write-LogFile -Message "[DEBUG] Response received:" -Level Debug
                        Write-LogFile -Message "[DEBUG] Value count: $($response.value.Count)" -Level Debug
                        Write-LogFile -Message "[DEBUG] Has @odata.nextLink: $($null -ne $response.'@odata.nextLink')" -Level Debug
                    }
        
                    if ($response.value -and $response.value.Count -gt 0) {
                        if ($isDebugEnabled) {
                            Write-LogFile -Message "[DEBUG] Found $($response.value.Count) risky user records" -Level Debug
                        }
                        foreach ($user in $response.value) {
                            if ($isDebugEnabled) {
                                $userIdentifier = if ([string]::IsNullOrEmpty($user.UserPrincipalName)) {
                                    if (![string]::IsNullOrEmpty($user.UserDisplayName)) {
                                        "DisplayName: $($user.UserDisplayName)"
                                    } elseif (![string]::IsNullOrEmpty($user.Id)) {
                                        "ID: $($user.Id)"
                                    } else {
                                        "[Unknown User]"
                                    }
                                } else {
                                    $user.UserPrincipalName
                                }
                                Write-LogFile -Message "[DEBUG] Processing user record:" -Level Debug
                                Write-LogFile -Message "[DEBUG] User: $userIdentifier" -Level Debug
                                Write-LogFile -Message "[DEBUG] Risk Level: $($user.RiskLevel)" -Level Debug
                                Write-LogFile -Message "[DEBUG] Risk State: $($user.RiskState)" -Level Debug
                                Write-LogFile -Message "[DEBUG] Risk Detail: $($user.RiskDetail)" -Level Debug
                                Write-LogFile -Message "[DEBUG] Last Updated: $($user.RiskLastUpdatedDateTime)" -Level Debug
                            }
                            $results += [PSCustomObject]@{
                                Id                          = $user.Id
                                IsDeleted                   = $user.IsDeleted
                                IsProcessing                = $user.IsProcessing
                                RiskDetail                  = $user.RiskDetail
                                RiskLastUpdatedDateTime     = $user.RiskLastUpdatedDateTime
                                RiskLevel                   = $user.RiskLevel
                                RiskState                   = $user.RiskState
                                UserDisplayName             = $user.UserDisplayName
                                UserPrincipalName           = $user.UserPrincipalName
                                AdditionalProperties = $user.AdditionalProperties -join ", "
                            }
                            
                            if ($user.RiskLevel) { $riskSummary[$user.RiskLevel]++ }
                            if ($user.RiskState -eq "atRisk") { $riskSummary.AtRisk++ }
                            elseif ($user.RiskState -eq "notAtRisk") { $riskSummary.NotAtRisk++ }
                            elseif ($user.RiskState -eq "remediated") { $riskSummary.Remediated++ }
                            elseif ($user.RiskState -eq "dismissed") { $riskSummary.Dismissed++ }
                            $count++

                            if ($isDebugEnabled) {
                                Write-LogFile -Message "[DEBUG] Updated summary counts - Total: $count, RiskLevel: $($user.RiskLevel), RiskState: $($user.RiskState)" -Level Debug
                            }
                        }
                    } else {
                        Write-LogFile -Message "[INFO] User ID $userId not found or not risky." -Level Standard
                    }
                } catch {
                    Write-LogFile -Message "[ERROR] Failed to retrieve data for User ID $userId : $($_.Exception.Message)" -Color "Red" -Level Minimal
                    if ($isDebugEnabled) {
                        Write-LogFile -Message "[DEBUG] Error details for user $userId :" -Level Debug
                        Write-LogFile -Message "[DEBUG] Exception type: $($_.Exception.GetType().Name)" -Level Debug
                        Write-LogFile -Message "[DEBUG] Error message: $($_.Exception.Message)" -Level Debug
                        Write-LogFile -Message "[DEBUG] Stack trace: $($_.ScriptStackTrace)" -Level Debug
                    }
                }
            }
        }
        else {
            $uri = "https://graph.microsoft.com/v1.0/identityProtection/riskyUsers"
            if ($isDebugEnabled) {
                Write-LogFile -Message "[DEBUG] Processing scenario: All risky users" -Level Debug
                Write-LogFile -Message "[DEBUG] Base URI: $uri" -Level Debug
            }
            do {
                $response = Invoke-MgGraphRequest -Method GET -Uri $uri
                if ($isDebugEnabled) {
                    Write-LogFile -Message "[DEBUG] Value count: $($response.value.Count)" -Level Debug
                    Write-LogFile -Message "[DEBUG] Has @odata.nextLink: $($null -ne $response.'@odata.nextLink')" -Level Debug
                }

                if ($response.value) {
                    if ($isDebugEnabled) {
                        Write-LogFile -Message "[DEBUG] Processing $($response.value.Count) users from page $pageCount" -Level Debug
                    }
                    foreach ($user in $response.value) {
                        if ($isDebugEnabled) {
                            $userIdentifier = if ([string]::IsNullOrEmpty($user.UserPrincipalName)) {
                                if (![string]::IsNullOrEmpty($user.UserDisplayName)) {
                                    "DisplayName: $($user.UserDisplayName)"
                                } elseif (![string]::IsNullOrEmpty($user.Id)) {
                                    "ID: $($user.Id)"
                                } else {
                                    "[Unknown User]"
                                }
                            } else {
                                $user.UserPrincipalName
                            }
                            Write-LogFile -Message "[DEBUG] Processing user: $userIdentifier" -Level Debug
                            Write-LogFile -Message "[DEBUG] Risk Level: $($user.RiskLevel)" -Level Debug
                            Write-LogFile -Message "[DEBUG] Risk State: $($user.RiskState)" -Level Debug
                        }
                        $results += [PSCustomObject]@{
                            Id                          = $user.Id
                            IsDeleted                   = $user.IsDeleted
                            IsProcessing                = $user.IsProcessing
                            RiskDetail                  = $user.RiskDetail
                            RiskLastUpdatedDateTime     = $user.RiskLastUpdatedDateTime
                            RiskLevel                   = $user.RiskLevel
                            RiskState                   = $user.RiskState
                            UserDisplayName             = $user.UserDisplayName
                            UserPrincipalName           = $user.UserPrincipalName
                            AdditionalProperties        = $user.AdditionalProperties -join ", "
                        }

                        if ($user.RiskLevel) { $riskSummary[$user.RiskLevel]++ }
                        if ($user.RiskState -eq "atRisk") { $riskSummary.AtRisk++ }
                        elseif ($user.RiskState -eq "confirmedSafe") { $riskSummary.NotAtRisk++ }
                        elseif ($user.RiskState -eq "remediated") { $riskSummary.Remediated++ }
                        elseif ($user.RiskState -eq "dismissed") { $riskSummary.Dismissed++ }
                        $count++
                    }
                }
                $uri = $response.'@odata.nextLink'
            } while ($uri -ne $null)
        }
    } catch {
        Write-LogFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" -Level Minimal
        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Error details:" -Level Debug
            Write-LogFile -Message "[DEBUG] Exception type: $($_.Exception.GetType().Name)" -Level Debug
            Write-LogFile -Message "[DEBUG] Error message: $($_.Exception.Message)" -Level Debug
            Write-LogFile -Message "[DEBUG] Stack trace: $($_.ScriptStackTrace)" -Level Debug
        }
        throw
    }

    $date = Get-Date -Format "yyyyMMddHHmm"
    $filePath = "$OutputDir\$($date)-RiskyUsers.csv"

    if ($results.Count -gt 0) {
        $results | Export-Csv -Path $filePath -NoTypeInformation -Encoding $Encoding
        Write-LogFile -Message "[INFO] A total of $count Risky Users found" -Level Standard
        
        Write-LogFile -Message "`nSummary of Risky Users:" -Color "Cyan" -Level Standard 
        Write-LogFile -Message "----------------------------------------" -Level Standard
        Write-LogFile -Message "Total Risky Users: $count" -Level Standard
        Write-LogFile -Message " - High Risk: $($riskSummary.High)" -Level Standard
        Write-LogFile -Message " - Medium Risk: $($riskSummary.Medium)" -Level Standard
        Write-LogFile -Message " - Low Risk: $($riskSummary.Low)" -Level Standard

        Write-LogFile -Message "`nRisk States:" -Level Standard
        Write-LogFile -Message " - At Risk: $($riskSummary.AtRisk)" -Level Standard
        Write-LogFile -Message " - Confirmed Safe: $($riskSummary.NotAtRisk)" -Level Standard
        Write-LogFile -Message " - Remediated: $($riskSummary.Remediated)" -Level Standard
        Write-LogFile -Message " - Dismissed: $($riskSummary.Dismissed)" -Level Standard

        Write-LogFile -Message "`nExported Files:" -Level Standard
        Write-LogFile -Message " - $filePath" -Level Standard
        } else {
        Write-LogFile -Message "[INFO] No Risky Users found" -Color "Yellow" -Level Standard
    }
}

function Get-RiskyDetections {
<#
    .SYNOPSIS
    Retrieves the risky detections from the Entra ID Identity Protection.
 
    .DESCRIPTION
    Retrieves the risky detections from the Entra ID Identity Protection.
 
    .PARAMETER OutputDir
    OutputDir is the parameter specifying the output directory.
    Default: Output\RiskyEvents
 
    .PARAMETER Encoding
    Encoding is the parameter specifying the encoding of the CSV output file.
    Default: UTF8
 
    .PARAMETER UserIds
    An array of User IDs to retrieve risky detections information for.
    If not specified, retrieves all risky detections.
 
    .PARAMETER LogLevel
    Specifies the level of logging:
    None: No logging
    Minimal: Critical errors only
    Standard: Normal operational logging
    Debug: Verbose logging for debugging purposes
    Default: Standard
         
    .EXAMPLE
    Get-RiskyDetections
    Retrieves all the risky detections.
     
    .EXAMPLE
    Get-RiskyDetections -Encoding utf32
    Retrieves the risky detections and exports the output to a CSV file with UTF-32 encoding.
         
    .EXAMPLE
    Get-RiskyDetections -OutputDir C:\Windows\Temp
    Retrieves the risky detections and saves the output to the C:\Windows\Temp folder.
     
    .EXAMPLE
    Get-RiskyDetections -UserIds "user-id-1","user-id-2"
    Retrieves risky detections for the specified User IDs.
#>

    [CmdletBinding()]
    param(
        [string]$OutputDir= "Output\RiskyEvents",
        [string]$Encoding = "UTF8",
        [string[]]$UserIds,
        [ValidateSet('None', 'Minimal', 'Standard', 'Debug')]
        [string]$LogLevel = 'Standard'
    )

    Set-LogLevel -Level ([LogLevel]::$LogLevel)
    $isDebugEnabled = $script:LogLevel -eq [LogLevel]::Debug

    if ($isDebugEnabled) {
        Write-LogFile -Message "[DEBUG] PowerShell Version: $($PSVersionTable.PSVersion)" -Level Debug
        Write-LogFile -Message "[DEBUG] Input parameters:" -Level Debug
        Write-LogFile -Message "[DEBUG] OutputDir: '$OutputDir'" -Level Debug
        Write-LogFile -Message "[DEBUG] Encoding: '$Encoding'" -Level Debug
        Write-LogFile -Message "[DEBUG] UserIds: '$($UserIds -join ', ')'" -Level Debug
        Write-LogFile -Message "[DEBUG] UserIds count: $($UserIds.Count)" -Level Debug
        Write-LogFile -Message "[DEBUG] LogLevel: '$LogLevel'" -Level Debug
        
        $graphModules = Get-Module -Name Microsoft.Graph* -ErrorAction SilentlyContinue
        if ($graphModules) {
            Write-LogFile -Message "[DEBUG] Microsoft Graph Modules loaded:" -Level Debug
            foreach ($module in $graphModules) {
                Write-LogFile -Message "[DEBUG] - $($module.Name) v$($module.Version)" -Level Debug
            }
        } else {
            Write-LogFile -Message "[DEBUG] No Microsoft Graph modules loaded" -Level Debug
        }
    }

    Write-LogFile -Message "=== Starting Risky Detections Collection ===" -Color "Cyan" -Level Standard

    $requiredScopes = @("IdentityRiskEvent.Read.All","IdentityRiskyUser.Read.All")
    $graphAuth = Get-GraphAuthType -RequiredScopes $RequiredScopes

    if ($isDebugEnabled) {
        Write-LogFile -Message "[DEBUG] Graph authentication details:" -Level Debug
        Write-LogFile -Message "[DEBUG] Required scopes: $($requiredScopes -join ', ')" -Level Debug
        Write-LogFile -Message "[DEBUG] Authentication type: $($graphAuth.AuthType)" -Level Debug
        Write-LogFile -Message "[DEBUG] Current scopes: $($graphAuth.Scopes -join ', ')" -Level Debug
        if ($graphAuth.MissingScopes.Count -gt 0) {
            Write-LogFile -Message "[DEBUG] Missing scopes: $($graphAuth.MissingScopes -join ', ')" -Level Debug
        } else {
            Write-LogFile -Message "[DEBUG] Missing scopes: None" -Level Debug
        }
    }

    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
        }
    }

    $results = @()
    $count = 0
    $riskSummary = @{
        High = 0
        Medium = 0 
        Low = 0
        AtRisk = 0
        NotAtRisk = 0
        Remediated = 0
        Dismissed = 0
        UniqueUsers = @{}
        UniqueCountries = @{}
        UniqueCities = @{}
    }

    try {
        $baseUri = "https://graph.microsoft.com/v1.0/identityProtection/riskDetections"

        if ($UserIds) {
            if ($isDebugEnabled) {
                Write-LogFile -Message "[DEBUG] Processing scenario: Specific users" -Level Debug
                Write-LogFile -Message "[DEBUG] Users to process: $($UserIds -join ', ')" -Level Debug
            }
            foreach ($userId in $UserIds) {
                $encodedUserId = [System.Web.HttpUtility]::UrlEncode($userId)
                $uri = "$baseUri`?`$filter=UserPrincipalName eq '$encodedUserId'"
                Write-LogFile -Message "[INFO] Retrieving risky detections for User ID: $userId" -Level Standard

                do {
                    $response = Invoke-MgGraphRequest -Method GET -Uri $uri

                    if ($isDebugEnabled) {
                        Write-LogFile -Message "[DEBUG] Value count: $($response.value.Count)" -Level Debug
                        Write-LogFile -Message "[DEBUG] Has @odata.nextLink: $($null -ne $response.'@odata.nextLink')" -Level Debug
                    }

                    if ($response.value) {
                        if ($isDebugEnabled) {
                            Write-LogFile -Message "[DEBUG] Processing $($response.value.Count) detections from page $pageCount" -Level Debug
                        }
                        foreach ($detection in $response.value) {
                            if ($isDebugEnabled) {
                                $userIdentifier = if ([string]::IsNullOrEmpty($detection.UserPrincipalName)) {
                                    if (![string]::IsNullOrEmpty($detection.UserDisplayName)) {
                                        "DisplayName: $($detection.UserDisplayName)"
                                    } elseif (![string]::IsNullOrEmpty($detection.UserId)) {
                                        "ID: $($detection.UserId)"
                                    } else {
                                        "[Unknown User]"
                                    }
                                } else {
                                    $detection.UserPrincipalName
                                }
                                Write-LogFile -Message "[DEBUG] Processing detection:" -Level Debug
                                Write-LogFile -Message "[DEBUG] Detection ID: $($detection.Id)" -Level Debug
                                Write-LogFile -Message "[DEBUG] Risk Event Type: $($detection.RiskEventType)" -Level Debug
                                Write-LogFile -Message "[DEBUG] Risk Level: $($detection.RiskLevel)" -Level Debug
                                Write-LogFile -Message "[DEBUG] Risk State: $($detection.RiskState)" -Level Debug
                                Write-LogFile -Message "[DEBUG] User: $userIdentifier" -Level Debug
                                Write-LogFile -Message "[DEBUG] IP Address: $($detection.IPAddress)" -Level Debug
                                Write-LogFile -Message "[DEBUG] Location: $($detection.Location.City), $($detection.Location.CountryOrRegion)" -Level Debug
                            }
                            $results += [PSCustomObject]@{
                                Activity = $detection.Activity
                                ActivityDateTime = $detection.ActivityDateTime
                                AdditionalInfo = $detection.AdditionalInfo
                                CorrelationId = $detection.CorrelationId
                                DetectedDateTime = $detection.DetectedDateTime
                                IPAddress = $detection.IPAddress
                                Id = $detection.Id
                                LastUpdatedDateTime = $detection.LastUpdatedDateTime
                                City = $detection.Location.City
                                CountryOrRegion = $detection.Location.CountryOrRegion
                                State = $detection.Location.State
                                RequestId = $detection.RequestId
                                RiskDetail = $detection.RiskDetail
                                RiskEventType = $detection.RiskEventType
                                RiskLevel = $detection.RiskLevel
                                RiskState = $detection.RiskState
                                DetectionTimingType = $detection.DetectionTimingType
                                Source = $detection.Source
                                TokenIssuerType = $detection.TokenIssuerType
                                UserDisplayName = $detection.UserDisplayName
                                UserId = $detection.UserId
                                UserPrincipalName = $detection.UserPrincipalName
                                AdditionalProperties = $detection.AdditionalProperties -join ", "
                            }

                            if ($detection.RiskLevel) { $riskSummary[$detection.RiskLevel]++ }
                            if ($detection.RiskState -eq "atRisk") { $riskSummary.AtRisk++ }
                            elseif ($detection.RiskState -eq "confirmedSafe") { $riskSummary.NotAtRisk++ }
                            elseif ($detection.RiskState -eq "remediated") { $riskSummary.Remediated++ }
                            elseif ($detection.RiskState -eq "dismissed") { $riskSummary.Dismissed++ }

                            if ($detection.UserPrincipalName) { $riskSummary.UniqueUsers[$detection.UserPrincipalName] = $true }
                            if ($detection.Location.CountryOrRegion) { $riskSummary.UniqueCountries[$detection.Location.CountryOrRegion] = $true }
                            if ($detection.Location.City) { $riskSummary.UniqueCities[$detection.Location.City] = $true }

                            $count++
                        }
                    }

                    $uri = $response.'@odata.nextLink'
                } while ($uri -ne $null)
            }
        }
        else {
            do {
                $response = Invoke-MgGraphRequest -Method GET -Uri $baseUri
                if ($isDebugEnabled) {
                    Write-LogFile -Message "[DEBUG] Processing scenario: All risky detections" -Level Debug
                    Write-LogFile -Message "[DEBUG] Base URI: $baseUri" -Level Debug
                    Write-LogFile -Message "[DEBUG] Value count: $($response.value.Count)" -Level Debug
                    Write-LogFile -Message "[DEBUG] Has @odata.nextLink: $($null -ne $response.'@odata.nextLink')" -Level Debug
                }

                if ($response.value) {
                    if ($isDebugEnabled) {
                        Write-LogFile -Message "[DEBUG] Processing $($response.value.Count) detections from page $pageCount" -Level Debug
                    }
                    foreach ($detection in $response.value) {
                        if ($isDebugEnabled) {
                            $userIdentifier = if ([string]::IsNullOrEmpty($detection.UserPrincipalName)) {
                                if (![string]::IsNullOrEmpty($detection.UserDisplayName)) {
                                    "DisplayName: $($detection.UserDisplayName)"
                                } elseif (![string]::IsNullOrEmpty($detection.UserId)) {
                                    "ID: $($detection.UserId)"
                                } else {
                                    "[Unknown User]"
                                }
                            } else {
                                $detection.UserPrincipalName
                            }
                            Write-LogFile -Message "[DEBUG] Processing detection: $($detection.Id)" -Level Debug
                            Write-LogFile -Message "[DEBUG] Risk Event Type: $($detection.RiskEventType)" -Level Debug
                            Write-LogFile -Message "[DEBUG] Risk Level: $($detection.RiskLevel)" -Level Debug
                            Write-LogFile -Message "[DEBUG] User: $userIdentifier" -Level Debug
                        }
                        $results += [PSCustomObject]@{
                            Activity = $detection.Activity
                            ActivityDateTime = $detection.ActivityDateTime
                            AdditionalInfo = $detection.AdditionalInfo
                            CorrelationId = $detection.CorrelationId
                            DetectedDateTime = $detection.DetectedDateTime
                            IPAddress = $detection.IPAddress
                            Id = $detection.Id
                            LastUpdatedDateTime = $detection.LastUpdatedDateTime
                            City = $detection.Location.City
                            CountryOrRegion = $detection.Location.CountryOrRegion
                            State = $detection.Location.State
                            RequestId = $detection.RequestId
                            RiskDetail = $detection.RiskDetail
                            RiskEventType = $detection.RiskEventType
                            RiskLevel = $detection.RiskLevel
                            RiskState = $detection.RiskState
                            DetectionTimingType = $detection.DetectionTimingType
                            Source = $detection.Source
                            TokenIssuerType = $detection.TokenIssuerType
                            UserDisplayName = $detection.UserDisplayName
                            UserId = $detection.UserId
                            UserPrincipalName = $detection.UserPrincipalName
                            AdditionalProperties = $detection.AdditionalProperties -join ", "
                        }

                        if ($detection.RiskLevel) { $riskSummary[$detection.RiskLevel]++ }
                        if ($detection.RiskState -eq "atRisk") { $riskSummary.AtRisk++ }
                        elseif ($detection.RiskState -eq "confirmedSafe") { $riskSummary.NotAtRisk++ }
                        elseif ($detection.RiskState -eq "remediated") { $riskSummary.Remediated++ }
                        elseif ($detection.RiskState -eq "dismissed") { $riskSummary.Dismissed++ }

                        if ($detection.UserPrincipalName) { $riskSummary.UniqueUsers[$detection.UserPrincipalName] = $true }
                        if ($detection.Location.CountryOrRegion) { $riskSummary.UniqueCountries[$detection.Location.CountryOrRegion] = $true }
                        if ($detection.Location.City) { $riskSummary.UniqueCities[$detection.Location.City] = $true }

                        $count++
                    }
                }

                $baseUri = $response.'@odata.nextLink'
            } while ($baseUri -ne $null)
        }
    } catch {
        Write-LogFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" -Level Minimal
        Write-LogFile -Message "[ERROR (Continued)] Check the below, as the target tenant may not be licenced for this feature $($_.ErrorDetails.Message)" -Color "Red" -Level Minimal
        throw
    }

    $date = Get-Date -Format "yyyyMMddHHmm"
    $filePath = "$OutputDir\$($date)-RiskyDetections.csv"

    if ($results.Count -gt 0) {
        $results | Export-Csv -Path $filePath -NoTypeInformation -Encoding $Encoding

        Write-LogFile -Message "`nSummary of Risky Detections:" -Color "Cyan" -Level Standard 
        Write-LogFile -Message "----------------------------------------" -Level Standard
        Write-LogFile -Message "Total Risky Detections: $count" -Level Standard
        Write-LogFile -Message " - High Risk: $($riskSummary.High)" -Level Standard
        Write-LogFile -Message " - Medium Risk: $($riskSummary.Medium)" -Level Standard
        Write-LogFile -Message " - Low Risk: $($riskSummary.Low)" -Level Standard

        Write-LogFile -Message "`nRisk States:" -Level Standard
        Write-LogFile -Message " - At Risk: $($riskSummary.AtRisk)" -Level Standard
        Write-LogFile -Message " - Confirmed Safe: $($riskSummary.NotAtRisk)" -Level Standard
        Write-LogFile -Message " - Remediated: $($riskSummary.Remediated)" -Level Standard
        Write-LogFile -Message " - Dismissed: $($riskSummary.Dismissed)" -Level Standard

        Write-LogFile -Message "`nAffected Resources:" -Level Standard 
        Write-LogFile -Message " - Unique Users: $($riskSummary.UniqueUsers.Count)" -Level Standard
        Write-LogFile -Message " - Unique Countries: $($riskSummary.UniqueCountries.Count)" -Level Standard

        Write-LogFile -Message "`nExported Files:" -Level Standard
        Write-LogFile -Message " - $filePath" -Level Standard
    } else {
        Write-LogFile -Message "[INFO] No Risky Detections found" -Color "Yellow" -Level Standard
    }
}