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.
     
    .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
    )

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

    if (!(test-path $OutputDir)) {
        New-Item -ItemType Directory -Force -Name $OutputDir > $null
        write-logFile -Message "[INFO] Creating the following directory: $OutputDir"
    }
    else {
        if (Test-Path -Path $OutputDir) {
            write-LogFile -Message "[INFO] Custom directory set to: $OutputDir"
        }    
        else {
            write-Error "[Error] Custom directory invalid: $OutputDir exiting script" -ErrorAction Stop
            write-LogFile -Message "[Error] Custom directory invalid: $OutputDir exiting script"
        }
    }

    Write-logFile -Message "[INFO] Running Get-RiskyUsers" -Color "Green"
    $results=@();
    $count = 0
    
    try {
        $baseUri = "https://graph.microsoft.com/v1.0/identityProtection/riskyUsers"

        if ($UserIds) {
            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"

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

                    if ($response.value -and $response.value.Count -gt 0) {
                        foreach ($user in $response.value) {
                            $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 ", "
                            }
                            $count++
                        }
                    } else {
                        Write-LogFile -Message "[INFO] User ID $userId not found or not risky."
                    }
                } catch {
                    Write-LogFile -Message "[ERROR] Failed to retrieve data for User ID $userId : $($_.Exception.Message)" -Color "Red"
                }
            }
        }
        else {
            $uri = "https://graph.microsoft.com/v1.0/identityProtection/riskyUsers"
            do {
                $response = Invoke-MgGraphRequest -Method GET -Uri $uri

                if ($response.value) {
                    foreach ($user in $response.value) {
                        $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 ", "
                        }

                        $count++
                    }
                }

                $uri = $response.'@odata.nextLink'
            } while ($uri -ne $null)
        }
    } catch {
        Write-LogFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red"
        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"
        Write-LogFile -Message "[INFO] Output written to $filePath" -Color "Green"
    } else {
        Write-LogFile -Message "[INFO] No Risky Users found" -Color "Yellow"
    }
}

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.
         
    .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
    )

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

    if (!(test-path $OutputDir)) {
        New-Item -ItemType Directory -Force -Name $OutputDir > $null
        write-logFile -Message "[INFO] Creating the following directory: $OutputDir"
    }

    else {
        if (Test-Path -Path $OutputDir) {
            write-LogFile -Message "[INFO] Custom directory set to: $OutputDir"
        }
    
        else {
            write-Error "[Error] Custom directory invalid: $OutputDir exiting script" -ErrorAction Stop
            write-LogFile -Message "[Error] Custom directory invalid: $OutputDir exiting script"
        }
    }

    Write-logFile -Message "[INFO] Running Get-RiskyDetections" -Color "Green"
    $results=@();
    $count = 0

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

        if ($UserIds) {
            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"

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

                    if ($response.value) {
                        foreach ($detection in $response.value) {
                            $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 ", "
                            }
                            $count++
                        }
                    }

                    $uri = $response.'@odata.nextLink'
                } while ($uri -ne $null)
            }
        }
        else {
            do {
                $response = Invoke-MgGraphRequest -Method GET -Uri $baseUri

                if ($response.value) {
                    foreach ($detection in $response.value) {
                        $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 ", "
                        }
                        $count++
                    }
                }

                $baseUri = $response.'@odata.nextLink'
            } while ($baseUri -ne $null)
        }
    } catch {
        Write-LogFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red"
        Write-LogFile -Message "[ERROR (Continued)] Check the below, as the target tenant may not be licenced for this feature $($_.ErrorDetails.Message)" -Color "Red"
        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 "[INFO] A total of $count Risky Detections found"
        Write-LogFile -Message "[INFO] Output written to $filePath" -Color "Green"
    } else {
        Write-LogFile -Message "[INFO] No Risky Detections found" -Color "Yellow"
    }
}