diff --git a/Hawk/Hawk.psd1 b/Hawk/Hawk.psd1 index 824a41e..a35d7ca 100644 --- a/Hawk/Hawk.psd1 +++ b/Hawk/Hawk.psd1 @@ -56,7 +56,7 @@ 'Get-HawkTenantConsentGrant', 'Get-HawkTenantRBACChange', 'Get-HawkTenantAzureAppAuditLog', - 'Get-HawkUserAuthHistory', + 'Get-HawkUserUALSignInLog', 'Get-HawkUserConfiguration', 'Get-HawkUserEmailForwarding', 'Get-HawkUserInboxRule', @@ -89,7 +89,7 @@ 'Get-HawkTenantDomainActivity', 'Get-HawkTenantEDiscoveryLog', 'Get-HawkUserSharePointSearchQuery', - 'Get-HawkTenantEntraSignInLog' + 'Get-HawkUserEntraSignInLog' # Cmdlets to export from this module # CmdletsToExport = '' diff --git a/Hawk/functions/Tenant/Get-HawkTenantEntraSignInLog.ps1 b/Hawk/functions/Tenant/Get-HawkTenantEntraSignInLog.ps1 deleted file mode 100644 index 8a39f84..0000000 --- a/Hawk/functions/Tenant/Get-HawkTenantEntraSignInLog.ps1 +++ /dev/null @@ -1,111 +0,0 @@ -Function Get-HawkTenantEntraSignInLog { - <# - .SYNOPSIS - Retrieves medium and high risk Microsoft Entra ID sign-in logs using Microsoft Graph. - - .DESCRIPTION - This function retrieves risky sign-in logs from Microsoft Entra ID (formerly Azure AD) - using the Microsoft Graph API. It specifically collects sign-ins that were marked as - medium or high risk either during sign-in or after aggregated risk analysis. - - The function: - - Filters for medium and high risk sign-ins at the API level - - Automatically handles pagination of large result sets - - Displays progress during data collection - - Exports data in both CSV and JSON formats - - Uses the configured Hawk date range - - .OUTPUTS - File: RiskySignInLog.csv/.json - Path: \Tenant - Description: Medium and high risk sign-in logs from Microsoft Entra ID - - .EXAMPLE - Get-HawkTenantEntraSignInLog - - Retrieves risky sign-in logs from the configured Hawk time window. - - .NOTES - Requires Microsoft.Graph.Authentication module - Requires appropriate Microsoft Graph permissions (AuditLog.Read.All) - #> - [CmdletBinding()] - param() - - BEGIN { - # Check if Hawk object exists and is fully initialized - if (Test-HawkGlobalObject) { - Initialize-HawkGlobalObject - } - - Out-LogFile "Gathering Risky Microsoft Entra ID Sign-in Logs" -Action - - # Verify Graph API connection - Test-GraphConnection - Send-AIEvent -Event "CmdRun" - - # Create tenant folder if it doesn't exist - $TenantPath = Join-Path -Path $Hawk.FilePath -ChildPath "Tenant" - if (-not (Test-Path -Path $TenantPath)) { - New-Item -Path $TenantPath -ItemType Directory -Force | Out-Null - } - } - - PROCESS { - try { - Out-LogFile "Retrieving high and medium risk sign-in logs from Microsoft Graph..." -Action - - # Build filter for medium or high risk levels - $filter = "(riskLevelDuringSignIn eq 'high' or riskLevelDuringSignIn eq 'medium' or " + - "riskLevelAggregated eq 'high' or riskLevelAggregated eq 'medium')" - - # Get filtered sign-in logs with progress tracking - $processedCount = 0 - $signInLogs = Get-MgAuditLogSignIn -Filter $filter -All -ErrorAction Stop - - foreach ($log in $signInLogs) { - $processedCount++ - - # Update progress every 100 items - if ($processedCount % 100 -eq 0) { - Write-Progress -Activity "Retrieving Risky Entra Sign-in Logs" ` - -Status "Processed $processedCount logs" ` - -PercentComplete -1 - } - } - - Write-Progress -Activity "Retrieving Risky Entra Sign-in Logs" -Completed - - if ($signInLogs.Count -gt 0) { - Out-LogFile "Retrieved $($signInLogs.Count) risky sign-in log entries" -Information - Out-LogFile "Risk Level Breakdown:" -Information - - # Provide risk level breakdown - $riskBreakdown = $signInLogs | Group-Object -Property RiskLevelDuringSignIn | - Select-Object Name, Count | Sort-Object -Property Name - foreach ($risk in $riskBreakdown) { - Out-LogFile " Risk During Sign-in - $($risk.Name): $($risk.Count) events" -Information - } - - $aggregatedBreakdown = $signInLogs | Group-Object -Property RiskLevelAggregated | - Select-Object Name, Count | Sort-Object -Property Name - foreach ($risk in $aggregatedBreakdown) { - Out-LogFile " Aggregated Risk - $($risk.Name): $($risk.Count) events" -Information - } - - $signInLogs | Out-MultipleFileType -FilePrefix "RiskySignInLog" -csv -json - } - else { - Out-LogFile "No risky sign-in logs found in the specified time period" -Information - } - } - catch { - Out-LogFile "Error retrieving sign-in logs: $($_.Exception.Message)" -isError - Write-Error -ErrorRecord $_ -ErrorAction Continue - } - } - - END { - Out-LogFile "Completed exporting risky Entra sign-in logs" -Information - } -} \ No newline at end of file diff --git a/Hawk/functions/User/Get-HawkUserEntraSignInLog.ps1 b/Hawk/functions/User/Get-HawkUserEntraSignInLog.ps1 new file mode 100644 index 0000000..2aef2f0 --- /dev/null +++ b/Hawk/functions/User/Get-HawkUserEntraSignInLog.ps1 @@ -0,0 +1,132 @@ +Function Get-HawkUserEntraSignInLog { + <# + .SYNOPSIS + Retrieves medium and high risk Microsoft Entra ID sign-in logs for a specific user using Microsoft Graph. + + .DESCRIPTION + This function retrieves risky sign-in logs from Microsoft Entra ID (formerly Azure AD) + for specific users using the Microsoft Graph API. It collects sign-ins that were marked as + medium or high risk either during sign-in or after aggregated risk analysis. + + The function: + - Filters for medium and high risk sign-ins at the API level + - Automatically handles pagination of large result sets + - Displays progress during data collection + - Exports data in both CSV and JSON formats + - Uses the configured Hawk date range + + .PARAMETER UserPrincipalName + Single UPN of a user, comma-separated list of UPNs, or array of objects that contain UPNs. + + .OUTPUTS + File: RiskySignInLog.csv/.json + Path: \ + Description: Medium and high risk sign-in logs for the specified user(s) + + .EXAMPLE + Get-HawkUserEntraSignInLog -UserPrincipalName user@contoso.com + + Retrieves risky sign-in logs for the specified user from the configured Hawk time window. + + .EXAMPLE + Get-HawkUserEntraSignInLog -UserPrincipalName "user1@contoso.com","user2@contoso.com" + + Retrieves risky sign-in logs for multiple users. + + .NOTES + Requires Microsoft.Graph.Authentication module + Requires appropriate Microsoft Graph permissions (AuditLog.Read.All) + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [array]$UserPrincipalName + ) + + BEGIN { + if (Test-HawkGlobalObject) { + Initialize-HawkGlobalObject + } + + $startTime = Get-Date + Out-LogFile "Gathering Microsoft Entra ID Sign-in Logs" -Action + Test-GraphConnection + Send-AIEvent -Event "CmdRun" + [array]$UserArray = Test-UserObject -ToTest $UserPrincipalName + } + + PROCESS { + foreach ($Object in $UserArray) { + [string]$User = $Object.UserPrincipalName + + try { + Out-LogFile ("Retrieving sign-in logs for " + $User) -Action + + # Filter just on user - get all sign-ins + $filter = "userPrincipalName eq '$User'" + + $processedCount = 0 + $signInLogs = Get-MgAuditLogSignIn -Filter $filter -All -ErrorAction Stop + + foreach ($log in $signInLogs) { + $processedCount++ + if ($processedCount % 100 -eq 0) { + Write-Progress -Activity "Retrieving Entra Sign-in Logs" ` + -Status "Processed $processedCount logs" ` + -PercentComplete -1 + } + } + + Write-Progress -Activity "Retrieving Entra Sign-in Logs" -Completed + + if ($signInLogs.Count -gt 0) { + Out-LogFile ("Retrieved " + $signInLogs.Count + " sign-in log entries for " + $User) -Information + + # Write all logs to CSV/JSON + $signInLogs | Out-MultipleFileType -FilePrefix "EntraSignInLog_$User" -User $User -csv -json + + # Check for risky sign-ins + $riskySignIns = $signInLogs | Where-Object { + $_.RiskLevelDuringSignIn -in @('high', 'medium') -or + $_.RiskLevelAggregated -in @('high', 'medium') + } + + if ($riskySignIns.Count -gt 0) { + # Flag for investigation + Out-LogFile ("Found " + $riskySignIns.Count + " risky sign-ins for " + $User) -notice + + # Group and report risk levels + $duringSignIn = $riskySignIns | Group-Object -Property RiskLevelDuringSignIn | + Where-Object {$_.Name -in @('high', 'medium')} + foreach ($risk in $duringSignIn) { + Out-LogFile ("Found " + $risk.Count + " sign-ins with risk level during sign-in: " + $risk.Name) -silentnotice + } + + $aggregated = $riskySignIns | Group-Object -Property RiskLevelAggregated | + Where-Object {$_.Name -in @('high', 'medium')} + foreach ($risk in $aggregated) { + Out-LogFile ("Found " + $risk.Count + " sign-ins with aggregated risk level: " + $risk.Name) -silentnotice + } + + Out-LogFile ("Review SignInLog.csv/json in the " + $User + " folder for complete details") -silentnotice + } + } + else { + Out-LogFile ("No sign-in logs found for " + $User + " in the specified time period") -Information + } + } + catch { + Out-LogFile ("Error retrieving sign-in logs for " + $User + " : " + $_.Exception.Message) -isError + Write-Error -ErrorRecord $_ -ErrorAction Continue + } + } + } + + END { + Out-LogFile "Completed exporting Entra sign-in logs" -Information + $endTime = Get-Date + $duration = $endTime - $startTime + $formattedDuration = ("Total Runtime: {0:N0} minutes {1} seconds" -f [math]::Floor($duration.TotalMinutes), $duration.Seconds) + Out-LogFile $formattedDuration -Information + } +} \ No newline at end of file diff --git a/Hawk/functions/User/Get-HawkUserAuthHistory.ps1 b/Hawk/functions/User/Get-HawkUserUALSignInLog.ps1 similarity index 96% rename from Hawk/functions/User/Get-HawkUserAuthHistory.ps1 rename to Hawk/functions/User/Get-HawkUserUALSignInLog.ps1 index 738897e..e3058e1 100644 --- a/Hawk/functions/User/Get-HawkUserAuthHistory.ps1 +++ b/Hawk/functions/User/Get-HawkUserUALSignInLog.ps1 @@ -1,4 +1,4 @@ -Function Get-HawkUserAuthHistory { +Function Get-HawkUserUALSignInLog { <# .SYNOPSIS Gathers ip addresses that logged into the user account @@ -20,13 +20,13 @@ Description: All authentication activity for the user in a more readable form .EXAMPLE - Get-HawkUserAuthHistory -UserPrincipalName user@contoso.com -ResolveIPLocations + Get-HawkUserUALSignInLog -UserPrincipalName user@contoso.com -ResolveIPLocations Gathers authentication information for user@contoso.com. Attempts to resolve the IP locations for all authentication IPs found. .EXAMPLE - Get-HawkUserAuthHistory -UserPrincipalName (get-mailbox -Filter {Customattribute1 -eq "C-level"}) -ResolveIPLocations + Get-HawkUserUALSignInLog -UserPrincipalName (get-mailbox -Filter {Customattribute1 -eq "C-level"}) -ResolveIPLocations Gathers authentication information for all users that have "C-Level" set in CustomAttribute1 Attempts to resolve the IP locations for all authentication IPs found. diff --git a/Hawk/functions/User/Start-HawkUserInvestigation.ps1 b/Hawk/functions/User/Start-HawkUserInvestigation.ps1 index de789af..b1eab7c 100644 --- a/Hawk/functions/User/Start-HawkUserInvestigation.ps1 +++ b/Hawk/functions/User/Start-HawkUserInvestigation.ps1 @@ -176,9 +176,9 @@ Get-HawkUserAutoReply -User $User } - if ($PSCmdlet.ShouldProcess("Running Get-HawkUserAuthHistory for $User")) { - Out-LogFile "Running Get-HawkUserAuthHistory" -Action - Get-HawkUserAuthHistory -User $User -ResolveIPLocations + if ($PSCmdlet.ShouldProcess("Running Get-HawkUserUALSignInLog for $User")) { + Out-LogFile "Running Get-HawkUserUALSignInLog" -Action + Get-HawkUserUALSignInLog -User $User -ResolveIPLocations } if ($PSCmdlet.ShouldProcess("Running Get-HawkUserMailboxAuditing for $User")) { diff --git a/Hawk/internal/functions/Write-HawkConfigurationComplete.ps1 b/Hawk/internal/functions/Write-HawkConfigurationComplete.ps1 index 7f77c33..719a9d9 100644 --- a/Hawk/internal/functions/Write-HawkConfigurationComplete.ps1 +++ b/Hawk/internal/functions/Write-HawkConfigurationComplete.ps1 @@ -68,6 +68,6 @@ Out-LogFile -string ("{0} = {1}" -f $prop.Name, $value) -Information } - Out-LogFile "`Happy hunting! 🦅`n" -action + Out-LogFile "`Happy hunting! 🦅`n" -Information } } \ No newline at end of file