From dc9c1a7a11dca27c1c25a8901473a23aee041ec3 Mon Sep 17 00:00:00 2001 From: JoeyInvictus <129975292+JoeyInvictus@users.noreply.github.com> Date: Tue, 25 Jun 2024 14:03:48 +0200 Subject: [PATCH 1/2] Update to 2.0 - Test --- Microsoft-Extractor-Suite.psd1 | 10 +- Microsoft-Extractor-Suite.psm1 | 86 ++-- README.md | 5 +- Scripts/Connect.ps1 | 7 +- Scripts/Get-AdminAuditLog.ps1 | 31 +- Scripts/Get-AzureADGraphLogs.ps1 | 362 +++++------------ Scripts/Get-AzureADLogs.ps1 | 98 +---- Scripts/Get-AzureActivityLogs.ps1 | 244 ++++------- Scripts/Get-AzureDirectoryActivityLogs.ps1 | 111 +++++ Scripts/Get-ConditionalAccessPolicy.ps1 | 111 +++-- Scripts/Get-Emails.ps1 | 183 ++++----- Scripts/Get-MFAStatus.ps1 | 378 ++++++++---------- Scripts/Get-MailItemsAccessed.ps1 | 143 ++++--- Scripts/Get-MailboxAuditLog.ps1 | 20 +- Scripts/Get-MessageTraceLog.ps1 | 26 +- Scripts/Get-OAuthPermissions.ps1 | 23 +- Scripts/Get-RiskyEvents.ps1 | 264 +++++------- Scripts/Get-Rules.ps1 | 40 +- Scripts/Get-UAL.ps1 | 197 +++------ Scripts/Get-UALGraph.ps1 | 171 ++++---- Scripts/Get-UALStatistics.ps1 | 40 +- Scripts/Get-UsersInfo.ps1 | 256 +++++------- docs/source/conf.py | 6 +- .../AzureActiveDirectoryAuditLog.rst | 2 +- .../AzureActiveDirectorysign-inlogs.rst | 2 +- .../functionality/AzureActivityLogs.rst | 16 +- .../functionality/AzureAuditLogsGraph.rst | 11 - .../AzureDirectoryActivityLogs.rst | 51 +++ .../functionality/AzureSignInLogsGraph.rst | 18 +- .../ConditionalAccessPolicies.rst | 4 +- docs/source/functionality/GetUserInfo.rst | 40 +- docs/source/index.rst | 4 +- 32 files changed, 1229 insertions(+), 1731 deletions(-) create mode 100644 Scripts/Get-AzureDirectoryActivityLogs.ps1 create mode 100644 docs/source/functionality/AzureDirectoryActivityLogs.rst diff --git a/Microsoft-Extractor-Suite.psd1 b/Microsoft-Extractor-Suite.psd1 index 192a5d8..802ef12 100644 --- a/Microsoft-Extractor-Suite.psd1 +++ b/Microsoft-Extractor-Suite.psd1 @@ -8,13 +8,13 @@ Author = 'Joey Rentenaar & Korstiaan Stam' CompanyName = 'Invictus-IR' # Version number of this module. -ModuleVersion = '1.3.5' +ModuleVersion = '2.0.0' # ID used to uniquely identify this module GUID = '4376306b-0078-4b4d-b565-e22804e3be01' # Copyright statement for this module -Copyright = 'Copyright (c) 2024 Invictus Incident Response' +Copyright = 'Copyright 2024 Invictus Incident Response' # Description of the functionality provided by this module Description = 'Microsoft-Extractor-Suite is a fully-featured, actively-maintained, Powershell tool designed to streamline the process of collecting all necessary data and information from various sources within Microsoft.' @@ -38,6 +38,7 @@ NestedModules = @( ".\Scripts\Get-Emails.ps1" ".\Scripts\Get-MailItemsAccessed.ps1" ".\Scripts\Get-UALGraph.ps1" + ".\Scripts\Get-AzureDirectoryActivityLogs.ps1" ) FunctionsToExport = @( @@ -83,6 +84,9 @@ FunctionsToExport = @( # Get-AzureActivityLogs.ps1 "Get-ActivityLogs" + # Get-AzureDirectoryActivityLogs.ps1 + "Get-DirectoryActivityLogs" + # Get-AzureADGraphLogs.ps1 "Get-ADSignInLogsGraph" "Get-ADAuditLogsGraph" @@ -113,7 +117,7 @@ FunctionsToExport = @( # Variables to export from this module VariablesToExport = @( - '$outputdir', + '$outputdir', '$curDir', '$logFile', '$retryCount' diff --git a/Microsoft-Extractor-Suite.psm1 b/Microsoft-Extractor-Suite.psm1 index 7a3be65..99c2047 100644 --- a/Microsoft-Extractor-Suite.psm1 +++ b/Microsoft-Extractor-Suite.psm1 @@ -3,14 +3,13 @@ $manifest = Import-PowerShellDataFile "$PSScriptRoot\Microsoft-Extractor-Suite.psd1" $version = $manifest.ModuleVersion -$host.ui.RawUI.WindowTitle="Microsoft-Extractor-Suite $version" +$host.ui.RawUI.WindowTitle = "Microsoft-Extractor-Suite $version" $logo=@" - +-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+ |M|i|c|r|o|s|o|f|t| |E|x|t|r|a|c|t|o|r| |S|u|i|t|e| +-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+ -Copyright (c) 2024 Invictus Incident Response +Copyright 2024 Invictus Incident Response Created by Joey Rentenaar & Korstiaan Stam "@ @@ -18,7 +17,7 @@ Write-Host $logo -ForegroundColor Yellow $outputDir = "Output" if (!(test-path $outputDir)) { - New-Item -ItemType Directory -Force -Name $Outputdir | Out-Null + New-Item -ItemType Directory -Force -Name $Outputdir > $null } $retryCount = 0 @@ -73,31 +72,25 @@ function Write-LogFile([String]$message,$color) { $outputDir = "Output" if (!(test-path $outputDir)) { - New-Item -ItemType Directory -Force -Name $Outputdir | Out-Null - } - if ($color -eq "Yellow") - { - Write-host $message -ForegroundColor Yellow - } - elseif ($color -eq "Red") - { - Write-host $message -ForegroundColor Red + New-Item -ItemType Directory -Force -Name $Outputdir > $null } - elseif ($color -eq "Green") - { - Write-host $message -ForegroundColor Green - } - else { - Write-host $message - } - - $logToWrite = [DateTime]::Now.ToString() + ": " + $message - $logToWrite | Out-File $LogFile -Append + + switch ($color) { + "Yellow" { [Console]::ForegroundColor = [ConsoleColor]::Yellow } + "Red" { [Console]::ForegroundColor = [ConsoleColor]::Red } + "Green" { [Console]::ForegroundColor = [ConsoleColor]::Green } + default { [Console]::ResetColor() } + } + + [Console]::WriteLine($message) + [Console]::ResetColor() + $logToWrite = [DateTime]::Now.ToString() + ": " + $message + $logToWrite | Out-File -FilePath $LogFile -Append } function versionCheck{ $moduleName = "Microsoft-Extractor-Suite" - $currentVersionString = $version + $currentVersionString = $version $currentVersion = [Version]$currentVersionString $latestVersionString = (Find-Module -Name $moduleName).Version.ToString() @@ -111,4 +104,47 @@ function versionCheck{ } } -versionCheck \ No newline at end of file +function Get-GraphAuthType { + $authContext = Get-MgContext | Select-Object -ExpandProperty AuthType + switch ($authContext) { + "AppOnly" { return "application" } + "Delegated" { return "delegated" } + } +} + +function Merge-OutputFiles { + param ( + [Parameter(Mandatory)][string]$OutputDir, + [Parameter(Mandatory)][string]$OutputType, + [string]$MergedFileName + ) + + $outputDirMerged = Join-Path -Path $OutputDir -ChildPath "Merged" + If (!(Test-Path $outputDirMerged)) { + Write-LogFile -Message "[INFO] Creating the following directory: $outputDirMerged" + New-Item -ItemType Directory -Force -Path $outputDirMerged > $null + } + + $mergedPath = Join-Path -Path $outputDirMerged -ChildPath $MergedFileName + + switch ($OutputType) { + 'CSV' { + Get-ChildItem $OutputDir -Filter *.csv | Select-Object -ExpandProperty FullName | Import-Csv | Export-Csv $mergedPath -NoTypeInformation -Append -Encoding UTF8 + Write-LogFile -Message "[INFO] CSV files merged into $mergedPath" + } + 'JSON' { + $allJsonObjects = Get-ChildItem $OutputDir -Filter *.json | ForEach-Object { + Get-Content -Path $_.FullName -Raw | ConvertFrom-Json + } + $allJsonObjects | ConvertTo-Json -Depth 100 | Set-Content $mergedPath + Write-Host "[INFO] JSON files merged into $mergedPath" + } + default { + Write-LogFile -Message "[ERROR] Unsupported file type specified: $OutputType" -Color Red + } + } +} + +versionCheck + +Export-ModuleMember -Function * -Alias * -Variable * -Cmdlet * diff --git a/README.md b/README.md index 6a19366..6ba20ae 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ ![alt text](https://github.com/invictus-ir/Microsoft-Extractor-Suite/blob/main/docs/source/Images/Invictus-Incident-Response.jpg?raw=true) - ![Language](https://img.shields.io/badge/Language-Powershell-blue) [![Documentation](https://img.shields.io/badge/Read%20the%20Docs-Documentation-blue)](https://microsoft-365-extractor-suite.readthedocs.io/en/latest/) [![Latest Version](https://img.shields.io/powershellgallery/v/Microsoft-Extractor-Suite?label=Latest%20Version&color=brightgreen)](https://www.powershellgallery.com/packages/Microsoft-Extractor-Suite) @@ -24,6 +23,8 @@ The following Microsoft data sources are supported: * Message Trace Logs * Azure AD Sign-In Logs * Azure AD Audit Logs +* Azure Activity Logs +* Azure Directory Activity Logs In addition to the log sources above the tool is also able to retrieve other relevant information: * Registered OAuth applications in Azure AD @@ -33,7 +34,7 @@ In addition to the log sources above the tool is also able to retrieve other rel * The risky detections * The conditional access policies * Administrator directory roles and their users -* A specific e-mail or attachment +* A specific or list of e-mail(s) or attachment(s) Microsoft-Extractor-Suite was created by Joey Rentenaar and Korstiaan Stam and is maintained by the [Invictus IR](https://www.invictus-ir.com/) team. diff --git a/Scripts/Connect.ps1 b/Scripts/Connect.ps1 index 713b43d..280cb4d 100644 --- a/Scripts/Connect.ps1 +++ b/Scripts/Connect.ps1 @@ -1,17 +1,18 @@ Function Connect-M365 { versionCheck - Connect-ExchangeOnline -Showbanner:$false -ShowProgress:$true + Connect-ExchangeOnline > $null } Function Connect-Azure { versionCheck - Connect-AzureAD | Out-Null + Connect-AzureAD > $null } Function Connect-AzureAZ { versionCheck - Connect-AzAccount | Out-Null + Connect-AzAccount > $null } + diff --git a/Scripts/Get-AdminAuditLog.ps1 b/Scripts/Get-AdminAuditLog.ps1 index d4f05e2..b2d8c11 100644 --- a/Scripts/Get-AdminAuditLog.ps1 +++ b/Scripts/Get-AdminAuditLog.ps1 @@ -31,28 +31,18 @@ function Get-AdminAuditLog { param ( [string]$StartDate, [string]$EndDate, - [string]$outputDir + [string]$outputDir = "Output\AdminAuditLog" ) - try { - $areYouConnected = Get-AdminAuditLogConfig -ErrorAction stop - } - catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" - break - } - write-logFile -Message "[INFO] Running Get-AdminAuditLog" -Color "Green" $date = [datetime]::Now.ToString('yyyyMMddHHmmss') $outputFile = "$($date)-AdminAuditLog.csv" - if ($OutputDir -eq "" ){ - $OutputDir = "Output\AdminAuditLog" - if (!(test-path $OutputDir)) { - New-Item -ItemType Directory -Force -Name $outputDir | Out-Null - write-LogFile -Message "[INFO] Creating the following directory: $outputDir" - } + + if (!(test-path $OutputDir)) { + New-Item -ItemType Directory -Force -Name $outputDir | Out-Null + write-LogFile -Message "[INFO] Creating the following directory: $outputDir" } else { @@ -73,8 +63,15 @@ function Get-AdminAuditLog { Write-LogFile -Message "[INFO] Extracting all available Admin Audit Logs between $($script:StartDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK")) and $($script:EndDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK"))" -Color "Green" - $results = Search-AdminAuditLog -ResultSize 250000 -StartDate $script:startDate -EndDate $script:EndDate - $results | Export-Csv $outputDirectory -NoTypeInformation -Append -Encoding UTF8 + try { + $results = Search-AdminAuditLog -ResultSize 250000 -StartDate $script:startDate -EndDate $script:EndDate + $results | Export-Csv $outputDirectory -NoTypeInformation -Append -Encoding UTF8 + } + catch { + write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break + } write-logFile -Message "[INFO] Output is written to: $outputDirectory" -Color "Green" } \ No newline at end of file diff --git a/Scripts/Get-AzureADGraphLogs.ps1 b/Scripts/Get-AzureADGraphLogs.ps1 index ab524b4..6e1cd30 100644 --- a/Scripts/Get-AzureADGraphLogs.ps1 +++ b/Scripts/Get-AzureADGraphLogs.ps1 @@ -20,21 +20,9 @@ function Get-ADSignInLogsGraph { Encoding is the parameter specifying the encoding of the JSON output file. Default: UTF8 - .PARAMETER Application - Application is the parameter specifying App-only access (access without a user) for authentication and authorization. - Default: Delegated access (access on behalf a user) - - .PARAMETER MergeOutput - MergeOutput is the parameter specifying if you wish to merge outputs to a single file - Default: No - .PARAMETER UserIds UserIds is the UserIds parameter filtering the log entries by the account of the user who performed the actions. - .PARAMETER Interval - Interval is the parameter specifying the interval in which the logs are being gathered. - Default: 1440 minutes - .EXAMPLE Get-ADSignInLogsGraph Get all audit logs of sign-ins. @@ -55,149 +43,68 @@ function Get-ADSignInLogsGraph { param( [string]$startDate, [string]$endDate, - [switch]$MergeOutput, [string]$OutputDir, [string]$UserIds, - [string]$Encoding = "UTF8", - [switch]$Application, - [string]$Interval - ) + [string]$Encoding = "UTF8" + ) - if (!($Application.IsPresent)) { - Connect-MgGraph -Scopes AuditLog.Read.All, Directory.Read.All -NoWelcome - } - - try { - $areYouConnected = Get-MgBetaAuditLogSignIn -ErrorAction stop - } - catch { - Write-LogFile -Message "[WARNING] You must call Connect-MgGraph before running this script" -Color "Red" - break - } - - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } - - if ($Interval -eq "") { - $Interval = 1440 - Write-LogFile -Message "[INFO] Setting the Interval to the default value of 1440" + $authType = Get-GraphAuthType + if ($authType -eq "Delegated") { + Connect-MgGraph -Scopes AuditLog.Read.All, Directory.Read.All > $null } write-logFile -Message "[INFO] Running Get-ADSignInLogsGraph" -Color "Green" - $date = [datetime]::Now.ToString('yyyyMMddHHmmss') + $date = [datetime]::Now.ToString('yyyyMMdd') if ($OutputDir -eq "" ){ - $OutputDir = "Output\AzureAD\$($date)_SignInLogs" + $OutputDir = "Output\AzureAD\$($date)-SignInLogs" if (!(test-path $OutputDir)) { - write-logFile -Message "[INFO] Creating the following directory: $OutputDir" - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null + write-logFile -Message "[INFO] Creating the following output directory: $OutputDir" + New-Item -ItemType Directory -Force -Name $OutputDir > $null } } - 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" } - - } - - if ($UserIds){ - Write-LogFile -Message "[INFO] UserID's eq $($UserIds)" } - StartDateAz + StartDate EndDate - if ($Interval -eq "") { - $Interval = 720 - Write-LogFile -Message "[INFO] Setting the Interval to the default value of 720 (Larger values may result in out of memory errors)" - } - - $date = Get-Date -Format 'yyyyMMddHHmmss' - $filePath = Join-Path -Path $outputDir -ChildPath "$($date)-SignInLogsGraph.json" - - [DateTime]$currentStart = $script:StartDate - [DateTime]$currentEnd = $script:EndDate - $currentDay = 0 + $StartDate = $script:StartDate.ToString('yyyy-MM-ddTHH:mm:ssZ') + $EndDate = $script:EndDate.ToString('yyyy-MM-ddTHH:mm:ssZ') - Write-LogFile -Message "[INFO] Extracting all available Directory Sign-in Logs between $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")) and $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))" -Color "Green" - if($currentStart -gt $script:EndDate){ - Write-LogFile -Message "[ERROR] $($currentStart.ToString("yyyy-MM-ddTHH:mm:ssZ")) is greather than $($script:EndDate.ToString("yyyy-MM-ddTHH:mm:ssZ")) - are you sure you put in the correct year? Exiting!" -Color "Red" - return - } - - while ($currentStart -lt $script:EndDate) { - $currentEnd = $currentStart.AddMinutes($Interval) - $retryCount = 0 - $maxRetries = 3 - $success = $false - - while (-not $success -and $retryCount -lt $maxRetries) { - try { - if ($UserIds) { - Write-LogFile -Message "[INFO] Collecting Directory Sign-in logs between $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")) and $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))." - [Array]$results = Get-MgBetaAuditLogSignIn -ExpandProperty * -All -Filter "UserPrincipalName eq '$($Userids)' and createdDateTime lt $($currentEnd.ToString("yyyy-MM-ddTHH:mm:ssZ")) and createdDateTime gt $($currentStart.ToString("yyyy-MM-ddTHH:mm:ssZ"))" | Select-Object AppDisplayName,AppId,AppTokenProtectionStatus,AppliedConditionalAccessPolicies,ConditionsNotSatisfied,ConditionsSatisfied,AppliedConditionalAccessPoliciesDisplayName,EnforcedGrantControls,EnforcedSessionControls,AppliedConditionalAccessPoliciesId,AppliedConditionalAccessPoliciesResult,AppliedConditionalAccessPolicies2,AppliedEventListeners,AuthenticationAppDeviceDetails,AppVersion,ClientApp,DeviceId,OperatingSystem,AuthenticationAppPolicyEvaluationDetails,AdminConfiguration,AuthenticationEvaluation,AuthenticationAppPolicyEvaluationDetailsPolicyName,AuthenticationAppPolicyEvaluationDetailsStatus,AuthenticationContextClassReferences,@{N='AuthDetailsAuthenticationMethod';E={$_.AuthenticationDetails.AuthenticationMethod.ToString()}},@{N='AuthDetailsAuthenticationMethodDetail';E={$_.AuthenticationDetails.AuthenticationMethodDetail.ToString()}},@{N='AuthDetailsAuthenticationStepDateTime';E={$_.AuthenticationDetails.AuthenticationStepDateTime.ToString()}},@{N='AuthDetailsAuthenticationStepRequirement';E={$_.AuthenticationDetails.AuthenticationStepRequirement.ToString()}},@{N='AuthDetailsAuthenticationStepResultDetail';E={$_.AuthenticationDetails.AuthenticationStepResultDetail.ToString()}},@{N='AuthDetailsSucceeded';E={$_.AuthenticationDetails.Succeeded.ToString()}},AuthenticationMethodsUsed,AuthenticationProcessingDetails,AuthenticationProtocol,AuthenticationRequirement,AuthenticationRequirementPolicies,Detail,RequirementProvider,AutonomousSystemNumber,AzureResourceId,ClientAppUsed,ClientCredentialType,ConditionalAccessStatus,CorrelationId,@{N='CreatedDateTime';E={$_.CreatedDateTime.ToString()}},CrossTenantAccessType,DeviceDetail,Browser,DeviceDetailDeviceId,DisplayName,IsCompliant,IsManaged,DeviceDetailOperatingSystem,TrustType,FederatedCredentialId,FlaggedForReview,HomeTenantId,HomeTenantName,IPAddress,IPAddressFromResourceProvider,Id,IncomingTokenType,IsInteractive,IsTenantRestricted,Location,City,CountryOrRegion,State,ManagedServiceIdentity,AssociatedResourceId,FederatedTokenId,FederatedTokenIssuer,MsiType,MfaDetail,AuthDetail,AuthMethod,NetworkLocationDetails,OriginalRequestId,OriginalTransferMethod,PrivateLinkDetails,PolicyId,PolicyName,PolicyTenantId,PrivateLinkDetailsResourceId,ProcessingTimeInMilliseconds,ResourceDisplayName,ResourceId,ResourceServicePrincipalId,ResourceTenantId,RiskDetail,RiskEventTypesV2,RiskLevelAggregated,RiskLevelDuringSignIn,RiskState,ServicePrincipalCredentialKeyId,ServicePrincipalCredentialThumbprint,ServicePrincipalId,ServicePrincipalName,SessionLifetimePolicies,SignInEventTypes,SignInIdentifier,SignInIdentifierType,SignInTokenProtectionStatus,Status,StatusAdditionalDetails,TokenIssuerName,TokenIssuerType,UniqueTokenIdentifier,UserAgent,UserDisplayName,UserId,UserPrincipalName,UserType,AdditionalProperties - } else { - Write-LogFile -Message "[INFO] Collecting Directory Sign-in logs between $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")) and $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))." - [Array]$results = Get-MgBetaAuditLogSignIn -ExpandProperty * -All -Filter "createdDateTime lt $($currentEnd.ToString("yyyy-MM-ddTHH:mm:ssZ")) and createdDateTime gt $($currentStart.ToString("yyyy-MM-ddTHH:mm:ssZ"))" | Select-Object AppDisplayName,AppId,AppTokenProtectionStatus,AppliedConditionalAccessPolicies,ConditionsNotSatisfied,ConditionsSatisfied,AppliedConditionalAccessPoliciesDisplayName,EnforcedGrantControls,EnforcedSessionControls,AppliedConditionalAccessPoliciesId,AppliedConditionalAccessPoliciesResult,AppliedConditionalAccessPolicies2,AppliedEventListeners,AuthenticationAppDeviceDetails,AppVersion,ClientApp,DeviceId,OperatingSystem,AuthenticationAppPolicyEvaluationDetails,AdminConfiguration,AuthenticationEvaluation,AuthenticationAppPolicyEvaluationDetailsPolicyName,AuthenticationAppPolicyEvaluationDetailsStatus,AuthenticationContextClassReferences,@{N='AuthDetailsAuthenticationMethod';E={$_.AuthenticationDetails.AuthenticationMethod.ToString()}},@{N='AuthDetailsAuthenticationMethodDetail';E={$_.AuthenticationDetails.AuthenticationMethodDetail.ToString()}},@{N='AuthDetailsAuthenticationStepDateTime';E={$_.AuthenticationDetails.AuthenticationStepDateTime.ToString()}},@{N='AuthDetailsAuthenticationStepRequirement';E={$_.AuthenticationDetails.AuthenticationStepRequirement.ToString()}},@{N='AuthDetailsAuthenticationStepResultDetail';E={$_.AuthenticationDetails.AuthenticationStepResultDetail.ToString()}},@{N='AuthDetailsSucceeded';E={$_.AuthenticationDetails.Succeeded.ToString()}},AuthenticationMethodsUsed,AuthenticationProcessingDetails,AuthenticationProtocol,AuthenticationRequirement,AuthenticationRequirementPolicies,Detail,RequirementProvider,AutonomousSystemNumber,AzureResourceId,ClientAppUsed,ClientCredentialType,ConditionalAccessStatus,CorrelationId,@{N='CreatedDateTime';E={$_.CreatedDateTime.ToString()}},CrossTenantAccessType,DeviceDetail,Browser,DeviceDetailDeviceId,DisplayName,IsCompliant,IsManaged,DeviceDetailOperatingSystem,TrustType,FederatedCredentialId,FlaggedForReview,HomeTenantId,HomeTenantName,IPAddress,IPAddressFromResourceProvider,Id,IncomingTokenType,IsInteractive,IsTenantRestricted,Location,City,CountryOrRegion,State,ManagedServiceIdentity,AssociatedResourceId,FederatedTokenId,FederatedTokenIssuer,MsiType,MfaDetail,AuthDetail,AuthMethod,NetworkLocationDetails,OriginalRequestId,OriginalTransferMethod,PrivateLinkDetails,PolicyId,PolicyName,PolicyTenantId,PrivateLinkDetailsResourceId,ProcessingTimeInMilliseconds,ResourceDisplayName,ResourceId,ResourceServicePrincipalId,ResourceTenantId,RiskDetail,RiskEventTypesV2,RiskLevelAggregated,RiskLevelDuringSignIn,RiskState,ServicePrincipalCredentialKeyId,ServicePrincipalCredentialThumbprint,ServicePrincipalId,ServicePrincipalName,SessionLifetimePolicies,SignInEventTypes,SignInIdentifier,SignInIdentifierType,SignInTokenProtectionStatus,Status,StatusAdditionalDetails,TokenIssuerName,TokenIssuerType,UniqueTokenIdentifier,UserAgent,UserDisplayName,UserId,UserPrincipalName,UserType,AdditionalProperties - } - $success = $true - } - - catch { - $retryCount++ - if ($retryCount -lt $maxRetries) { - Start-Sleep -Seconds 10 - Write-LogFile -Message "[WARNING] Failed to acquire logs. Retrying... Attempt $retryCount of $maxRetries" -Color "Yellow" - } else { - Write-LogFile -Message "[ERROR] Failed to acquire logs after $maxRetries attempts. Moving on." -Color "Red" - } - } - } - - if ($null -eq $results -or $results.Count -eq 0) { - Write-LogFile -Message "[WARNING] Empty data set returned between $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")) and $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")). Moving On!" -Color "Yellow" - } - else { - $currentCount = $results.Count - Write-LogFile -Message "[INFO] Found $currentCount Directory Sign-in Logs between $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")) and $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))" -Color "Green" - - $filePath = "$OutputDir\SignInLogsGraph-$($CurrentStart.ToString("yyyyMMdd"))-$($CurrentEnd.ToString("yyyyMMdd")).json" - $results | ConvertTo-Json -Depth 100 | Out-File -Append $filePath -Encoding $Encoding - - Write-LogFile -Message "[INFO] Successfully retrieved $($currentCount) records for the current time range." - } - [Array]$results = @() - $CurrentStart = $CurrentEnd - $currentDay++ - } - - if ($MergeOutput.IsPresent) - { - Write-LogFile -Message "[INFO] Merging output files into one file" - $outputDirMerged = "$OutputDir\Merged\" - If (!(test-path $outputDirMerged)) { - Write-LogFile -Message "[INFO] Creating the following directory: $outputDirMerged" - New-Item -ItemType Directory -Force -Path $outputDirMerged | Out-Null - } - - $allJsonObjects = @() - - Get-ChildItem $OutputDir -Filter *.json | ForEach-Object { - $content = Get-Content -Path $_.FullName -Raw - $jsonObjects = $content | ConvertFrom-Json - $allJsonObjects += $jsonObjects - } - - $allJsonObjects | ConvertTo-Json -Depth 100 | Set-Content "$outputDirMerged\SignInLogs-Combined.json" + $filterQuery = "createdDateTime ge $StartDate and createdDateTime le $EndDate" + if ($UserIds) { + $filterQuery += " and startsWith(userPrincipalName, '$UserIds')" } + $encodedFilterQuery = [System.Web.HttpUtility]::UrlEncode($filterQuery) + $apiUrl = "https://graph.microsoft.com/v1.0/auditLogs/signIns?`$filter=$encodedFilterQuery" + + try { + Do { + $response = Invoke-MgGraphRequest -Method Get -Uri $apiUrl -ContentType 'application/json' + if ($response.value) { + $date = [datetime]::Now.ToString('yyyyMMddHHmmss') + $filePath = Join-Path -Path $OutputDir -ChildPath "$($date)-SignInLogsGraph.json" + $response.value | ConvertTo-Json -Depth 100 | Out-File -FilePath $filePath -Append -Encoding $Encoding + Write-LogFile -Message "[INFO] Sign-in logs written to $filePath" -ForegroundColor Green + } else { + Write-LogFile -Message "[INFO] No data to write for current batch." + } + $apiUrl = $response.'@odata.nextLink' + } While ($apiUrl) + } + catch { + Write-LogFile -Message "[INFO] Make sure you are connected to Connect-MgGraph before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + } Write-LogFile -Message "[INFO] Acquisition complete, check the $($OutputDir) directory for your files.." -Color "Green" } @@ -225,18 +132,6 @@ function Get-ADAuditLogsGraph { .PARAMETER Encoding Encoding is the parameter specifying the encoding of the JSON output file. Default: UTF8 - - .PARAMETER MergeOutput - MergeOutput is the parameter specifying if you wish to merge outputs to a single file - Default: No - - .PARAMETER Application - Application is the parameter specifying App-only access (access without a user) for authentication and authorization. - Default: Delegated access (access on behalf a user) - - .PARAMETER Interval - Interval is the parameter specifying the interval in which the logs are being gathered. - Default: 720 minutes .EXAMPLE Get-ADAuditLogsGraph @@ -254,139 +149,72 @@ function Get-ADAuditLogsGraph { Get-ADAuditLogsGraph -After 2023-04-12 Get directory audit logs after 2023-04-12. #> - [CmdletBinding()] - param( - [string]$startDate, - [string]$endDate, - [string]$OutputDir, - [switch]$MergeOutput, - [string]$Encoding, - [string]$UserIds, - [switch]$Application, - [string]$Interval - ) + [CmdletBinding()] + param( + [string]$startDate, + [string]$endDate, + [string]$OutputDir, + [string]$Encoding = "UTF8", + [string]$UserIds + ) + + $authType = Get-GraphAuthType + if ($authType -eq "Delegated") { + Connect-MgGraph -Scopes AuditLog.Read.All, Directory.Read.All > $null + } + + Write-logFile -Message "[INFO] Running Get-ADAuditLogsGraph" -Color "Green" - try { - $areYouConnected = Get-MgAuditLogDirectoryAudit -ErrorAction stop - } - catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph before running this script" -Color "Red" - break + $date = [datetime]::Now.ToString('yyyyMMdd') + if ($OutputDir -eq "" ){ + $OutputDir = "Output\AzureAD\$($date)-Auditlogs" + if (!(test-path $OutputDir)) { + New-Item -ItemType Directory -Force -Name $OutputDir > $null + write-logFile -Message "[INFO] Creating the following directory: $OutputDir" } - - if ($Encoding -eq "" ){ - $Encoding = "UTF8" + } + else { + if (Test-Path -Path $OutputDir) { + write-LogFile -Message "[INFO] Custom directory set to: $OutputDir" } - - if (!($Application.IsPresent)) { - Connect-MgGraph -Scopes AuditLog.Read.All, Directory.Read.All -NoWelcome + else { + write-Error "[Error] Custom directory invalid: $OutputDir exiting script" -ErrorAction Stop + write-LogFile -Message "[Error] Custom directory invalid: $OutputDir exiting script" } + } - if ($Interval -eq "") { - $Interval = 720 - Write-LogFile -Message "[INFO] Setting the Interval to the default value of 720 (Larger values may result in out of memory errors)" - } + StartDateAz + EndDate - StartDateAz - EndDate - - Write-logFile -Message "[INFO] Running Get-ADAuditLogsGraph" -Color "Green" - - if ($OutputDir -eq "" ){ - $OutputDir = "Output\AzureAD\$($date)_Auditlogs" - if (!(test-path $OutputDir)) { - New-Item -ItemType Directory -Force -Name $OutputDir | Out-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" - } - } - - $date = [datetime]::Now.ToString('yyyyMMddHHmmss') - [DateTime]$currentStart = $script:StartDate - [DateTime]$currentEnd = $script:EndDate - $currentDay = 0 - - Write-LogFile -Message "[INFO] Extracting all available Directory Audit Logs between $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")) and $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))" -Color "Green" - if($currentStart -gt $script:EndDate){ - Write-LogFile -Message "[ERROR] $($currentStart.ToString("yyyy-MM-ddTHH:mm:ssZ")) is greather than $($script:EndDate.ToString("yyyy-MM-ddTHH:mm:ssZ")) - are you sure you put in the correct year? Exiting!" -Color "Red" - return - } + $StartDate = $script:StartDate.ToString('yyyy-MM-ddTHH:mm:ssZ') + $EndDate = $script:EndDate.ToString('yyyy-MM-ddTHH:mm:ssZ') - while ($currentStart -lt $script:EndDate) { - $currentEnd = $currentStart.AddMinutes($Interval) - $retryCount = 0 - $maxRetries = 3 - $success = $false - - while (-not $success -and $retryCount -lt $maxRetries) { - try { - if ($UserIds) { - Write-LogFile -Message "[INFO] Collecting Directory Audit logs between $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")) and $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))." - [Array]$results = Get-MgAuditLogDirectoryAudit -ExpandProperty * -All -Filter "initiatedBy/user/userPrincipalName eq '$UserIds' and activityDateTime gt $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")) and activityDateTime lt $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))" - } else { - Write-LogFile -Message "[INFO] Collecting Directory Audit logs between $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")) and $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))." - [Array]$results = Get-MgAuditLogDirectoryAudit -ExpandProperty * -All -Filter "activityDateTime gt $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")) and activityDateTime lt $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))" - } - $success = $true - } - catch { - $retryCount++ - if ($retryCount -lt $maxRetries) { - Start-Sleep -Seconds 10 - Write-LogFile -Message "[WARNING] Failed to acquire logs. Retrying... Attempt $retryCount of $maxRetries" -Color "Yellow" - } else { - Write-LogFile -Message "[ERROR] Failed to acquire logs after $maxRetries attempts. Moving on." -Color "Red" - } - } - } - - if ($null -eq $results -or $results.Count -eq 0) { - Write-LogFile -Message "[WARNING] Empty data set returned between $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")) and $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")). Moving On!" -Color "Yellow" - } - else { - $currentCount = $results.Count - Write-LogFile -Message "[INFO] Found $currentCount Directory Audit Logs between $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")) and $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))" -Color "Green" - - $filePath = "$OutputDir\AuditLogs-$($CurrentStart.ToString("yyyyMMddHHmmss"))-$($CurrentEnd.ToString("yyyyMMddHHmmss")).json" - $results | ConvertTo-Json -Depth 100 | Out-File -Append $filePath -Encoding $Encoding - - Write-LogFile -Message "[INFO] Successfully retrieved $($currentCount) records for the current time range." - } - [Array]$results = @() - $CurrentStart = $CurrentEnd - $currentDay++ - } - - if ($MergeOutput.IsPresent) - { - Write-LogFile -Message "[INFO] Merging output files into one file" - $outputDirMerged = "$OutputDir\Merged\" - If (!(test-path $outputDirMerged)) { - Write-LogFile -Message "[INFO] Creating the following directory: $outputDirMerged" - New-Item -ItemType Directory -Force -Path $outputDirMerged | Out-Null - } - - $allJsonObjects = @() - - Get-ChildItem $OutputDir -Filter *.json | ForEach-Object { - $content = Get-Content -Path $_.FullName -Raw - $jsonObjects = $content | ConvertFrom-Json - $allJsonObjects += $jsonObjects + $filterQuery = "activityDateTime ge $StartDate and activityDateTime le $EndDate" + if ($UserIds) { + $filterQuery += " and startsWith(initiatedBy/user/userPrincipalName, '$UserIds')" + } + + $encodedFilterQuery = [System.Web.HttpUtility]::UrlEncode($filterQuery) + $apiUrl = "https://graph.microsoft.com/v1.0/auditLogs/directoryAudits?`$filter=$encodedFilterQuery" + + try { + Do { + $response = Invoke-MgGraphRequest -Method Get -Uri $apiUrl -ContentType 'application/json' + if ($response.value) { + $date = [datetime]::Now.ToString('yyyyMMddHHmmss') + $filePath = Join-Path -Path $OutputDir -ChildPath "$($date)-AuditLogs.json" + $response.value | ConvertTo-Json -Depth 100 | Out-File -FilePath $filePath -Append -Encoding $Encoding + Write-LogFile -Message "[INFO] Audit logs written to $filePath" -ForegroundColor Green + } else { + Write-LogFile -Message "[INFO] No data to write for current batch." } - - $allJsonObjects | ConvertTo-Json -Depth 100 | Set-Content "$outputDirMerged\AuditLogs-Combined.json" - } - - Write-LogFile -Message "[INFO] Acquisition complete, check the $($OutputDir) directory for your files.." -Color "Green" + $apiUrl = $response.'@odata.nextLink' + } While ($apiUrl) } + catch { + Write-LogFile -Message "[INFO] Make sure you are connected to Connect-MgGraph before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + } + Write-LogFile -Message "[INFO] Acquisition complete, check the $($OutputDir) directory for your files.." -Color "Green" +} diff --git a/Scripts/Get-AzureADLogs.ps1 b/Scripts/Get-AzureADLogs.ps1 index d0ade66..f8bbed9 100644 --- a/Scripts/Get-AzureADLogs.ps1 +++ b/Scripts/Get-AzureADLogs.ps1 @@ -56,39 +56,21 @@ function Get-ADSignInLogs { [string]$outputDir, [string]$UserIds, [switch]$MergeOutput, - [string]$Encoding, - [string]$Interval + [string]$Encoding = "UTF8", + [string]$Interval = 1440 ) - try { - import-module AzureADPreview -force -ErrorAction stop - $areYouConnected = Get-AzureADAuditSignInLogs -ErrorAction stop - } - catch { - Write-logFile -Message "[WARNING] You must call Connect-Azure or install AzureADPreview before running this script" -Color "Red" - break - } - Write-logFile -Message "[INFO] Running Get-AADSignInLogs" -Color "Green" StartDateAz EndDate - if ($Interval -eq "") { - $Interval = 1440 - Write-LogFile -Message "[INFO] Setting the Interval to the default value of 1440" - } - - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } - $date = [datetime]::Now.ToString('yyyyMMddHHmmss') if ($OutputDir -eq "" ){ $OutputDir = "Output\AzureAD\$($date)_SignInLogs" if (!(test-path $OutputDir)) { write-logFile -Message "[INFO] Creating the following directory: $OutputDir" - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null + New-Item -ItemType Directory -Force -Name $OutputDir > $null } } @@ -97,10 +79,8 @@ function Get-ADSignInLogs { } $filePath = "$OutputDir\SignInLogs.json" - [DateTime]$currentStart = $script:StartDate [DateTime]$currentEnd = $script:EndDate - [DateTime]$lastLog = $script:EndDate $currentDay = 0 Write-LogFile -Message "[INFO] Extracting all available Directory Sign-in Logs between $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")) and $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))" -Color "Green" @@ -134,6 +114,8 @@ function Get-ADSignInLogs { Write-LogFile -Message "[WARNING] Failed to acquire logs. Retrying... Attempt $retryCount of $maxRetries" -Color "Yellow" } else { Write-LogFile -Message "[ERROR] Failed to acquire logs after $maxRetries attempts. Moving on." -Color "Red" + Write-logFile -Message "[INFO] You must call Connect-Azure or install AzureADPreview before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" } } } @@ -146,7 +128,7 @@ function Get-ADSignInLogs { Write-LogFile -Message "[INFO] Found $currentCount Directory Sign-in Logs between $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")) and $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))" -Color "Green" $filePath = "$OutputDir\SignInLogs-$($CurrentStart.ToString("yyyyMMdd"))-$($CurrentEnd.ToString("yyyyMMdd")).json" - $results | ConvertTo-Json -Depth 100 | Out-File -Append $filePath -Encoding $Encoding + $results | ConvertTo-Json -Depth 100 | out-file -Append $filePath -Encoding $Encoding Write-LogFile -Message "[INFO] Successfully retrieved $($currentCount) records for the current time range." } @@ -155,24 +137,9 @@ function Get-ADSignInLogs { $currentDay++ } - if ($MergeOutput.IsPresent) - { + if ($MergeOutput.IsPresent) { Write-LogFile -Message "[INFO] Merging output files into one file" - $outputDirMerged = "$OutputDir\Merged\" - If (!(test-path $outputDirMerged)) { - Write-LogFile -Message "[INFO] Creating the following directory: $outputDirMerged" - New-Item -ItemType Directory -Force -Path $outputDirMerged | Out-Null - } - - $allJsonObjects = @() - - Get-ChildItem $OutputDir -Filter *.json | ForEach-Object { - $content = Get-Content -Path $_.FullName -Raw - $jsonObjects = $content | ConvertFrom-Json - $allJsonObjects += $jsonObjects - } - - $allJsonObjects | ConvertTo-Json -Depth 100 | Set-Content "$outputDirMerged\SignInLogs-Combined.json" + Merge-OutputFiles -OutputDir $OutputDir -OutputType "JSON" -MergedFileName "SignInLogs-Combined.json" } Write-LogFile -Message "[INFO] Acquisition complete, check the $($OutputDir) directory for your files.." -Color "Green" @@ -238,39 +205,21 @@ function Get-ADAuditLogs { [string]$outputDir, [string]$UserIds, [switch]$MergeOutput, - [string]$Encoding, - [string]$Interval + [string]$Encoding = "UTF8", + [string]$Interval = 720 ) - try { - $areYouConnected = Get-AzureADAuditDirectoryLogs -ErrorAction stop - } - catch { - Write-logFile -Message "[WARNING] You must call Connect-Azure or install AzureADPreview before running this script" -Color "Red" - break - } - - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } - Write-logFile -Message "[INFO] Running Get-ADAuditLogs" -Color "Green" StartDateAz EndDate - if ($Interval -eq "") { - $Interval = 720 - Write-LogFile -Message "[INFO] Setting the Interval to the default value of 720 (Larger values may result in out of memory errors)" - } - - $date = [datetime]::Now.ToString('yyyyMMddHHmmss') if ($OutputDir -eq "" ){ $OutputDir = "Output\AzureAD\$($date)_AuditLogs" if (!(test-path $OutputDir)) { write-logFile -Message "[INFO] Creating the following directory: $OutputDir" - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null + New-Item -ItemType Directory -Force -Name $OutputDir > $null } } else { @@ -324,6 +273,8 @@ function Get-ADAuditLogs { Write-LogFile -Message "[WARNING] Failed to acquire logs. Retrying... Attempt $retryCount of $maxRetries" -Color "Yellow" } else { Write-LogFile -Message "[ERROR] Failed to acquire logs after $maxRetries attempts. Moving on." -Color "Red" + Write-logFile -Message "[WARNING] You must call Connect-Azure or install AzureADPreview before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" } } } @@ -344,26 +295,11 @@ function Get-ADAuditLogs { $CurrentStart = $CurrentEnd $currentDay++ } - - if ($MergeOutput.IsPresent) - { - Write-LogFile -Message "[INFO] Merging output files into one file" - $outputDirMerged = "$OutputDir\Merged\" - If (!(test-path $outputDirMerged)) { - Write-LogFile -Message "[INFO] Creating the following directory: $outputDirMerged" - New-Item -ItemType Directory -Force -Path $outputDirMerged | Out-Null - } - - $allJsonObjects = @() - Get-ChildItem $OutputDir -Filter *.json | ForEach-Object { - $content = Get-Content -Path $_.FullName -Raw - $jsonObjects = $content | ConvertFrom-Json - $allJsonObjects += $jsonObjects - } - - $allJsonObjects | ConvertTo-Json -Depth 100 | Set-Content "$outputDirMerged\AuditLogs-Combined.json" + if ($MergeOutput.IsPresent) { + Write-LogFile -Message "[INFO] Merging output files into one file" + Merge-OutputFiles -OutputDir $OutputDir -OutputType "JSON" -MergedFileName "AuditLogs-Combined.json" } - + Write-LogFile -Message "[INFO] Acquisition complete, check the $($OutputDir) directory for your files.." -Color "Green" } diff --git a/Scripts/Get-AzureActivityLogs.ps1 b/Scripts/Get-AzureActivityLogs.ps1 index 64a9e91..4154d03 100644 --- a/Scripts/Get-AzureActivityLogs.ps1 +++ b/Scripts/Get-AzureActivityLogs.ps1 @@ -1,39 +1,3 @@ -Function StartDateAzure -{ - if (($startDate -eq "") -Or ($null -eq $startDate)) { - $startDate = [datetime]::Now.ToUniversalTime().AddDays(-89) - $startDate = Get-Date $startDate -Format "yyyy-MM-dd HH:mm:ss" - write-host "[INFO] No start date provived by user setting the start date to: $startDate" - - $script:StartDate = Get-Date $startDate -Format "yyyy-MM-dd HH:mm:ss" - } - - else { - $script:startDate = $startDate -as [datetime] - if (!$script:startDate ) { - write-host "[WARNING] Not A valid start date and time, make sure to use YYYY-MM-DD" - } - } -} - -function EndDateAzure -{ - if (($endDate -eq "") -Or ($null -eq $endDate)) { - $endDate = [datetime]::Now.ToUniversalTime() - $endDate = Get-Date $endDate -Format "yyyy-MM-dd HH:mm:ss" - write-host "[INFO] No end date provived by user setting the end date to: $endDate" - - $script:endDate = Get-Date $endDate -Format "yyyy-MM-dd HH:mm:ss" - } - - else { - $script:endDate = $endDate -as [datetime] - if (!$endDate) { - write-host "[WARNING] Not A valid end date and time, make sure to use YYYY-MM-DD" - } - } -} - function Get-ActivityLogs { <# .SYNOPSIS @@ -41,7 +5,7 @@ function Get-ActivityLogs { .DESCRIPTION The Get-ActivityLogs cmdlet collects the Azure Activity logs. - The output will be written to: Output\AzureAD\$date\$iD-ActivityLog.json + The output will be written to: Output\ActivityLogs\$date\$iD-ActivityLog.json .PARAMETER StartDate startDate is the parameter specifying the start date of the date range. @@ -57,7 +21,7 @@ function Get-ActivityLogs { .PARAMETER OutputDir OutputDir is the parameter specifying the output directory. - Default: Output\AzureActivityLogs + Default: Output\ActivityLogs .PARAMETER Encoding Encoding is the parameter specifying the encoding of the JSON output file. @@ -68,12 +32,12 @@ function Get-ActivityLogs { Get all the activity logs for all subscriptions connected to the logged-in user account for the last 89 days. .EXAMPLE - Get-ActivityLogs -EndDate 2023-04-12 - Get all the activity logs before 2023-04-12. + Get-ActivityLogs -EndDate 2024-04-12 + Get all the activity logs before 2024-04-12. .EXAMPLE - Get-ActivityLogs -StartDate 2023-04-12 - Get all the activity logs after 2023-04-12. + Get-ActivityLogs -StartDate 2024-04-12 + Get all the activity logs after 2024-04-12. .EXAMPLE Get-ActivityLogs -SubscriptionID "4947f939-cf12-4329-960d-4dg68a3eb66f" @@ -84,33 +48,17 @@ function Get-ActivityLogs { [string]$StartDate, [string]$EndDate, [string]$SubscriptionID, - [string]$OutputDir, - [string]$Encoding + [string]$OutputDir = "Output\ActivityLogs", + [string]$Encoding = "UTF8" ) - - try { - $areYouConnected = Get-AzSubscription -ErrorAction stop -WarningAction silentlyContinue - } - catch { - write-logFile -Message "[WARNING] You must call Connect-AzureAZ before running this script" -Color "Red" - break - } - - StartDateAzure - EndDateAzure - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } + StartDate + EndDate - if ($OutputDir -eq "" ){ - $OutputDir = "Output\AzureActivityLogs" - if (!(test-path $OutputDir)) { - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null - write-logFile -Message "[INFO] Creating the following directory: $OutputDir" - } + 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" @@ -122,123 +70,81 @@ function Get-ActivityLogs { } } - $validSubscriptions = @() + $currentContext = Get-AzContext + $azureRmProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile + $profileClient = [Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient]::new($azureRmProfile) + $token = $profileClient.AcquireAccessToken($currentContext.Tenant.Id) + #$token if ($SubscriptionID -eq "") { write-logFile -Message "[INFO] Retrieving all subscriptions linked to the logged-in user account" -Color "Green" - $subScription = Get-AzSubscription + + try { + $subscriptionsUri = "https://management.azure.com/subscriptions?api-version=2020-01-01" + $headers = @{ + Authorization = "Bearer $($token.AccessToken)" + 'Content-Type' = 'application/json' + } + + $subscriptionsResponse = Invoke-RestMethod -Uri $subscriptionsUri -Headers $headers -Method Get + $subScription = $subscriptionsResponse.value + } + catch { + write-logFile -Message "[INFO] You must call Connect-AzureAZ before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break + } foreach ($i in $subScription) { - write-logFile -Message "[INFO] Identified Subscription: $i" + $subId = $i.subscriptionId + write-logFile -Message "[INFO] Identified Subscription: $subId" } } - else { - $subScription = Get-AzSubscription -SubscriptionId $SubscriptionID + try { + $subScription = Get-AzSubscription -SubscriptionId $SubscriptionID + } + catch { + write-logFile -Message "[INFO] You must call Connect-AzureAZ before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break + } } foreach ($sub in $subScription) { - try { - Set-AzContext -Subscription $sub.Id | Out-Null - $logs = Get-AzActivityLog -StartTime (Get-Date).AddDays(-89) -EndTime (Get-Date) -ErrorAction Stop -WarningAction SilentlyContinue - - if ($logs) { - $validSubscriptions += $sub - Write-Host "[INFO] Activity logs found in subscription: $($sub.Id)" -ForegroundColor Green - } - } - catch { - Write-Host "[WARNING] No Activity logs in subscription: $($sub.Id), or an error occurred." -ForegroundColor Yellow - } - } - - if ($validSubscriptions.Count -eq 0) { - Write-Host "[WARNING] No valid subscriptions with logs found." -ForegroundColor Red - return - } - - try { - - foreach ($sub in $validSubscriptions) { - $name = $sub.Name - $iD = $sub.Id - - write-logFile -Message "[INFO] Retrieving all Activity Logs for $sub" -Color "Green" - Set-AzContext -Subscription $iD | Out-Null - - write-logFile -Message "[INFO] Connected to Subscription $iD" -Color "Green" - $date = [datetime]::Now.ToString('yyyyMMddHHmmss') - $filePath = "$OutputDir\$($date)-$iD-ActivityLog.json" - - [DateTime]$currentStart = $script:StartDate - [DateTime]$currentEnd = $script:EndDate - - $totalDays = ($currentEnd - $currentStart).TotalDays - - for ($i = 0; $i -lt $totalDays; $i++) { - $dagCounter = $currentStart.AddDays($i) - $formattedDate = $dagCounter.ToString("yyyy-MM-dd") - - [DateTime]$start = (Get-Date $formattedDate).Date - [DateTime]$end = (Get-Date $formattedDate).Date.AddDays(1).AddSeconds(-1) - - $currentStartnew = $start - $currentEnd = $end - - $amountResults = Get-AzActivityLog -StartTime $start -EndTime $end -MaxRecord 1000 -WarningAction SilentlyContinue - if ($amountResults.count -gt 0) { - if ($amountResults.count -gt 1000) { - while ($currentStartnew -lt $currentEnd) { - Write-LogFile -Message "[WARNING] $formattedDate - We have exceeded the maximum allowable number of 100 logs, lowering the time interval.." -Color "Red" - Write-LogFile -Message "[INFO] $formattedDate - Temporary lowering time interval.." -Color "Yellow" - - $tempInterval = 24 - $tempStartDate = $start - $amountResults = Get-AzActivityLog -StartTime $tempStartDate -EndTime $currentEnd -MaxRecord 1000 -WarningAction SilentlyContinue - - while ($($amountResults.count) -gt 1000) { - $timeLeft = ($currentEnd - $tempStartDate).TotalHours - $tempInterval = $timeLeft / 2 - - $backup = $tempInterval - $tempStartDate = $tempStartDate.AddHours($tempInterval) - $amountResults = Get-AzActivityLog -StartTime $tempStartDate -EndTime $currentEnd -MaxRecord 1000 -WarningAction SilentlyContinue - } - - $amountResults = Get-AzActivityLog -StartTime $tempStartDate -EndTime $currentEnd -MaxRecord 1000 -WarningAction SilentlyContinue - Write-LogFile -Message "[INFO] Successfully retrieved $($amountResults.count) Activity logs between $tempStartDate and $currentEnd" -Color "Green" - - $amountResults | Convert-ToJSON -Depth 100 | Out-File -FilePath $filePath -Append -Encoding $Encoding - - if ($tempStartDate -eq $currentEnd) { - $timeLeft = ($currentEnd - $start).TotalHours - $tempStartDate = $start - } - - $currentEnd = $tempStartDate - } - } - - else { - Write-LogFile -Message "[INFO] Successfully retrieved $($amountResults.count) Activity logs for $formattedDate. Moving on!" -Color "Green" - Get-AzActivityLog -StartTime $start -EndTime $end -MaxRecord 1000 -WarningAction silentlyContinue | Select-Object @{N='EventTimestamp';E={$_.EventTimestamp.ToString()}},EventName,EventDataId,TenantId,CorrelationId,SubStatus,SubscriptionId,@{N='SubmissionTimestamp';E={$_.SubmissionTimestamp.ToString()}},Status,ResourceType,ResourceProviderName,ResourceId,ResourceGroupName,OperationName,OperationId,Level,Id,Description,Category,Caller,Authorization,Claims,HttpRequest,Properties | ConvertTo-Json -Depth 100| Out-File -FilePath $filePath -Append -Encoding $Encoding - } - } - - else { - Write-LogFile -Message "[INFO] No Activity Logs found on $formattedDate. Moving on!" + $subId = $sub.subscriptionId + write-logFile -Message "[INFO] Retrieving all Activity Logs for $subId" -Color "Green" + + #$subId = $sub.Id + $date = [datetime]::Now.ToString('yyyyMMddHHmmss') + $filePath = "$OutputDir\$($date)-$subId-ActivityLog.json" + + $uriBase = "https://management.azure.com/subscriptions/$subId/providers/Microsoft.Insights/eventtypes/management/values?api-version=2015-04-01&`$filter=eventTimestamp ge '$script:StartDate' and eventTimestamp le '$script:endDate'" + $events = @() + + do { + $listOperations = @{ + Uri = $uriBase + Headers = @{ + Authorization = "Bearer $($token.AccessToken)" + 'Content-Type' = 'application/json' } + Method = 'GET' } - - Write-LogFile -Message "[INFO] Done all logs are collected for $name" -Color "Green" + + $response = Invoke-RestMethod @listOperations + $events += $response.value + $uriBase = $response.nextLink + } while ($null -ne $uriBase) + + if ($events.Count -eq 0) { + Write-LogFile -Message "[WARNING] No Activity logs in subscription: $($subId), or an error occurred." -ForegroundColor Yellow + } + else{ + $eventCount = $events.Count + Write-LogFile -Message "[INFO] $eventCount Activity logs found in subscription: $subId" -ForegroundColor Green + $events | ConvertTo-Json -Depth 100 | Set-Content -Path $filePath -encoding $Encoding } } - catch [System.Management.Automation.ActionPreferenceStopException] { - write-logFile -Message "[WARNING] $sub contains no or null logs! moving on" -Color "Red" - } - catch { - write-logFile -Message "[ERROR] another error has occured $($error) please check the azure documentaion for further troubleshooting" -Color "Red" - return - } - + Write-LogFile -Message "[INFO] Done all Activity Logs are collected" -Color "Green" } diff --git a/Scripts/Get-AzureDirectoryActivityLogs.ps1 b/Scripts/Get-AzureDirectoryActivityLogs.ps1 new file mode 100644 index 0000000..6ac259d --- /dev/null +++ b/Scripts/Get-AzureDirectoryActivityLogs.ps1 @@ -0,0 +1,111 @@ +function Get-DirectoryActivityLogs { + <# + .SYNOPSIS + Retrieves the Directory Activity logs. + + .DESCRIPTION + The Get-DirectoryActivityLogs cmdlet collects the Azure Directory Activity logs. + The output will be written to: Output\AzureAD\$date\$iD-ActivityLog.json + + .PARAMETER StartDate + startDate is the parameter specifying the start date of the date range. + Default: Today -90 days + + .PARAMETER EndDate + endDate is the parameter specifying the end date of the date range. + Default: Now + + .PARAMETER OutputDir + OutputDir is the parameter specifying the output directory. + Default: Output\DirectoryActivityLogs + + .PARAMETER Encoding + Encoding is the parameter specifying the encoding of the JSON output file. + Default: UTF8 + + .PARAMETER Output + Output is the parameter specifying the CSV or JSON output type. + Default: CSV + + .EXAMPLE + Get-DirectoryActivityLogs + Get all the Directory Activity logs for the last 90 days. + + .EXAMPLE + Get-DirectoryActivityLogs -EndDate 2023-04-12 + Get all the Directory Activity before 2023-04-12. + + .EXAMPLE + Get-DirectoryActivityLogs -StartDate 2023-04-12 + Get all the Directory Activity after 2023-04-12. +#> + [CmdletBinding()] + param( + [string]$StartDate, + [string]$endDate, + [string]$output = "CSV", + [string]$outputDir = "Output\DirectoryActivityLogs", + [string]$encoding = "UTF8" + ) + + StartDate + EndDate + + 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" + } + } + + $currentContext = Get-AzContext + $azureRmProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile + $profileClient = [Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient]::new($azureRmProfile) + $token = $profileClient.AcquireAccessToken($currentContext.Tenant.Id) + + $uriBase = "https://management.azure.com/providers/microsoft.insights/eventtypes/management/values?api-version=2015-04-01&`$filter=eventTimestamp ge '$script:StartDate' and eventTimestamp le '$script:endDate'" + $events = @() + + do { + $listOperations = @{ + Uri = $uriBase + Headers = @{ + Authorization = "Bearer $($token.AccessToken)" + 'Content-Type' = 'application/json' + } + Method = 'GET' + } + + $response = Invoke-RestMethod @listOperations + $events += $response.value + $uriBase = $response.nextLink + } while ($null -ne $uriBase) + + $filteredEvents = $events | Where-Object { $_.id -match '/providers/Microsoft.Management/' } | ForEach-Object { + $eventProps = @{} + foreach ($prop in $_.PSObject.Properties) { + $eventProps[$prop.Name] = $prop.Value + } + [PSCustomObject]$eventProps + } + + $date = [datetime]::Now.ToString('yyyyMMddHHmmss') + if ($output -eq "JSON") { + $filteredEvents | ConvertTo-Json -Depth 100 | Set-Content -Path "$OutputDir/$($date)-DirectoryActivityLogs.JSON" + } + + elseif ($output -eq "CSV") { + $filteredEvents | Export-Csv -Path "$OutputDir/$($date)-DirectoryActivityLogs.csv" -NoTypeInformation + } + + Write-LogFile -Message "[INFO] Done all Directory Activity Logs are collected" -Color "Green" +} + diff --git a/Scripts/Get-ConditionalAccessPolicy.ps1 b/Scripts/Get-ConditionalAccessPolicy.ps1 index 9e14c77..481a05f 100644 --- a/Scripts/Get-ConditionalAccessPolicy.ps1 +++ b/Scripts/Get-ConditionalAccessPolicy.ps1 @@ -6,11 +6,10 @@ Function Get-ConditionalAccessPolicies { .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. - The output will be written to: Output\UserInfo\ .PARAMETER OutputDir OutputDir is the parameter specifying the output directory. - Default: Output\UserInfo + Default: Output\ConditionalAccessPolicies .PARAMETER Encoding Encoding is the parameter specifying the encoding of the CSV output file. @@ -38,40 +37,24 @@ Function Get-ConditionalAccessPolicies { #> [CmdletBinding()] param( - [string]$OutputDir, - [string]$Encoding, + [string]$OutputDir = "Output\ConditionalAccessPolicies", + [string]$Encoding = "UTF8", [switch]$Application ) - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } - - if (!($Application.IsPresent)) { - Connect-MgGraph -Scopes Policy.Read.All -NoWelcome - } - - try { - $areYouConnected = get-MgIdentityConditionalAccessPolicy -ErrorAction stop - } - catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes Policy.Read.All before running this script" -Color "Red" - break - } - - if ($OutputDir -eq "" ){ - $OutputDir = "Output\UserInfo" - if (!(test-path $OutputDir)) { - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null - write-logFile -Message "[INFO] Creating the following directory: $OutputDir" - } + $authType = Get-GraphAuthType + if ($authType -eq "Delegated") { + Connect-MgGraph -Scopes Policy.Read.All > $null } + 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" @@ -81,41 +64,49 @@ Function Get-ConditionalAccessPolicies { Write-logFile -Message "[INFO] Running Get-ConditionalAccess" -Color "Green" $results=@(); - get-MgIdentityConditionalAccessPolicy -all | ForEach-Object { - $myObject = [PSCustomObject]@{ - DisplayName = "-" - CreatedDateTime = "-" - Description = "-" - Id = "-" - ModifiedDateTime = "-" - State = "-" - ClientAppTypes = "-" - ServicePrincipalRiskLevels = "-" - SignInRiskLevels = "-" - UserRiskLevels = "-" - BuiltInControls = "-" - CustomAuthenticationFactors = "-" - ClientOperatorAppTypes = "-" - TermsOfUse = "-" - DisableResilienceDefaults = "-" + try { + get-MgIdentityConditionalAccessPolicy -all | ForEach-Object { + $myObject = [PSCustomObject]@{ + DisplayName = "-" + CreatedDateTime = "-" + Description = "-" + Id = "-" + ModifiedDateTime = "-" + State = "-" + ClientAppTypes = "-" + ServicePrincipalRiskLevels = "-" + SignInRiskLevels = "-" + UserRiskLevels = "-" + BuiltInControls = "-" + CustomAuthenticationFactors = "-" + ClientOperatorAppTypes = "-" + TermsOfUse = "-" + DisableResilienceDefaults = "-" + } + + $myobject.DisplayName = $_.DisplayName + $myobject.CreatedDateTime = $_.CreatedDateTime + $myobject.Description = $_.Description + $myobject.Id = $_.Id + $myobject.ModifiedDateTime = $_.ModifiedDateTime + $myobject.State = $_.State + $myobject.ClientAppTypes = $_.Conditions.ClientAppTypes | out-string + $myobject.ServicePrincipalRiskLevels = $_.Conditions.ServicePrincipalRiskLevels | out-string + $myobject.SignInRiskLevels = $_.Conditions.SignInRiskLevels | out-string + $myobject.UserRiskLevels = $_.Conditions.UserRiskLevels | out-string + $myobject.BuiltInControls = $_.GrantControls.BuiltInControls | out-string + $myobject.CustomAuthenticationFactors = $_.GrantControls.CustomAuthenticationFactors | out-string + $myobject.ClientOperatorAppTypes = $_.GrantControls.Operator | out-string + $myobject.TermsOfUse = $_.GrantControls.TermsOfUse | out-string + $myobject.DisableResilienceDefaults = $_.SessionControls.DisableResilienceDefaults | out-string + $results+= $myObject; } + } - $myobject.DisplayName = $_.DisplayName - $myobject.CreatedDateTime = $_.CreatedDateTime - $myobject.Description = $_.Description - $myobject.Id = $_.Id - $myobject.ModifiedDateTime = $_.ModifiedDateTime - $myobject.State = $_.State - $myobject.ClientAppTypes = $_.Conditions.ClientAppTypes | out-string - $myobject.ServicePrincipalRiskLevels = $_.Conditions.ServicePrincipalRiskLevels | out-string - $myobject.SignInRiskLevels = $_.Conditions.SignInRiskLevels | out-string - $myobject.UserRiskLevels = $_.Conditions.UserRiskLevels | out-string - $myobject.BuiltInControls = $_.GrantControls.BuiltInControls | out-string - $myobject.CustomAuthenticationFactors = $_.GrantControls.CustomAuthenticationFactors | out-string - $myobject.ClientOperatorAppTypes = $_.GrantControls.Operator | out-string - $myobject.TermsOfUse = $_.GrantControls.TermsOfUse | out-string - $myobject.DisableResilienceDefaults = $_.SessionControls.DisableResilienceDefaults | out-string - $results+= $myObject; + catch { + Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes Policy.Read.All before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" + break } $date = [datetime]::Now.ToString('yyyyMMddHHmmss') diff --git a/Scripts/Get-Emails.ps1 b/Scripts/Get-Emails.ps1 index 3fbcb13..1e5a1cb 100644 --- a/Scripts/Get-Emails.ps1 +++ b/Scripts/Get-Emails.ps1 @@ -43,34 +43,21 @@ Function Get-Email { param( [Parameter(Mandatory=$true)]$userIds, [string]$internetMessageId, - [string]$output, - [string]$outputDir, - [string]$attachment, + [string]$Output = "eml", + [string]$outputDir = "Output\EmailExport", + [switch]$attachment, [string]$inputFile ) Write-logFile -Message "[INFO] Running Get-Email" -Color "Green" - if ($outputDir -eq "" ){ - $outputDir = "Output\EmailExport" - if (!(test-path $outputDir)) { - write-logFile -Message "[INFO] Creating the following directory: $outputDir" - New-Item -ItemType Directory -Force -Name $outputDir | Out-Null - } + if (!(Test-Path -Path $outputDir)) { + Write-LogFile -Message "[INFO] Creating the following directory: $outputDir" + New-Item -ItemType Directory -Path $outputDir -Force > $null + } else { + Write-LogFile -Message "[INFO] Directory exists: $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" - } - } - - if ($inputFile) { try { $internetMessageIds = Get-Content $inputFile @@ -80,19 +67,17 @@ Function Get-Email { return } - # Loop through each internetMessageId in the inputFile $notCollected = @() foreach ($id in $internetMessageIds) { $id = $id.Trim() write-host "[INFO] Identified: $id" + try { - $getMessage = Get-MgUserMessage -UserId $userIds -Filter "internetMessageId eq '$id'" - $messageId = $getMessage.Id - - $subject = $getMessage.Subject - $subject = $subject -replace '[\\/:*?"<>|]', '_' - - $ReceivedDateTime = $getMessage.ReceivedDateTime.ToString("yyyyMMdd_HHmmss") + $getMessage = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/users/$userIds/messages?filter=internetMessageId eq '$id'" + $message = $getMessage.value[0] + $ReceivedDateTime = [datetime]::Parse($message.ReceivedDateTime).ToString("yyyyMMdd_HHmmss") + $messageId = $message.Id + $subject = $message.Subject -replace '[\\/:*?"<>|]', '_' if ($output -eq "txt") { $filePath = "$outputDir\$ReceivedDateTime-$subject.txt" @@ -101,20 +86,20 @@ Function Get-Email { else { $filePath = "$outputDir\$ReceivedDateTime-$subject.eml" } - - Get-MgUserMessageContent -MessageId $messageId -UserId $userIds -OutFile $filePath + + $contentUri = "https://graph.microsoft.com/v1.0/users/$userIds/messages/$messageId/\$value" + Invoke-MgGraphRequest -Uri $contentUri -Method Get -OutputFilePath $filePath Write-logFile -Message "[INFO] Output written to $filePath" -Color "Green" - - if ($attachment -eq "True"){ + + if ($attachment.IsPresent){ Get-Attachment -Userid $Userids -internetMessageId $id } - } - catch { - Write-Warning "[WARNING] Failed to collect message with ID '$id': $_" - $notCollected += $id # Add the message ID to the list of not collected IDs - } + } + catch { + Write-Warning "[WARNING] Failed to collect message with ID '$id': $_" + $notCollected += $id + } } - # Check if there are any message IDs that were not collected and write them to the log file if ($notCollected.Count -gt 0) { Write-logFile -Message "[INFO] The following messages have not been collected:" -Color "Yellow" foreach ($notCollectedID in $notCollected) { @@ -124,43 +109,39 @@ Function Get-Email { } else { - # Check if internetMessageId is provided if (-not $internetMessageId) { Write-Error "[ERROR] Either internetMessageId or inputFile must be provided." return } try { - $areYouConnected = Get-MgUserMessage -UserId $userIds -Filter "internetMessageId eq '$internetMessageId'" + $getMessage = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/users/$userIds/messages?filter=internetMessageId eq '$internetMessageId'" + $message = $getMessage.value[0] + $ReceivedDateTime = [datetime]::Parse($message.ReceivedDateTime).ToString("yyyyMMdd_HHmmss") + $messageId = $message.Id + $subject = $message.Subject -replace '[\\/:*?"<>|]', '_' + + if ($output -eq "txt") { + $filePath = "$outputDir\$ReceivedDateTime-$subject.txt" + } + + else { + $filePath = "$outputDir\$ReceivedDateTime-$subject.eml" + } + + $contentUri = "https://graph.microsoft.com/v1.0/users/$userIds/messages/$messageId/\$value" + Invoke-MgGraphRequest -Uri $contentUri -Method Get -OutputFilePath $filePath + Write-logFile -Message "[INFO] Output written to $filePath" -Color "Green" + + if ($attachment.IsPresent){ + Get-Attachment -Userid $Userids -internetMessageId $internetMessageId + } } catch { Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes Mail.ReadBasic.All before running this script" -Color "Red" Write-logFile -Message "[WARNING] The 'Mail.ReadBasic.All' is an application-level permission, requiring an application-based connection through the 'Connect-MgGraph' command for its use." -Color "Red" return - } - - $getMessage = Get-MgUserMessage -UserId $userIds -Filter "internetMessageId eq '$internetMessageId'" - $messageId = $getMessage.Id - - $subject = $getMessage.Subject - $subject = $subject -replace '[\\/:*?"<>|]', '_' - - $ReceivedDateTime = $getMessage.ReceivedDateTime.ToString("yyyyMMdd_HHmmss") - - if ($output -eq "txt") { - $filePath = "$outputDir\$ReceivedDateTime-$subject.txt" - } - - else { - $filePath = "$outputDir\$ReceivedDateTime-$subject.eml" - } - - Get-MgUserMessageContent -MessageId $messageId -UserId $userIds -OutFile $filePath - Write-logFile -Message "[INFO] Output written to $filePath" -Color "Green" - - if ($attachment -eq "True"){ - Get-Attachment -Userid $Userids -internetMessageId $internetMessageId - } + } } } @@ -195,67 +176,51 @@ Function Get-Attachment { param( [Parameter(Mandatory=$true)]$userIds, [Parameter(Mandatory=$true)]$internetMessageId, - [string]$outputDir + [string]$outputDir = "Output\EmailExport" ) - write-host $internetMessageId - write-host $userIds - Write-logFile -Message "[INFO] Running Get-Attachment" -Color "Green" + if (!(Test-Path -Path $outputDir)) { + Write-LogFile -Message "[INFO] Creating the following directory: $outputDir" + New-Item -ItemType Directory -Path $outputDir -Force > $null + } else { + Write-LogFile -Message "[INFO] Directory exists: $outputDir" + } + try { - $areYouConnected = Get-MgUserMessage -Filter "internetMessageId eq '$internetMessageId'" -UserId $userIds -ErrorAction stop + $getMessage = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/users/$userIds/messages?filter=internetMessageId eq '$internetMessageId'" -ErrorAction stop } catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes Mail.Read, Mail.ReadBasic, Mail.ReadBasic.All before running this script" -Color "Red" + Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes Mail.ReadBasic.All before running this script" -Color "Red" Write-logFile -Message "[WARNING] The 'Mail.ReadBasic.All' is an application-level permission, requiring an application-based connection through the 'Connect-MgGraph' command for its use." -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } - if ($outputDir -eq "" ){ - $outputDir = "Output\EmailExport" - if (!(test-path $outputDir)) { - New-Item -ItemType Directory -Force -Name $outputDir | Out-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" - } - } - - #$getMessage = Get-MgUserMessage -Filter "internetMessageId eq '$internetMessageId'" -UserId $userIds - $getMessage = Get-MgUserMessage -UserId $userIds -Filter "internetMessageId eq '$internetMessageId'" - $messageId = $getMessage.Id - $messageId = $messageId.Trim() - $hasAttachment = $getMessage.HasAttachments - $ReceivedDateTime = $getMessage.ReceivedDateTime.ToString("yyyyMMdd_HHmmss") - $subject = $getMessage.Subject - $subject = $subject -replace '[\\/:*?"<>|]', '_' + $messageId = $getMessage.value.Id + $hasAttachment = $getMessage.value.HasAttachments + $ReceivedDateTime = $getMessage.value.ReceivedDateTime.ToString("yyyyMMdd_HHmmss") + $subject = $getMessage.value.Subject if ($hasAttachment -eq "True"){ - $attachments = Get-MgUserMessageAttachment -UserId $userIds -MessageId $messageId + $response = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/users/$userIds/messages/$messageId/attachments" - foreach ($attachment in $attachments){ + foreach ($attachment in $response.value){ $filename = $attachment.Name - Write-logFile -Message "[INFO] Found attachment named $filename" Write-logFile -Message "[INFO] Downloading attachment" Write-host "[INFO] Name: $filename" write-host "[INFO] Size: $($attachment.Size)" - - $base64B = ($attachment).AdditionalProperties.contentBytes - $decoded = [System.Convert]::FromBase64String($base64B) + + $uri = "https://graph.microsoft.com/v1.0/users/$userIds/messages/$messageId/attachments/$($attachment.Id)/\$value" + $response = Invoke-MgGraphRequest -Method Get -Uri $uri $filename = $filename -replace '[\\/:*?"<>|]', '_' - $filePath = Join-Path -Path $outputDir -ChildPath "$ReceivedDateTime-$subject-$filename" + $filePath = Join-Path $outputDir "$ReceivedDateTime-$subject-$filename" + + $base64B = ($attachment.contentBytes) + $decoded = [System.Convert]::FromBase64String($base64B) Set-Content -Path $filePath -Value $decoded -Encoding Byte Write-logFile -Message "[INFO] Output written to '$subject-$filename'" -Color "Green" @@ -290,13 +255,15 @@ Function Show-Email { Write-logFile -Message "[INFO] Running Show-Email" -Color "Green" try { - $areYouConnected = Get-MgUserMessage -Filter "internetMessageId eq '$internetMessageId'" -UserId $userIds -ErrorAction stop + + $message = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/users/$userIds/messages?filter=internetMessageId eq '$internetMessageId'" -ErrorAction stop } catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes Mail.Read, Mail.ReadBasic, Mail.ReadBasic.All, Mail.ReadWrite before running this script" -Color "Red" + Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes Mail.ReadBasic.All before running this script" -Color "Red" Write-logFile -Message "[WARNING] The 'Mail.ReadBasic.All' is an application-level permission, requiring an application-based connection through the 'Connect-MgGraph' command for its use." -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } - Get-MgUserMessage -Filter "internetMessageId eq '$internetMessageId'" -UserId $userIds | fl * + $message.Value } \ No newline at end of file diff --git a/Scripts/Get-MFAStatus.ps1 b/Scripts/Get-MFAStatus.ps1 index 867b8b3..52407b0 100644 --- a/Scripts/Get-MFAStatus.ps1 +++ b/Scripts/Get-MFAStatus.ps1 @@ -1,87 +1,64 @@ function Get-MFA { -<# - .SYNOPSIS - Retrieves the MFA status for all users. - Script inspired by: https://activedirectorypro.com/mfa-status-powershell/ - - .DESCRIPTION - Retrieves the MFA status for all users. - The output will be written to: Output\UserInfo\ - - .PARAMETER OutputDir - OutputDir is the parameter specifying the output directory. - Default: Output\UserInfo - - .PARAMETER Encoding - Encoding is the parameter specifying the encoding of the CSV output file. - Default: UTF8 - - .PARAMETER Application - Application is the parameter specifying App-only access (access without a user) for authentication and authorization. - Default: Delegated access (access on behalf a user) + <# + .SYNOPSIS + Retrieves the MFA status for all users. + Script inspired by: https://activedirectorypro.com/mfa-status-powershell/ - .EXAMPLE - Get-MFA - Retrieves the MFA status for all users. - - .EXAMPLE - Get-MFA - Retrieves the MFA status for all users via application authentication. - - .EXAMPLE - Get-MFA -Encoding utf32 - Retrieves the MFA status for all users and exports the output to a CSV file with UTF-32 encoding. - - .EXAMPLE - Get-MFA -OutputDir C:\Windows\Temp - Retrieves the MFA status for all users and saves the output to the C:\Windows\Temp folder. -#> + .DESCRIPTION + Retrieves the MFA status for all users. + + .PARAMETER OutputDir + OutputDir is the parameter specifying the output directory. + Default: Output\MFA + + .PARAMETER Encoding + Encoding is the parameter specifying the encoding of the CSV output file. + Default: UTF8 + + .EXAMPLE + Get-MFA + Retrieves the MFA status for all users. + + .EXAMPLE + Get-MFA + Retrieves the MFA status for all users. + + .EXAMPLE + Get-MFA -Encoding utf32 + Retrieves the MFA status for all users and exports the output to a CSV file with UTF-32 encoding. + + .EXAMPLE + Get-MFA -OutputDir C:\Windows\Temp + Retrieves the MFA status for all users and saves the output to the C:\Windows\Temp folder. + #> [CmdletBinding()] param( - [string]$OutputDir, - [string]$Encoding, - [switch]$Application + [string]$OutputDir = "Output\MFA", + [string]$Encoding = "UTF8" ) - if (!($Application.IsPresent)) { - Connect-MgGraph -Scopes UserAuthenticationMethod.Read.All,User.Read.All -NoWelcome - } - - try { - $areYouConnected = Get-MgUser -ErrorAction stop - } - catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes 'UserAuthenticationMethod.Read.All,User.Read.All' before running this script" -Color "Red" - break - } - - if ($Encoding -eq "" ){ - $Encoding = "UTF8" + $authType = Get-GraphAuthType + if ($authType -eq "Delegated") { + Connect-MgGraph -Scopes UserAuthenticationMethod.Read.All,User.Read.All > $null } - if ($OutputDir -eq "" ){ - $OutputDir = "Output\UserInfo" - if (!(test-path $OutputDir)) { - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null - write-logFile -Message "[INFO] Creating the following directory: $OutputDir" - } + if (!(Test-Path $OutputDir)) { + New-Item -ItemType Directory -Force -Path $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-MFA" -Color "Green" - Write-logFile -Message "[INFO] Identifying all the authentication methods utilized within the environment" -Color "Green" - - $users = Get-MgUser -All + Write-LogFile -Message "[INFO] Running Get-MFA" + Write-LogFile -Message "[INFO] Identifying all the authentication methods utilized within the environment" -Color "Green" + + $results = @() + $allUsers = @() + $nextLink = "https://graph.microsoft.com/v1.0/users" + + do { + $response = Invoke-MgGraphRequest -Uri $nextLink -Method Get -OutputType PSObject + $allUsers += $response.value + $nextLink = $response.'@odata.nextLink' + } while ($nextLink) $MFAEmail = 0 $MFAfido2 = 0 @@ -90,167 +67,140 @@ function Get-MFA { $MFAsoftwareoath = 0 $MFAhellobusiness = 0 $MFAstatusAmount = 0 - - $results=@(); - - foreach ($user in $users) { - + + foreach ($user in $allUsers) { + $userPrinc = $user.userPrincipalName $myObject = [PSCustomObject]@{ - user = "-" + user = $userPrinc MFAstatus = "Disabled" # Default to 'Disabled' - email = "-" - fido2 = "-" - app = "-" - password = "-" - phone = "-" - softwareoath = "-" - hellobusiness = "-" - temporaryAccessPass = "-" - certificateBasedAuthConfiguration = "-" + email = $false + fido2 = $false + app = $false + password = $false + phone = $false + softwareoath = $false + hellobusiness = $false + temporaryAccessPass = $false + certificateBasedAuthConfiguration = $false } - + try { - $MFAData= Get-MgUserAuthenticationMethod -UserId $user.UserPrincipalName - - $myobject.user = $user.UserPrincipalName; - ForEach ($method in $MFAData) { - - Switch ($method.AdditionalProperties["@odata.type"]) { - "#microsoft.graph.emailAuthenticationMethod" { - $myObject.email = $true - $myObject.MFAstatus = "Enabled" - } - - "#microsoft.graph.fido2AuthenticationMethod" { - $myObject.fido2 = $true - $myObject.MFAstatus = "Enabled" + $contentUri = "https://graph.microsoft.com/v1.0/users/$($user.id)/authentication/methods" + $MFAData = Invoke-MgGraphRequest -Uri $contentUri -Method Get -OutputType PSObject + if ($MFAData -and $MFAData.value) { + ForEach ($method in $MFAData.value) { + $odataType = $method.'@odata.type' + + Switch ($odataType) { + "#microsoft.graph.emailAuthenticationMethod" { + $myObject.email = $true + $myObject.MFAstatus = "Enabled" + } + "#microsoft.graph.fido2AuthenticationMethod" { + $myObject.fido2 = $true + $myObject.MFAstatus = "Enabled" + } + "#microsoft.graph.microsoftAuthenticatorAuthenticationMethod" { + $myObject.app = $true + $myObject.MFAstatus = "Enabled" + } + "#microsoft.graph.passwordAuthenticationMethod" { + $myObject.password = $true + if ($myObject.MFAstatus -ne "Enabled") { + $myObject.MFAstatus = "Disabled" + } + } + "#microsoft.graph.phoneAuthenticationMethod" { + $myObject.phone = $true + $myObject.MFAstatus = "Enabled" + } + "#microsoft.graph.softwareOathAuthenticationMethod" { + $myObject.softwareoath = $true + $myObject.MFAstatus = "Enabled" + } + "#microsoft.graph.windowsHelloForBusinessAuthenticationMethod" { + $myObject.hellobusiness = $true + $myObject.MFAstatus = "Enabled" + } + "#microsoft.graph.temporaryAccessPassAuthenticationMethod" { + $myObject.temporaryAccessPass = $true + $myObject.MFAstatus = "Enabled" + } + "#microsoft.graph.certificateBasedAuthConfiguration" { + $myObject.certificateBasedAuthConfiguration = $true + $myObject.MFAstatus = "Enabled" + } + Default { + Write-Output "Unknown method type: $odataType for user $userPrinc" + } + } } - - "#microsoft.graph.microsoftAuthenticatorAuthenticationMethod" { - $myObject.app = $true - $myObject.MFAstatus = "Enabled" - } - - "#microsoft.graph.passwordAuthenticationMethod" { - $myObject.password = $true - if($myObject.MFAstatus -ne "Enabled"){ - $myObject.MFAstatus = "Disabled" - } - } - - "#microsoft.graph.phoneAuthenticationMethod" { - $myObject.phone = $true - $myObject.MFAstatus = "Enabled" - } - - "#microsoft.graph.softwareOathAuthenticationMethod" { - $myObject.softwareoath = $true - $myObject.MFAstatus = "Enabled" - } - - "#microsoft.graph.windowsHelloForBusinessAuthenticationMethod" { - $myObject.hellobusiness = $true - $myObject.MFAstatus = "Enabled" - } - - "#microsoft.graph.temporaryAccessPassAuthenticationMethod" { - $myObject.temporaryAccessPassAuthenticationMethod = $true - $myObject.MFAstatus = "Enabled" - } - - "#microsoft.graph.certificateBasedAuthConfiguration" { - $myObject.certificateBasedAuthConfiguration = $true - $myObject.MFAstatus = "Enabled" - } - } - } + } + + else { + Write-LogFile -Message "[WARNING] No MFA data found for user $userPrinc" -Color "Yellow" + } } catch { - Write-logFile -Message "[ERROR] Error while retrieving the MFA status for $user | Error: $_ " -Color "Red" + Write-LogFile -Message "[ERROR] Error while retrieving the MFA status for $userPrinc | Error: $_ " -Color "Red" } - - if($myObject.MFAstatus -eq "Enabled") { + + if ($myObject.MFAstatus -eq "Enabled") { $MFAstatusAmount++ } - - $results+= $myObject; + + $results += $myObject } - + $date = Get-Date -Format "yyyyMMddHHmm" $filePath = "$OutputDir\$($date)-MFA-AuthenticationMethods.csv" $results | Export-Csv -Path $filePath -NoTypeInformation -Encoding $Encoding - Write-logFile -Message "[INFO] Output written to $filePath" -Color "Green" + Write-LogFile -Message "[INFO] Output written to $filePath" -Color "Green" - $MFAEmail = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.email -eq "True" } | Measure-Object).Count - $MFAfido2 = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.fido2 -eq "True" } | Measure-Object).Count - $MFAapp = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.app -eq "True" } | Measure-Object).Count - $MFAphone = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.phone -eq "True" } | Measure-Object).Count - $MFAsoftwareoath = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.softwareoath -eq "True" } | Measure-Object).Count - $MFApassword = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.password -eq "True" } | Measure-Object).Count - $MFAhellobusiness = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.hellobusiness -eq "True" } | Measure-Object).Count + $MFAEmail = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.email -eq $true } | Measure-Object).Count + $MFAfido2 = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.fido2 -eq $true } | Measure-Object).Count + $MFAapp = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.app -eq $true } | Measure-Object).Count + $MFAphone = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.phone -eq $true } | Measure-Object).Count + $MFAsoftwareoath = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.softwareoath -eq $true } | Measure-Object).Count + $MFAhellobusiness = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.hellobusiness -eq $true } | Measure-Object).Count $MFAstatusAmount = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.MFAstatus -eq "Enabled" } | Measure-Object).Count - $temporaryAccessPassAuthenticationMethod = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.temporaryAccessPassAuthenticationMethod -eq "True" } | Measure-Object).Count - $certificateBasedAuthConfiguration = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.certificateBasedAuthConfiguration -eq "Enabled" } | Measure-Object).Count - - write-host "$MFAstatusAmount out of $($users.count) users have MFA enabled:" - write-host " - $MFAEmail x Email" - write-host " - $MFAfido2 x Fido2" - write-host " - $MFAapp x Microsoft Authenticator App" - write-host " - $MFAphone x Phone" - write-host " - $MFAsoftwareoath x SoftwareOAuth" - write-host " - $MFAhellobusiness x HelloBusiness" - write-host " - $temporaryAccessPassAuthenticationMethod x Temporary Access Pass (TAP)" - write-host " - $certificateBasedAuthConfiguration x Certificate Based Auth Configuration" + $temporaryAccessPassAuthenticationMethod = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.temporaryAccessPass -eq $true } | Measure-Object).Count + $certificateBasedAuthConfiguration = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.certificateBasedAuthConfiguration -eq $true } | Measure-Object).Count + + $totalUsers = $allUsers.Count + Write-Host "$MFAstatusAmount out of $totalUsers users have MFA enabled:" + Write-Host " - $MFAEmail x Email" + Write-Host " - $MFAfido2 x Fido2" + Write-Host " - $MFAapp x Microsoft Authenticator App" + Write-Host " - $MFAphone x Phone" + Write-Host " - $MFAsoftwareoath x SoftwareOAuth" + Write-Host " - $MFAhellobusiness x HelloBusiness" + Write-Host " - $temporaryAccessPassAuthenticationMethod x Temporary Access Pass (TAP)" + Write-Host " - $certificateBasedAuthConfiguration x Certificate Based Auth Configuration" write-host "" - Write-logFile -Message "[INFO] Retrieving the user registration details" -Color "Green" + Write-logFile -Message "[INFO] Retrieving the user registration details" - $results=@(); - $date = Get-Date -Format "yyyyMMddHHmm" + $results = @() $filePath = "$OutputDir\$($date)-MFA-UserRegistrationDetails.csv" - - Get-MgReportAuthenticationMethodUserRegistrationDetail -all | ForEach-Object { - $myObject = [PSCustomObject]@{ - Id = "-" - IsAdmin = "-" - IsMfaCapable = "-" - IsMfaRegistered = "-" - IsPasswordlessCapable = "-" - IsSsprCapable = "-" - IsSsprEnabled = "-" - IsSsprRegistered = "-" - IsSystemPreferredAuthenticationMethodEnabled = "-" - MethodsRegistered = "-" - SystemPreferredAuthenticationMethods = "-" - UserDisplayName = "-" - UserPreferredMethodForSecondaryAuthentication = "-" - UserPrincipalName = "-" - UserType = "-" - LastUpdatedDateTime = "-" - AdditionalProperties = "-" - } + $nextLink = "https://graph.microsoft.com/v1.0/reports/authenticationMethods/userRegistrationDetails" - $myobject.Id = $_.Id - $myobject.IsAdmin = $_.IsAdmin - $myobject.IsMfaCapable = $_.IsMfaCapable - $myobject.IsMfaRegistered = $_.IsMfaRegistered - $myobject.IsPasswordlessCapable = $_.IsPasswordlessCapable - $myobject.IsSsprCapable = $_.IsSsprCapable - $myobject.IsSsprEnabled = $_.IsSsprEnabled - $myobject.IsSsprRegistered = $_.IsSsprRegistered - $myobject.IsSystemPreferredAuthenticationMethodEnabled = $_.IsSystemPreferredAuthenticationMethodEnabled - $myobject.MethodsRegistered = $_.MethodsRegistered | out-string - $myobject.SystemPreferredAuthenticationMethods = $_.SystemPreferredAuthenticationMethods | out-string - $myobject.UserDisplayName = $_.UserDisplayName - $myobject.UserPreferredMethodForSecondaryAuthentication = $_.UserPreferredMethodForSecondaryAuthentication - $myobject.UserPrincipalName = $_.UserPrincipalName - $myobject.UserType = $_.UserType - $myobject.LastUpdatedDateTime = $_.LastUpdatedDateTime - $myobject.AdditionalProperties = $_.AdditionalProperties | out-string - $results+= $myObject; - } + do { + $response = Invoke-MgGraphRequest -Uri $nextLink -Method Get -OutputType PSObject + $userDetails = $response.value + $nextLink = $response.'@odata.nextLink' + + ForEach ($detail in $userDetails) { + $myObject = [PSCustomObject]@{} + $detail.PSObject.Properties | ForEach-Object { + $myObject | Add-Member -Type NoteProperty -Name $_.Name -Value $_.Value + } + $results += $myObject + } + } while ($nextLink) $results | Export-Csv -Path $filePath -NoTypeInformation -Encoding $Encoding - Write-logFile -Message "[INFO] Output written to $filePath" -Color "Green" -} \ No newline at end of file + Write-LogFile -Message "[INFO] Output written to $filePath" -Color "Green" +} + diff --git a/Scripts/Get-MailItemsAccessed.ps1 b/Scripts/Get-MailItemsAccessed.ps1 index fe6f7ef..4c702c3 100644 --- a/Scripts/Get-MailItemsAccessed.ps1 +++ b/Scripts/Get-MailItemsAccessed.ps1 @@ -43,50 +43,40 @@ Function Get-Sessions { param( [Parameter(Mandatory=$true)]$StartDate, [Parameter(Mandatory=$true)]$EndDate, - [string]$OutputDir, + [string]$OutputDir = "Output\MailItemsAccessed", [string]$UserIds, [string]$IP, - [string]$Encoding, - [string]$Output + [string]$Encoding = "UTF8", + [string]$Output = "No" ) - try { - $areYouConnected = Get-AdminAuditLogConfig -ErrorAction stop - } - catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" - break - } - - if ($OutputDir -eq "" ){ - $OutputDir = "Output\MailItemsAccessed" - if (!(test-path $OutputDir)) { - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null - write-logFile -Message "[INFO] Creating the following directory: $OutputDir" - } + 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" } } - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } - Write-logFile -Message "[INFO] Running Get-Sessions" -Color "Green" if ($UserIds -And !$IP){ $Results = @() - $amountResults = (Search-UnifiedAuditLog -StartDate $StartDate -UserIds $UserIds -EndDate $EndDate -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount) + try { + $amountResults = (Search-UnifiedAuditLog -StartDate $StartDate -UserIds $UserIds -EndDate $EndDate -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount) + } + catch { + write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break + } if($amountResults -gt 4999){ write-logFile -Message "[WARNING] A total of $amountResults events have been identified, surpassing the maximum limit of 5000 results for a single session. To refine your search, kindly provide more specific details, such as specifying a user or IP address." -Color "Red" @@ -122,7 +112,14 @@ Function Get-Sessions { elseif($IP -And !$UserIds){ $Results = @() - $amountResults = (Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -FreeText $IP -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount) + try { + $amountResults = (Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -FreeText $IP -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount) + } + catch { + write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break + } if($amountResults -gt 4999){ write-logFile -Message "[WARNING] A total of $amountResults events have been identified, surpassing the maximum limit of 5000 results for a single session. To refine your search, kindly provide more specific details, such as specifying a user." -Color "Red" @@ -160,7 +157,15 @@ Function Get-Sessions { elseif($IP -And $UserIds){ $Results = @() - $amountResults = (Search-UnifiedAuditLog -UserIds $UserIds -FreeText $IP -StartDate $StartDate -EndDate $EndDate -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount) + try { + $amountResults = (Search-UnifiedAuditLog -UserIds $UserIds -FreeText $IP -StartDate $StartDate -EndDate $EndDate -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount) + } + catch { + write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break + } + if($amountResults -gt 4999){ write-logFile -Message "[WARNING] A total of $amountResults events have been identified, surpassing the maximum limit of 5000 results for a single session. To refine your search, kindly provide a more specific time window." -Color "Red" } @@ -197,7 +202,14 @@ Function Get-Sessions { else{ $Results = @() - $amountResults = (Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount) + try { + $amountResults = (Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount) + } + catch { + write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break + } if($amountResults -gt 4999){ write-logFile -Message "[WARNING] A total of $amountResults events have been identified, surpassing the maximum limit of 5000 results for a single session. To refine your search, kindly provide more specific details, such as specifying a user." -Color "Red" @@ -282,52 +294,43 @@ function Get-MessageIDs { param( [Parameter(Mandatory=$true)]$StartDate, [Parameter(Mandatory=$true)]$EndDate, - [string]$OutputDir, + [string]$OutputDir = "Output\MailItemsAccessed", [string]$IP, - [string]$Encoding, + [string]$Encoding = "UTF8", [string]$Sessions, [string]$Output, - [string]$Download + [string]$Download = "No" ) - try { - $areYouConnected = Get-AdminAuditLogConfig -ErrorAction stop - } - catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" - break - } - - if ($OutputDir -eq "" ){ - $OutputDir = "Output\MailItemsAccessed" - if (!(test-path $OutputDir)) { - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null - write-logFile -Message "[INFO] Creating the following directory: $OutputDir" - } + 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" } } - - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } Write-logFile -Message "[INFO] Running Get-MessageIDs" -Color "Green" $results=@(); if (!$Sessions -And !$IP){ - $amountResults = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount - + + try { + $amountResults = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount + } + catch { + write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break + } + if ($amountResults -gt 4999){ write-logFile -Message "[WARNING] A total of $amountResults events have been identified, surpassing the maximum limit of 5000 results for a single session. To refine your search, kindly lower the time window." -Color "Red" } @@ -398,8 +401,16 @@ function Get-MessageIDs { } elseif ($IP -And $Sessions){ - $amountResults = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -FreeText $IP -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount + try { + $amountResults = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -FreeText $IP -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount + } + catch { + write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break + } + if ($amountResults -gt 4999){ write-logFile -Message "[WARNING] A total of $amountResults events have been identified, surpassing the maximum limit of 5000 results for a single session. To refine your search, kindly lower the time window." -Color "Red" } @@ -478,7 +489,14 @@ function Get-MessageIDs { } elseif ($Sessions -And !$IP){ - $amountResults = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -FreeText $Sessions -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount + try { + $amountResults = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -FreeText $Sessions -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount + } + catch { + write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break + } if ($amountResults -gt 4999){ write-logFile -Message "[WARNING] A total of $amountResults events have been identified, surpassing the maximum limit of 5000 results for a single session. To refine your search, kindly lower the time window." -Color "Red" @@ -553,7 +571,15 @@ function Get-MessageIDs { } elseif (!$Sessions -And $IP){ - $amountResults = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -FreeText $IP -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount + try { + $amountResults = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -FreeText $IP -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount + } + catch { + write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break + } + if ($amountResults -gt 4999){ write-logFile -Message "[WARNING] A total of $amountResults events have been identified, surpassing the maximum limit of 5000 results for a single session. To refine your search, kindly lower the time window." -Color "Red" } @@ -634,7 +660,7 @@ function DownloadMails($iMessageID,$UserIds){ $outputDir = "Output\MailItemsAccessed\Emails" if (!(test-path $outputDir)) { write-logFile -Message "[INFO] Creating the following directory: $outputDir" - New-Item -ItemType Directory -Force -Name $outputDir | Out-Null + New-Item -ItemType Directory -Force -Name $outputDir > $null } } @@ -676,10 +702,5 @@ function DownloadMails($iMessageID,$UserIds){ Write-Host "[WARNING] Error Message: $($_.Exception.Message)" -Color "Red" break } - - - - - } diff --git a/Scripts/Get-MailboxAuditLog.ps1 b/Scripts/Get-MailboxAuditLog.ps1 index 60726f1..3059d4f 100644 --- a/Scripts/Get-MailboxAuditLog.ps1 +++ b/Scripts/Get-MailboxAuditLog.ps1 @@ -48,8 +48,8 @@ function Get-MailboxAuditLog [string]$UserIds, [string]$StartDate, [string]$EndDate, - [string]$OutputDir, - [string]$Encoding + [string]$OutputDir = "Output\MailboxAuditLog", + [string]$Encoding = "UTF8" ) try { @@ -57,17 +57,15 @@ function Get-MailboxAuditLog } catch { write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } write-logFile -Message "[INFO] Running Get-MailboxAuditLog" -Color "Green" - if ($OutputDir -eq "" ){ - $OutputDir = "Output\MailboxAuditLog" - If (!(test-path $OutputDir)){ - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null - write-logFile -Message "[INFO] Creating the following directory: $OutputDir" - } + If (!(test-path $OutputDir)){ + New-Item -ItemType Directory -Force -Name $OutputDir > $null + write-logFile -Message "[INFO] Creating the following directory: $OutputDir" } else { @@ -80,17 +78,13 @@ function Get-MailboxAuditLog write-LogFile -Message "[Error] Custom directory invalid: $OutputDir exiting script" } } - - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } StartDate EndDate if (($null -eq $UserIds) -Or ($UserIds -eq "")) { write-logFile -Message "[INFO] No users provided.. Getting the MailboxAuditLog for all users" -Color "Yellow" - Get-mailbox -resultsize unlimited | + Get-mailbox -resultsize unlimited | ForEach-Object { $date = Get-Date -Format "yyyyMMddHHmm" $outputFile = "$OutputDir\$($date)-mailboxAuditLog-$($_.UserPrincipalName).csv" diff --git a/Scripts/Get-MessageTraceLog.ps1 b/Scripts/Get-MessageTraceLog.ps1 index 12567fb..c3a7edb 100644 --- a/Scripts/Get-MessageTraceLog.ps1 +++ b/Scripts/Get-MessageTraceLog.ps1 @@ -81,8 +81,8 @@ function Get-MessageTraceLog [string]$UserIds, [string]$StartDate, [string]$EndDate, - [string]$OutputDir, - [string]$Encoding + [string]$OutputDir = "Output\MessageTrace", + [string]$Encoding = "UTF8" ) try { @@ -90,6 +90,7 @@ function Get-MessageTraceLog } catch { write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -100,36 +101,23 @@ function Get-MessageTraceLog $date = Get-Date -Format "yyyyMMddHHmm" - if ($OutputDir -eq "" ){ - $OutputDir = "Output\MessageTrace" - if (!(test-path $OutputDir)) { - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null - write-logFile -Message "[INFO] Creating the following directory: $OutputDir" - } + 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" } } - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } - - else { - write-logFile -Message "[INFO] Output directory set to: $OutputDir" -Color "Green" - } - if (($null -eq $UserIds) -Or ($UserIds -eq "")) { write-logFile -Message "[INFO] No users provided. Getting the Message Trace Log for all users between $($script:StartDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK")) and $($script:EndDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK"))" -Color "Yellow" - Get-mailbox -resultsize unlimited | + Get-mailbox -resultsize unlimited | ForEach-Object { $outputFile = "$OutputDir\"+$($_.PrimarySmtpAddress)+"-MTL.csv" diff --git a/Scripts/Get-OAuthPermissions.ps1 b/Scripts/Get-OAuthPermissions.ps1 index 3cf45b3..f5ffff3 100644 --- a/Scripts/Get-OAuthPermissions.ps1 +++ b/Scripts/Get-OAuthPermissions.ps1 @@ -86,30 +86,24 @@ Lists delegated permissions (OAuth2PermissionGrants) and application permissions [string[]] $ServicePrincipalProperties = @("DisplayName"), [switch] $ShowProgress = $true, [int] $PrecacheSize = 999, - [string] $OutputDir, - [string] $Encoding + [string] $OutputDir = "Output\OAuthPermissions", + [string] $Encoding = "UTF8" ) try { $tenant_details = Get-AzureADTenantDetail -ErrorAction stop } catch { write-logFile -Message "[WARNING] You must call Connect-Azure before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } - write-logFile -Message "[INFO] Running Get-OAuthPermissions" -Color "Green" $date = Get-Date -Format "ddMMyyyyHHmmss" - if ($OutputDir -eq "" ){ - $OutputDir = "Output\OAuthPermissions" - if (!(test-path $OutputDir)) { - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null - write-logFile -Message "[INFO] Creating the following directory: $OutputDir" - } + if (!(test-path $OutputDir)) { + New-Item -ItemType Directory -Force -Name $OutputDir > $null + write-logFile -Message "[INFO] Creating the following directory: $OutputDir" } else { @@ -304,10 +298,9 @@ Lists delegated permissions (OAuth2PermissionGrants) and application permissions } } } - ) +) - - $report | ConvertTo-Csv | Format-Table | out-null + $report | ConvertTo-Csv | Format-Table > $null $prop = $report.ForEach{ $_.PSObject.Properties.Name } | Select-Object -Unique $report | Select-Object $prop | Export-CSV -NoTypeInformation -Path "$OutputDir\$($date)-OAuthPermissions.csv" -Encoding $Encoding diff --git a/Scripts/Get-RiskyEvents.ps1 b/Scripts/Get-RiskyEvents.ps1 index 872af9d..88f1414 100644 --- a/Scripts/Get-RiskyEvents.ps1 +++ b/Scripts/Get-RiskyEvents.ps1 @@ -5,27 +5,18 @@ function Get-RiskyUsers { .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. - The output will be written to: Output\UserInfo\ .PARAMETER OutputDir OutputDir is the parameter specifying the output directory. - Default: Output\UserInfo + Default: Output\RiskyEvents .PARAMETER Encoding Encoding is the parameter specifying the encoding of the CSV output file. Default: UTF8 - - .PARAMETER Application - Application is the parameter specifying App-only access (access without a user) for authentication and authorization. - Default: Delegated access (access on behalf a user) .EXAMPLE Get-RiskyUsers Retrieves all risky users. - - .EXAMPLE - Get-RiskyUsers -Application - Retrieves all risky users via application authentication. .EXAMPLE Get-RiskyUsers -Encoding utf32 @@ -37,40 +28,23 @@ function Get-RiskyUsers { #> [CmdletBinding()] param( - [string]$OutputDir, - [string]$Encoding, - [switch]$Application + [string]$OutputDir = "Output\RiskyEvents", + [string]$Encoding = "UTF8" ) - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } - - if (!($Application.IsPresent)) { - Connect-MgGraph -Scopes IdentityRiskEvent.Read.All -NoWelcome - } - - try { - $areYouConnected = Get-MgRiskyUser -ErrorAction stop - } - catch { - Write-logFile -Message "[WARNING] You must call Connect-GraphAPI -Scopes IdentityRiskyUser.Read.All before running this script" -Color "Red" - break + $authType = Get-GraphAuthType + if ($authType -eq "Delegated") { + Connect-MgGraph -Scopes IdentityRiskEvent.Read.All,IdentityRiskyServicePrincipal.Read.All,IdentityRiskyUser.Read.All -NoWelcome > $null } - if ($OutputDir -eq "" ){ - $OutputDir = "Output\UserInfo" - if (!(test-path $OutputDir)) { - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null - write-logFile -Message "[INFO] Creating the following directory: $OutputDir" - } + 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" @@ -80,43 +54,49 @@ function Get-RiskyUsers { Write-logFile -Message "[INFO] Running Get-RiskyUsers" -Color "Green" $results=@(); $count = 0 - - Get-MgRiskyUser -All | ForEach-Object { - $myObject = [PSCustomObject]@{ - History = "-" - Id = "-" - IsDeleted = "-" - IsProcessing = "-" - RiskDetail = "-" - RiskLastUpdatedDateTime = "-" - RiskLevel = "-" - RiskState = "-" - UserDisplayName = "-" - UserPrincipalName = "-" - AdditionalProperties = "-" - } - - $myobject.History = $_.History - $myobject.Id = $_.Id - $myobject.IsDeleted = $_.IsDeleted - $myobject.IsProcessing = $_.IsProcessing - $myobject.RiskDetail = $_.RiskDetail - $myobject.RiskLastUpdatedDateTime = $_.RiskLastUpdatedDateTime - $myobject.RiskLevel = $_.RiskLevel - $myobject.RiskState = $_.RiskState - $myobject.UserDisplayName = $_.UserDisplayName - $myobject.UserPrincipalName = $_.UserPrincipalName - $myobject.AdditionalProperties = $_.AdditionalProperties | out-string - - $results+= $myObject; - $count = $count +1 + + try { + $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 "[WARNING] You must call Connect-MgGraph -Scopes IdentityRiskEvent.Read.All,IdentityRiskyServicePrincipal.Read.All,IdentityRiskyUser.Read.All before running this script" -Color "Red" + Write-LogFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break } $date = Get-Date -Format "yyyyMMddHHmm" $filePath = "$OutputDir\$($date)-RiskyUsers.csv" - $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" + + 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 { @@ -126,27 +106,18 @@ function Get-RiskyDetections { .DESCRIPTION Retrieves the risky detections from the Entra ID Identity Protection. - The output will be written to: Output\UserInfo\ .PARAMETER OutputDir OutputDir is the parameter specifying the output directory. - Default: Output\UserInfo + Default: Output\RiskyEvents .PARAMETER Encoding Encoding is the parameter specifying the encoding of the CSV output file. Default: UTF8 - - .PARAMETER Application - Application is the parameter specifying App-only access (access without a user) for authentication and authorization. - Default: Delegated access (access on behalf a user) .EXAMPLE Get-RiskyDetections Retrieves all the risky detections. - - .EXAMPLE - Get-RiskyDetections -Application - Retrieves all the risky detections via application authentication. .EXAMPLE Get-RiskyDetections -Encoding utf32 @@ -158,33 +129,18 @@ function Get-RiskyDetections { #> [CmdletBinding()] param( - [string]$OutputDir, - [string]$Encoding, - [switch]$Application + [string]$OutputDir= "Output\RiskyEvents", + [string]$Encoding = "UTF8" ) - if (!($Application.IsPresent)) { - Connect-MgGraph -Scopes IdentityRiskEvent.Read.All -NoWelcome - } - - try { - $areYouConnected = Get-MgRiskDetection -ErrorAction stop - } - catch { - Write-logFile -Message "[WARNING] You must call Connect-GraphAPI -Scopes IdentityRiskEvent.Read.All before running this script" -Color "Red" - break - } - - if ($Encoding -eq "" ){ - $Encoding = "UTF8" + $authType = Get-GraphAuthType + if ($authType -eq "Delegated") { + Connect-MgGraph -Scopes IdentityRiskEvent.Read.All,IdentityRiskyServicePrincipal.Read.All,IdentityRiskyUser.Read.All -NoWelcome > $null } - if ($OutputDir -eq "" ){ - $OutputDir = "Output\UserInfo" - if (!(test-path $OutputDir)) { - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null - write-logFile -Message "[INFO] Creating the following directory: $OutputDir" - } + if (!(test-path $OutputDir)) { + New-Item -ItemType Directory -Force -Name $OutputDir > $null + write-logFile -Message "[INFO] Creating the following directory: $OutputDir" } else { @@ -201,64 +157,58 @@ function Get-RiskyDetections { Write-logFile -Message "[INFO] Running Get-RiskyDetections" -Color "Green" $results=@(); $count = 0 - Get-MgRiskDetection -All | ForEach-Object { - $myObject = [PSCustomObject]@{ - Activity = "-" - ActivityDateTime = "-" - AdditionalInfo = "-" - CorrelationId = "-" - DetectedDateTime = "-" - IPAddress = "-" - Id = "-" - LastUpdatedDateTime = "-" - City = "-" - CountryOrRegion = "-" - State = "-" - RequestId = "-" - RiskDetail = "-" - RiskEventType = "-" - RiskLevel = "-" - riskState = "-" - detectionTimingType = "-" - Source = "-" - TokenIssuerType = "-" - UserDisplayName = "-" - UserId = "-" - UserPrincipalName = "-" - AdditionalProperties = "-" - } - $myobject.Activity = $_.Activity - $myobject.ActivityDateTime = $_.ActivityDateTime - $myobject.AdditionalInfo = $_.AdditionalInfo - $myobject.CorrelationId = $_.CorrelationId - $myobject.DetectedDateTime = $_.DetectedDateTime - $myobject.IPAddress = $_.IPAddress - $myobject.Id = $_.Id - $myobject.LastUpdatedDateTime = $_.LastUpdatedDateTime - $myobject.City = $_.Location.City | out-string - $myobject.CountryOrRegion = $_.Location.CountryOrRegion | out-string - $myobject.State = $_.Location.State | out-string - $myobject.RequestId = $_.RequestId - $myobject.RiskDetail = $_.RiskDetail - $myobject.RiskEventType = $_.RiskEventType - $myobject.RiskLevel = $_.RiskLevel - $myobject.riskState = $_.riskState - $myobject.detectionTimingType = $_.detectionTimingType - $myobject.Source = $_.Source - $myobject.TokenIssuerType = $_.TokenIssuerType - $myobject.UserDisplayName = $_.UserDisplayName - $myobject.UserId = $_.UserId - $myobject.UserPrincipalName = $_.UserPrincipalName - $myobject.AdditionalProperties = $_.AdditionalProperties | out-string - - $results+= $myObject; - $count = $count +1 + try { + $uri = "https://graph.microsoft.com/v1.0/identityProtection/riskDetections" + 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) + } catch { + Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes IdentityRiskEvent.Read.All,IdentityRiskyServicePrincipal.Read.All,IdentityRiskyUser.Read.All before running this script" -Color "Red" + Write-LogFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break } $date = Get-Date -Format "yyyyMMddHHmm" $filePath = "$OutputDir\$($date)-RiskyDetections.csv" - $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" + 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" + } } diff --git a/Scripts/Get-Rules.ps1 b/Scripts/Get-Rules.ps1 index 5ef9f7f..20848aa 100644 --- a/Scripts/Get-Rules.ps1 +++ b/Scripts/Get-Rules.ps1 @@ -52,23 +52,18 @@ function Get-TransportRules [CmdletBinding()] param ( - [string]$OutputDir, - [string]$Encoding + [string]$OutputDir = "Output\Rules" , + [string]$Encoding = "UTF8" ) - if ($OutputDir -eq "" ){ - $OutputDir = "Output\Rules" - if (!(test-path $OutputDir)) { - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null - write-LogFile -Message "[INFO] Creating the following directory: $OutputDir" - } + 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" @@ -76,12 +71,7 @@ function Get-TransportRules } $filename = "$($date)-TransportRules.csv" - $outputDirectory = Join-Path $OutputDir $filename - - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } - + $outputDirectory = Join-Path $OutputDir $filename $transportRules = Get-TransportRule | Select-Object -Property Name,Description,CreatedBy,WhenChanged,State if ($null -ne $transportRules) { @@ -209,22 +199,14 @@ function Get-MailboxRules [CmdletBinding()] param( [string]$UserIds, - [string]$OutputDir, - [string]$Encoding + [string]$OutputDir = "Output\Rules", + [string]$Encoding = "UTF8" ) $RuleList = @() - - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } - - if ($OutputDir -eq "" ){ - $OutputDir = "Output\Rules" - if (!(test-path $OutputDir)) { - write-LogFile -Message "[INFO] Creating the following directory: $OutputDir" - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null - } + if (!(test-path $OutputDir)) { + write-LogFile -Message "[INFO] Creating the following directory: $OutputDir" + New-Item -ItemType Directory -Force -Name $OutputDir > $null } else{ diff --git a/Scripts/Get-UAL.ps1 b/Scripts/Get-UAL.ps1 index cc55dd8..33c3b78 100644 --- a/Scripts/Get-UAL.ps1 +++ b/Scripts/Get-UAL.ps1 @@ -63,7 +63,7 @@ function Get-UALAll .EXAMPLE Get-UALAll -UserIds Test@invictus-ir.com -MergeOutput - Gets all the unified audit log entries for the user Test@invictus-ir.com and adds a combined output csv file at the end of acquisition + Gets all the unified audit log entries for the user Test@invictus-ir.com and adds a combined output JSON file at the end of acquisition .EXAMPLE Get-UALAll -UserIds Test@invictus-ir.com -Output JSON @@ -71,22 +71,23 @@ function Get-UALAll #> [CmdletBinding()] - param( - [string]$StartDate, - [string]$EndDate, - [string]$UserIds, - [string]$Interval, - [string]$Output, - [switch]$MergeOutput, - [string]$OutputDir, - [string]$Encoding - ) + param ( + [string]$StartDate, + [string]$EndDate, + [string]$UserIds = "*", + [int]$Interval = 720, + [string]$Output = "CSV", + [switch]$MergeOutput, + [string]$OutputDir, + [string]$Encoding = "UTF8" + ) try { $areYouConnected = Get-AdminAuditLogConfig -ErrorAction stop } catch { write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -95,33 +96,12 @@ function Get-UALAll StartDate EndDate - if ($UserIds -eq "") { - $UserIds = "*" - } - - if ($Interval -eq "") { - $Interval = 720 - Write-LogFile -Message "[INFO] Setting the Interval to the default value of 720" - } - - if ($Output -eq "JSON") { - $Output = "JSON" - Write-LogFile -Message "[INFO] Output set to JSON" - } else { - $Output = "CSV" - Write-LogFile -Message "[INFO] Output set to CSV" - } - - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } - $date = [datetime]::Now.ToString('yyyyMMddHHmmss') if ($OutputDir -eq "" ){ $OutputDir = "Output\UnifiedAuditLog\$date" If (!(test-path $OutputDir)) { Write-LogFile -Message "[INFO] Creating the following directory: $OutputDir" - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null + New-Item -ItemType Directory -Force -Name $OutputDir > $null } } @@ -147,7 +127,7 @@ function Get-UALAll $currentEnd = $currentStart.AddMinutes($Interval) $amountResults = Search-UnifiedAuditLog -UserIds $UserIds -StartDate $currentStart -EndDate $currentEnd -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount - if ($amountResults -eq $null) { + if ($null -eq $amountResults) { Write-LogFile -Message "[INFO] No audit logs between $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK")) and $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK")). Moving on!" $CurrentStart = $CurrentEnd } @@ -208,14 +188,13 @@ function Get-UALAll $message = "[INFO] Successfully retrieved $($currentCount) records out of total $($currentTotal) for the current time range. Moving on!" if ($Output -eq "JSON") { - $results = $results|Select-Object AuditData -ExpandProperty AuditData + $results = $results | Select-Object AuditData -ExpandProperty AuditData $results | Out-File -Append "$OutputDir/UAL-$sessionID.json" -Encoding $Encoding Write-LogFile -Message $message -Color "Green" } elseif ($Output -eq "CSV") { $results | export-CSV "$OutputDir/UAL-$sessionID.csv" -NoTypeInformation -Append -Encoding $Encoding - Write-LogFile -Message $message -Color "Green" } @@ -227,16 +206,9 @@ function Get-UALAll } } - if ($Output -eq "CSV" -and ($MergeOutput.IsPresent)) - { + if ($Output -eq "CSV" -and ($MergeOutput.IsPresent)) { Write-LogFile -Message "[INFO] Merging output files into one file" - $outputDirMerged = "$OutputDir\Merged\" - If (!(test-path $outputDirMerged)) { - Write-LogFile -Message "[INFO] Creating the following directory: $outputDirMerged" - New-Item -ItemType Directory -Force -Path $outputDirMerged | Out-Null - } - - Get-ChildItem $OutputDir -Filter *.csv | Select-Object -ExpandProperty FullName | Import-Csv | Export-Csv "$outputDirMerged/UAL-Combined.csv" -NoTypeInformation -Append + Merge-OutputFiles -OutputDir $OutputDir -OutputType "CSV" -MergedFileName "UAL-Combined.csv" } Write-LogFile -Message "[INFO] Acquisition complete, check the Output directory for your files.." -Color "Green" @@ -309,19 +281,19 @@ function Get-UALGroup .EXAMPLE Get-UALGroup -Group Exchange -MergeOutput - Gets the Azure related unified audit log entries and adds a combined output csv file at the end of acquisition + Gets the Azure related unified audit log entries and adds a combined output JSON file at the end of acquisition #> [CmdletBinding()] param( [string]$StartDate, [string]$EndDate, - [string]$UserIds, - [string]$Interval, + [string]$UserIds = "*", + [string]$Interval = 1440, [string]$Group, - [string]$Output, + [string]$Output = "CSV", [switch]$MergeOutput, [string]$OutputDir, - [string]$Encoding + [string]$Encoding = "UTF8" ) try { @@ -329,6 +301,7 @@ function Get-UALGroup } catch { write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -361,32 +334,11 @@ function Get-UALGroup StartDate EndDate - if ($UserIds -eq "") { - $UserIds = "*" - } - - if ($Interval -eq "") { - $Interval = 1440 - write-logFile -Message "[INFO] Setting the Interval to the default value of 1440" - } - - if ($Output -eq "JSON") { - $Output = "JSON" - write-logFile -Message "[INFO] Output type set to JSON" - } else { - $Output = "CSV" - Write-LogFile -Message "[INFO] Output set to CSV" - } - - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } - if ($OutputDir -eq "" ){ $OutputDir = "Output\UnifiedAuditLog\$recordFile" if (!(test-path $OutputDir)) { write-logFile -Message "[INFO] Creating the following directory: $OutputDir" - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null + New-Item -ItemType Directory -Force -Name $OutputDir > $null } } @@ -423,7 +375,7 @@ function Get-UALGroup $currentEnd = $currentStart.AddMinutes($Interval) $amountResults = Search-UnifiedAuditLog -UserIds $UserIds -StartDate $currentStart -EndDate $currentEnd -RecordType $record -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount - if ($amountResults -eq $null) { + if ($null -eq $amountResults) { Write-LogFile -Message "[INFO] No audit logs between $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK")) and $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK")). Moving on!" $CurrentStart = $CurrentEnd } @@ -508,17 +460,10 @@ function Get-UALGroup Write-LogFile -message "[INFO] No Records found for $Record" } } - if ($Output -eq "CSV" -and ($MergeOutput.IsPresent)) - { + if ($Output -eq "CSV" -and ($MergeOutput.IsPresent)) { Write-LogFile -Message "[INFO] Merging output files into one file" - $outputDirMerged = "$OutputDir\Merged\" - If (!(test-path $outputDirMerged)) { - Write-LogFile -Message "[INFO] Creating the following directory: $outputDirMerged" - New-Item -ItemType Directory -Force -Path $outputDirMerged | Out-Null - } - - Get-ChildItem $OutputDir -Filter *.csv | Select-Object -ExpandProperty FullName | Import-Csv | Export-Csv "$outputDirMerged/UAL-Combined.csv" -NoTypeInformation -Append - } + Merge-OutputFiles -OutputDir $OutputDir -OutputType "CSV" -MergedFileName "UAL-Combined.csv" + } Write-LogFile -Message "[INFO] Acquisition complete, check the Output directory for your files.." -Color "Green" } @@ -562,11 +507,11 @@ function Get-UALSpecific Default: Output\UnifiedAuditLog .PARAMETER Encoding - Encoding is the parameter specifying the encoding of the CSV/JSON output file. + Encoding is the parameter specifying the encoding of the CSV output file. Default: UTF8 .PARAMETER MergeOutput - MergeOutput is the parameter specifying if you wish to merge CSV outputs to a single file + MergeOutput is the parameter specifying if you wish to merge CSV/JSON outputs to a single file .EXAMPLE Get-UALSpecific -RecordType ExchangeItem @@ -590,19 +535,19 @@ function Get-UALSpecific .EXAMPLE Get-UALSpecific -RecordType MipAutoLabelExchangeItem -MergeOutput - Gets the ExchangeItem logging from the unified audit log and adds a combined output csv file at the end of acquisition + Gets the ExchangeItem logging from the unified audit log and adds a combined output JSON file at the end of acquisition #> [CmdletBinding()] param( [string]$StartDate, [string]$EndDate, - [string]$UserIds, - [string]$Interval, + [string]$UserIds = "*", + [string]$Interval = 1440, [Parameter(Mandatory=$true)]$RecordType, - [string]$Output, + [string]$Output = "CSV", [switch]$MergeOutput, [string]$OutputDir, - [string]$Encoding + [string]$Encoding = "UTF8" ) try { @@ -610,6 +555,7 @@ function Get-UALSpecific } catch { write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -617,20 +563,8 @@ function Get-UALSpecific StartDate EndDate - - if ($UserIds -eq "") - { - $UserIds = "*" - } - - if ($interval -eq "") - { - $Interval = 1440 - write-logFile -Message "[INFO] Setting the Interval to the default value of 1440" - } - - if ($Output -eq "JSON") - { + + if ($Output -eq "JSON") { $Output = "JSON" write-logFile -Message "[INFO] Output set to JSON" } @@ -639,10 +573,6 @@ function Get-UALSpecific Write-LogFile -Message "[INFO] Output set to CSV" } - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } - write-logFile -Message "[INFO] Extracting all available audit logs between $($script:StartDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK")) and $($script:EndDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK"))" write-logFile -Message "[INFO] The following RecordType(s) are configured to be extracted:" @@ -663,7 +593,7 @@ function Get-UALSpecific $OutputDir = "Output\UnifiedAuditLog\$record" if (!(test-path $OutputDir)) { write-logFile -Message "[INFO] Creating the following output directory: $OutputDir" - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null + New-Item -ItemType Directory -Force -Name $OutputDir > $null } } @@ -686,7 +616,7 @@ function Get-UALSpecific $amountResults = Search-UnifiedAuditLog -UserIds $UserIds -StartDate $currentStart -EndDate $currentEnd -RecordType $record -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount - if ($amountResults -eq $null) { + if ($null -eq $amountResults) { Write-LogFile -Message "[INFO] No audit logs between $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK")) and $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK")). Moving on!" $CurrentStart = $CurrentEnd } @@ -765,18 +695,10 @@ function Get-UALSpecific } } - if ($Output -eq "CSV" -and ($MergeOutput.IsPresent)) - { - Write-LogFile -Message "[INFO] Merging output files into one file" - $outputDirMerged = "$OutputDir\Merged\" - write-host $outputDirMerged - If (!(test-path $outputDirMerged)) { - Write-LogFile -Message "[INFO] Creating the following directory: $outputDirMerged" - New-Item -ItemType Directory -Force -Path $outputDirMerged | Out-Null - } - - Get-ChildItem $OutputDir -Filter *.csv | Select-Object -ExpandProperty FullName | Import-Csv | Export-Csv "$outputDirMerged/UAL-Combined.csv" -NoTypeInformation -Append - } + if ($Output -eq "CSV" -and ($MergeOutput.IsPresent)) { + Write-LogFile -Message "[INFO] Merging output files into one file" + Merge-OutputFiles -OutputDir $OutputDir -OutputType "CSV" -MergedFileName "UAL-Combined.csv" + } Write-LogFile -Message "[INFO] Acquisition complete, check the Output directory for your files.." -Color "Green" } @@ -847,12 +769,12 @@ function Get-UALSpecificActivity param( [string]$StartDate, [string]$EndDate, - [string]$UserIds, - [string]$Interval, + [string]$UserIds = "*", + [string]$Interval = 1440, [Parameter(Mandatory=$true)]$ActivityType, - [string]$Output, + [string]$Output = "CSV", [string]$OutputDir, - [string]$Encoding + [string]$Encoding = "UTF8" ) try { @@ -860,6 +782,7 @@ function Get-UALSpecificActivity } catch { write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -868,19 +791,7 @@ function Get-UALSpecificActivity StartDate EndDate - if ($UserIds -eq "") - { - $UserIds = "*" - } - - if ($interval -eq "") - { - $Interval = 1440 - write-logFile -Message "[INFO] Setting the Interval to the default value of 1440" - } - - if ($Output -eq "JSON") - { + if ($Output -eq "JSON") { $Output = "JSON" write-logFile -Message "[INFO] Output set to JSON" } @@ -890,10 +801,6 @@ function Get-UALSpecificActivity write-logFile -Message "[INFO] Output set to CSV" } - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } - write-logFile -Message "[INFO] Extracting all available audit logs between $($script:StartDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK")) and $($script:EndDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK"))" write-logFile -Message "[INFO] The following ActivityType(s) are configured to be extracted:" @@ -914,7 +821,7 @@ function Get-UALSpecificActivity $OutputDir = "Output\UnifiedAuditLog\$record\" if (!(test-path $OutputDir)) { write-logFile -Message "[INFO] Creating the following output directory: $OutputDir" - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null + New-Item -ItemType Directory -Force -Name $OutputDir > $null } } @@ -937,7 +844,7 @@ function Get-UALSpecificActivity $amountResults = Search-UnifiedAuditLog -UserIds $UserIds -StartDate $currentStart -EndDate $currentEnd -Operations $record -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount - if ($amountResults -eq $null) { + if ($null -eq $amountResults) { Write-LogFile -Message "[INFO] No audit logs between $($currentStart.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK")) and $($currentEnd.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK")). Moving on!" $CurrentStart = $CurrentEnd } diff --git a/Scripts/Get-UALGraph.ps1 b/Scripts/Get-UALGraph.ps1 index a288ced..a9c5a3a 100644 --- a/Scripts/Get-UALGraph.ps1 +++ b/Scripts/Get-UALGraph.ps1 @@ -26,10 +26,6 @@ Function Get-UALGraph { Encoding is the parameter specifying the encoding of the CSV/JSON output file. Default: UTF8 - .PARAMETER Application - Application is the parameter specifying App-only access (access without a user) for authentication and authorization. - Default: Delegated access (access on behalf a user) - .PARAMETER RecordType The RecordType parameter filters the log entries by record type. Options are: ExchangeItem, ExchangeAdmin, etc. A total of 236 RecordTypes are supported. @@ -52,28 +48,27 @@ Function Get-UALGraph { Specifies the name of the search query. This parameter is required. .EXAMPLE - Get-UALGraph -SearchName Test + Get-UALGraph -searchName Test Gets all the unified audit log entries. .EXAMPLE - Get-UALGraph -SearchName Test -UserIds Test@invictus-ir.com + Get-UALGraph -searchName Test -UserIds Test@invictus-ir.com Gets all the unified audit log entries for the user Test@invictus-ir.com. .EXAMPLE - Get-UALGraph -SearchName Scan1GraphAPI -startDate "2024-03-10T09:28:56Z" -endDate "2024-03-20T09:28:56Z" -Service Exchange + Get-UALGraph -searchName Test -startDate "2024-03-10T09:28:56Z" -endDate "2024-03-20T09:28:56Z" -Service Exchange Retrieves audit log data for the specified time range March 10, 2024 to March 20, 2024 and filters the results to include only events related to the Exchange service. .EXAMPLE - Get-UALGraph -searchName scan1 -startDate "2024-03-01" -endDate "2024-03-10" -IPAddress 182.74.242.26 + Get-UALGraph -searchName Test -startDate "2024-03-01" -endDate "2024-03-10" -IPAddress 182.74.242.26 Retrieve audit log data for the specified time range March 1, 2024 to March 10, 2024 and filter the results to include only entries associated with the IP address 182.74.242.26. #> [CmdletBinding()] param( [Parameter(Mandatory=$true)]$searchName, - [switch]$Application, - [string]$OutputDir, - [string]$Encoding, + [string]$OutputDir = "Output\UnifiedAuditLog\", + [string]$Encoding = "UTF8", [string]$startDate, [string]$endDate, [string[]]$RecordType = @(), @@ -84,31 +79,19 @@ Function Get-UALGraph { [string[]]$IPAddress = @() ) - if (!($Application.IsPresent)) { - Connect-MgGraph -Scopes AuditLogsQuery.Read.All -NoWelcome + $authType = Get-GraphAuthType + if ($authType -eq "Delegated") { + Connect-MgGraph -Scopes AuditLogsQuery.Read.All > $null } - try { - $areYouConnected = Get-MgBetaSecurityAuditLogQuery -ErrorAction stop - } - catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes 'AuditLogsQuery.Read.All' before running this script" -Color "Red" - break - } - - if ($OutputDir -eq "" ){ - $OutputDir = "Output\UnifiedAuditLog\" - if (!(test-path $OutputDir)) { - write-logFile -Message "[INFO] Creating the following directory: $OutputDir" - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null - } + if (!(test-path $OutputDir)) { + write-logFile -Message "[INFO] Creating the following directory: $OutputDir" + New-Item -ItemType Directory -Force -Name $OutputDir > $null } - 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" @@ -122,15 +105,10 @@ Function Get-UALGraph { write-logFile -Message "[INFO] Running Get-UALGraph" -Color "Green" - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } - - $params = - @{ + $body = @{ "@odata.type" = "#microsoft.graph.security.auditLogQuery" displayName = $searchName - filterStartDateTime = $script:StartDate + filterStartDateTime = $script:startDate filterEndDateTime = $script:endDate recordTypeFilters = $RecordType keywordFilter = $Keyword @@ -141,68 +119,71 @@ Function Get-UALGraph { objectIdFilters = @() administrativeUnitIdFilters = @() status = "" - } + } | ConvertTo-Json - $startScan = New-MgBetaSecurityAuditLogQuery -BodyParameter $params - write-logFile -Message "[INFO] New Unfied Audit Log Search started with the name: $searchName and Id: $($startScan.Id)" -Color "Green" - Start-Sleep -Seconds 10 - - do { - $auditLogQuery = Get-MgBetaSecurityAuditLogQuery -AuditLogQueryId $startScan.Id - $scanId = $startScan.Id - - if ($auditLogQuery.Status -eq "running") { - write-logFile -Message "[INFO] Unified Audit Log search is stil running. Waiting..." - Start-Sleep -Seconds 10 - } - elseif ($auditLogQuery.Status -eq "failed") { - write-logFile -Message "[INFO] Unified Audit Log search failed." -Color "Red" - exit 1 - } - elseif ($auditLogQuery.Status -eq "succeeded") { - write-logFile -Message "[INFO] Unified Audit Log search succeeded." -Color "Green" - DownloadUAL $scanId $searchName $Encoding $OutputDir - } - else { - write-logFile -Message "[INFO] Unified Audit Log search is stil running. Waiting..." - Start-Sleep -Seconds 10 - } - } until ($auditLogQuery.Status -eq "succeeded") -} - -Function DownloadUAL($scanId, $searchName, $Encoding, $OutputDir) { - $date = Get-Date -Format "yyyyMMddHHmm" - $outputFilePath = "$($date)-$searchName-UnifiedAuditLog.json" - $customObjects = @() - - Get-MgBetaSecurityAuditLogQueryRecord -AuditLogQueryId $scanId -All | - ForEach-Object { - $customObject = New-Object PSObject -Property @{ - AdministrativeUnits = $_.AdministrativeUnits - AuditData = $_ | Select-Object -ExpandProperty AuditData - AuditLogRecordType = $_.AuditLogRecordType - ClientIP = $_.ClientIP - CreatedDateTime = $_.CreatedDateTime - Id = $_.Id - ObjectId = $_.ObjectId - Operation = $_.Operation - OrganizationId = $_.OrganizationId - Service = $_.Service - UserId = $_.UserId - UserPrincipalName = $_.UserPrincipalName - UserType = $_.UserType - AdditionalProperties = $_.AdditionalProperties + try { + $response = Invoke-MgGraphRequest -Method POST -Uri "https://graph.microsoft.com/beta/security/auditLog/queries" -Body $body -ContentType "application/json" + $scanId = $response.id + write-logFile -Message "[INFO] A new Unified Audit Log search has started with the name: $searchName and ID: $scanId." -Color "Green" + + Start-Sleep -Seconds 10 + $apiUrl = "https://graph.microsoft.com/beta/security/auditLog/queries/$scanId" + + write-logFile -Message "[INFO] Waiting for the scan to start..." + $lastStatus = "" + do { + $response = Invoke-MgGraphRequest -Method Get -Uri $apiUrl -ContentType 'application/json' + $status = $response.status + if ($status -ne $lastStatus) { + $lastStatus = $status } - - $customObjects += $customObject - } - - $customObjects | ConvertTo-Json -Depth 100 | Out-File -Append "$OutputDir/$($date)-$searchName-UnifiedAuditLog.json" -Encoding $Encoding + Start-Sleep -Seconds 5 + } while ($status -ne "succeeded" -and $status -ne "running") + if ($status -eq "running") { + write-logFile -Message "[INFO] Unified Audit Log search has started... This can take a while..." + do { + $response = Invoke-MgGraphRequest -Method Get -Uri $apiUrl -ContentType 'application/json' + $status = $response.status + if ($status -ne $lastStatus) { + write-logFile -Message "[INFO] Unified Audit Log search is still running. Waiting..." + $lastStatus = $status + } + Start-Sleep -Seconds 5 + } while ($status -ne "succeeded") + } + } + catch { + Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes 'AuditLogsQuery.Read.All' before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break + } - write-logFile -Message "[INFO] Audit log records have been saved to $outputFilePath" -Color "Green" - $endTime = Get-Date - $runtime = $endTime - $script:startTime - write-logFile -Message "[INFO] Total runtime (HH:MM:SS): $($runtime.Hours):$($runtime.Minutes):$($runtime.Seconds)" -Color "Green" + try { + $date = [datetime]::Now.ToString('yyyyMMddHHmmss') + $outputFilePath = "$($date)-$searchName-UnifiedAuditLog.json" + $apiUrl = "https://graph.microsoft.com/beta/security/auditLog/queries/$scanId/records" + + Do { + $response = Invoke-MgGraphRequest -Method Get -Uri $apiUrl -ContentType 'application/json' + if ($response.value) { + $filePath = Join-Path -Path $OutputDir -ChildPath $outputFilePath + $response.value | ConvertTo-Json -Depth 100 | Out-File -FilePath $filePath -Append -Encoding $Encoding + + } else { + Write-logFile -Message "[INFO] No results matched your search." -color Yellow + } + $apiUrl = $response.'@odata.nextLink' + } While ($apiUrl) + + write-logFile -Message "[INFO] Audit log records have been saved to $outputFilePath" -Color "Green" + $endTime = Get-Date + $runtime = $endTime - $script:startTime + write-logFile -Message "[INFO] Total runtime (HH:MM:SS): $($runtime.Hours):$($runtime.Minutes):$($runtime.Seconds)" -Color "Green" + } + catch { + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break + } } diff --git a/Scripts/Get-UALStatistics.ps1 b/Scripts/Get-UALStatistics.ps1 index 8687959..dd6da9b 100644 --- a/Scripts/Get-UALStatistics.ps1 +++ b/Scripts/Get-UALStatistics.ps1 @@ -31,41 +31,25 @@ function Get-UALStatistics #> [CmdletBinding()] param( - [string]$UserIds, + [string]$UserIds = "*", [string]$StartDate, [string]$EndDate, - [string]$OutputDir + [string]$OutputDir = "Output\" ) - try { - $areYouConnected = Get-AdminAuditLogConfig -ErrorAction stop - } - catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" - break - } - write-logFile -Message "[INFO] Running Get-UALStatistics" -Color "Green" StartDate EndDate - - if ($UserIds -eq "") { - $UserIds = "*" - } - + $recordTypes = "ExchangeAdmin","ExchangeItem","ExchangeItemGroup","SharePoint","SyntheticProbe","SharePointFileOperation","OneDrive","AzureActiveDirectory","AzureActiveDirectoryAccountLogon","DataCenterSecurityCmdlet","ComplianceDLPSharePoint","Sway","ComplianceDLPExchange","SharePointSharingOperation","AzureActiveDirectoryStsLogon","SkypeForBusinessPSTNUsage","SkypeForBusinessUsersBlocked","SecurityComplianceCenterEOPCmdlet","ExchangeAggregatedOperation","PowerBIAudit","CRM","Yammer","SkypeForBusinessCmdlets","Discovery","MicrosoftTeams","ThreatIntelligence","MailSubmission","MicrosoftFlow","AeD","MicrosoftStream","ComplianceDLPSharePointClassification","ThreatFinder","Project","SharePointListOperation","SharePointCommentOperation","DataGovernance","Kaizala","SecurityComplianceAlerts","ThreatIntelligenceUrl","SecurityComplianceInsights","MIPLabel","WorkplaceAnalytics","PowerAppsApp","PowerAppsPlan","ThreatIntelligenceAtpContent","LabelContentExplorer","TeamsHealthcare","ExchangeItemAggregated","HygieneEvent","DataInsightsRestApiAudit","InformationBarrierPolicyApplication","SharePointListItemOperation","SharePointContentTypeOperation","SharePointFieldOperation","MicrosoftTeamsAdmin","HRSignal","MicrosoftTeamsDevice","MicrosoftTeamsAnalytics","InformationWorkerProtection","Campaign","DLPEndpoint","AirInvestigation","Quarantine","MicrosoftForms","ApplicationAudit","ComplianceSupervisionExchange","CustomerKeyServiceEncryption","OfficeNative","MipAutoLabelSharePointItem","MipAutoLabelSharePointPolicyLocation","MicrosoftTeamsShifts","SecureScore","MipAutoLabelExchangeItem","CortanaBriefing","Search","WDATPAlerts","PowerPlatformAdminDlp","PowerPlatformAdminEnvironment","MDATPAudit","SensitivityLabelPolicyMatch","SensitivityLabelAction","SensitivityLabeledFileAction","AttackSim","AirManualInvestigation","SecurityComplianceRBAC","UserTraining","AirAdminActionInvestigation","MSTIC","PhysicalBadgingSignal","TeamsEasyApprovals","AipDiscover","AipSensitivityLabelAction","AipProtectionAction","AipFileDeleted","AipHeartBeat","MCASAlerts","OnPremisesFileShareScannerDlp","OnPremisesSharePointScannerDlp","ExchangeSearch","SharePointSearch","PrivacyDataMinimization","LabelAnalyticsAggregate","MyAnalyticsSettings","SecurityComplianceUserChange","ComplianceDLPExchangeClassification","ComplianceDLPEndpoint","MipExactDataMatch","MSDEResponseActions","MSDEGeneralSettings","MSDEIndicatorsSettings","MS365DCustomDetection","MSDERolesSettings","MAPGAlerts","MAPGPolicy","MAPGRemediation","PrivacyRemediationAction","PrivacyDigestEmail","MipAutoLabelSimulationProgress","MipAutoLabelSimulationCompletion","MipAutoLabelProgressFeedback","DlpSensitiveInformationType","MipAutoLabelSimulationStatistics","LargeContentMetadata","Microsoft365Group","CDPMlInferencingResult","FilteringMailMetadata","CDPClassificationMailItem","CDPClassificationDocument","OfficeScriptsRunAction","FilteringPostMailDeliveryAction","CDPUnifiedFeedback","TenantAllowBlockList","ConsumptionResource","HealthcareSignal","DlpImportResult","CDPCompliancePolicyExecution","MultiStageDisposition","PrivacyDataMatch","FilteringDocMetadata","FilteringEmailFeatures","PowerBIDlp","FilteringUrlInfo","FilteringAttachmentInfo","CoreReportingSettings","ComplianceConnector","PowerPlatformLockboxResourceAccessRequest","PowerPlatformLockboxResourceCommand","CDPPredictiveCodingLabel","CDPCompliancePolicyUserFeedback","WebpageActivityEndpoint","OMEPortal","CMImprovementActionChange","FilteringUrlClick","MipLabelAnalyticsAuditRecord","FilteringEntityEvent","FilteringRuleHits","FilteringMailSubmission","LabelExplorer","MicrosoftManagedServicePlatform","PowerPlatformServiceActivity","ScorePlatformGenericAuditRecord","FilteringTimeTravelDocMetadata","Alert","AlertStatus","AlertIncident","IncidentStatus","Case","CaseInvestigation","RecordsManagement","PrivacyRemediation","DataShareOperation","CdpDlpSensitive","EHRConnector","FilteringMailGradingResult","PublicFolder","PrivacyTenantAuditHistoryRecord","AipScannerDiscoverEvent","EduDataLakeDownloadOperation","M365ComplianceConnector","MicrosoftGraphDataConnectOperation","MicrosoftPurview","FilteringEmailContentFeatures","PowerPagesSite","PowerAppsResource","PlannerPlan","PlannerCopyPlan","PlannerTask","PlannerRoster","PlannerPlanList","PlannerTaskList","PlannerTenantSettings","ProjectForTheWebProject","ProjectForTheWebTask","ProjectForTheWebRoadmap","ProjectForTheWebRoadmapItem","ProjectForTheWebProjectSettings","ProjectForTheWebRoadmapSettings","QuarantineMetadata","MicrosoftTodoAudit","TimeTravelFilteringDocMetadata","TeamsQuarantineMetadata","SharePointAppPermissionOperation","MicrosoftTeamsSensitivityLabelAction","FilteringTeamsMetadata","FilteringTeamsUrlInfo","FilteringTeamsPostDeliveryAction","MDCAssessments","MDCRegulatoryComplianceStandards","MDCRegulatoryComplianceControls","MDCRegulatoryComplianceAssessments","MDCSecurityConnectors","MDADataSecuritySignal","VivaGoals","FilteringRuntimeInfo","AttackSimAdmin","MicrosoftGraphDataConnectConsent","FilteringAtpDetonationInfo","PrivacyPortal","ManagedTenants","UnifiedSimulationMatchedItem","UnifiedSimulationSummary","UpdateQuarantineMetadata","MS365DSuppressionRule","PurviewDataMapOperation","FilteringUrlPostClickAction","IrmUserDefinedDetectionSignal","TeamsUpdates","PlannerRosterSensitivityLabel","MS365DIncident","FilteringDelistingMetadata","ComplianceDLPSharePointClassificationExtended","MicrosoftDefenderForIdentityAudit","SupervisoryReviewDayXInsight","DefenderExpertsforXDRAdmin","CDPEdgeBlockedMessage","HostedRpa" $date = [datetime]::Now.ToString('yyyyMMddHHmmss') $outputFile = "$($date)-Amount_Of_Audit_Logs.csv" - if ($OutputDir -eq "" ){ - $OutputDir = "Output\" - if (!(test-path $OutputDir)) { - New-Item -ItemType Directory -Force -Name $outputDir | Out-Null - Write-LogFile -Message "Creating the following directory: $OutputDir" - } + if (!(test-path $OutputDir)) { + New-Item -ItemType Directory -Force -Name $outputDir > $null + Write-LogFile -Message "Creating the following directory: $OutputDir" } - else { if (Test-Path -Path $OutputDir) { write-LogFile -Message "[INFO] Custom directory set to: $OutputDir" @@ -80,10 +64,16 @@ function Get-UALStatistics $outputDirectory = Join-Path $OutputDir $outputFile Set-Content $outputDirectory -Value "RecordType,Amount of log entries" - - #Write-Host "[INFO] Calculating the number of audit logs" -ForegroundColor Green Write-LogFile -Message "[INFO] Calculating the number of audit logs for each of the 236 Record Types between $($script:StartDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK")) and $($script:EndDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK"))" -Color "Green" - $totalCount = Search-UnifiedAuditLog -Userids $UserIds -StartDate $script:StartDate -EndDate $script:EndDate -ResultSize 1 | Format-List -Property ResultCount| out-string -Stream | select-string ResultCount + + try { + $totalCount = Search-UnifiedAuditLog -Userids $UserIds -StartDate $script:StartDate -EndDate $script:EndDate -ResultSize 1 | Format-List -Property ResultCount| out-string -Stream | select-string ResultCount + } + catch { + write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break + } Foreach ($record in $recordTypes) { $specificResult = Search-UnifiedAuditLog -Userids $UserIds -StartDate $script:StartDate -EndDate $script:EndDate -RecordType $record -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount diff --git a/Scripts/Get-UsersInfo.ps1 b/Scripts/Get-UsersInfo.ps1 index cbaa0cb..6bfe286 100644 --- a/Scripts/Get-UsersInfo.ps1 +++ b/Scripts/Get-UsersInfo.ps1 @@ -6,28 +6,19 @@ function Get-Users { .DESCRIPTION Retrieves the creation time and date of the last password change for all users. - The output will be written to: Output\UserInfo\ .PARAMETER OutputDir OutputDir is the parameter specifying the output directory. - Default: Output\UserInfo + Default: Output\Users .PARAMETER Encoding Encoding is the parameter specifying the encoding of the CSV output file. Default: UTF8 - - .PARAMETER Application - Application is the parameter specifying App-only access (access without a user) for authentication and authorization. - Default: Delegated access (access on behalf a user) .EXAMPLE Get-Users Retrieves the creation time and date of the last password change for all users. - .EXAMPLE - Get-Users -Application - Retrieves the creation time and date of the last password change for all users via application authentication. - .EXAMPLE Get-Users -Encoding utf32 Retrieves the creation time and date of the last password change for all users and exports the output to a CSV file with UTF-32 encoding. @@ -38,40 +29,23 @@ function Get-Users { #> [CmdletBinding()] param( - [string]$OutputDir, - [string]$Encoding, - [switch]$Application + [string]$OutputDir = "Output\Users", + [string]$Encoding = "UTF8" ) - if (!($Application.IsPresent)) { - Connect-MgGraph -Scopes User.Read.All, Directory.AccessAsUser.All, User.ReadBasic.All, Directory.Read.All -NoWelcome - } - - try { - $areYouConnected = Get-MgUser -ErrorAction stop - } - catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes 'User.Read.All, Directory.AccessAsUser.All, User.ReadBasic.All, Directory.Read.All' before running this script" -Color "Red" - break + $authType = Get-GraphAuthType + if ($authType -eq "Delegated") { + Connect-MgGraph -Scopes User.Read.All, Directory.AccessAsUser.All, User.ReadBasic.All, Directory.Read.All > $null } - if ($Encoding -eq "" ){ - $Encoding = "UTF8" - } - - if ($OutputDir -eq "" ){ - $OutputDir = "Output\UserInfo" - if (!(test-path $OutputDir)) { - New-Item -ItemType Directory -Force -Name $OutputDir | Out-Null - write-logFile -Message "[INFO] Creating the following directory: $OutputDir" - } + 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" @@ -80,46 +54,53 @@ function Get-Users { Write-logFile -Message "[INFO] Running Get-Users" -Color "Green" - $selectobjects = "Id","AccountEnabled","DisplayName","UserPrincipalName","Mail","CreatedDateTime","LastPasswordChangeDateTime","DeletedDateTime","JobTitle","Department","OfficeLocation","City","State","Country" - $mgUsers = Get-MgUser -All -Select $selectobjects - write-host "A total of $($mgUsers.count) users found:" + try { + $selectobjects = "Id","AccountEnabled","DisplayName","UserPrincipalName","Mail","CreatedDateTime","LastPasswordChangeDateTime","DeletedDateTime","JobTitle","Department","OfficeLocation","City","State","Country" + $mgUsers = Get-MgUser -All -Select $selectobjects + write-host "A total of $($mgUsers.count) users found:" - $date = (Get-Date).AddDays(-7) - $oneweekold = $mgUsers | Where-Object { - $_.CreatedDateTime -gt $date - } - write-host " - $($oneweekold.count) users created within the last 7 days." + $date = (Get-Date).AddDays(-7) + $oneweekold = $mgUsers | Where-Object { + $_.CreatedDateTime -gt $date + } + write-host " - $($oneweekold.count) users created within the last 7 days." - $date = (Get-Date).AddDays(-30) - $onemonthold = $mgUsers | Where-Object { - $_.CreatedDateTime -gt $date - } - write-host " - $($onemonthold.count) users created within the last 30 days." + $date = (Get-Date).AddDays(-30) + $onemonthold = $mgUsers | Where-Object { + $_.CreatedDateTime -gt $date + } + write-host " - $($onemonthold.count) users created within the last 30 days." - $date = (Get-Date).AddDays(-90) - $threemonthold = $mgUsers | Where-Object { - $_.CreatedDateTime -gt $date - } - write-host " - $($threemonthold.count) users created within the last 90 days." + $date = (Get-Date).AddDays(-90) + $threemonthold = $mgUsers | Where-Object { + $_.CreatedDateTime -gt $date + } + write-host " - $($threemonthold.count) users created within the last 90 days." - $date = (Get-Date).AddDays(-180) - $sixmonthold = $mgUsers | Where-Object { - $_.CreatedDateTime -gt $date - } - write-host " - $($sixmonthold.count) users created within the last 6 months." + $date = (Get-Date).AddDays(-180) + $sixmonthold = $mgUsers | Where-Object { + $_.CreatedDateTime -gt $date + } + write-host " - $($sixmonthold.count) users created within the last 6 months." - $date = (Get-Date).AddDays(-360) - $sixmonthold = $mgUsers | Where-Object { - $_.CreatedDateTime -gt $date - } - write-host " - $($sixmonthold.count) users created within the last 1 year." + $date = (Get-Date).AddDays(-360) + $sixmonthold = $mgUsers | Where-Object { + $_.CreatedDateTime -gt $date + } + write-host " - $($sixmonthold.count) users created within the last 1 year." - Get-MgUser | Get-Member | out-null + Get-MgUser | Get-Member > $null - $date = Get-Date -Format "yyyyMMddHHmm" - $filePath = "$OutputDir\$($date)-Users.csv" - - $mgUsers | select-object $selectobjects | Export-Csv -Path $filePath -NoTypeInformation -Encoding $Encoding + $date = Get-Date -Format "yyyyMMddHHmm" + $filePath = "$OutputDir\$($date)-Users.csv" + + $mgUsers | select-object $selectobjects | Export-Csv -Path $filePath -NoTypeInformation -Encoding $Encoding + } + catch { + Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes 'User.Read.All, Directory.AccessAsUser.All, User.ReadBasic.All, Directory.Read.All' before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break + } Write-logFile -Message "[INFO] Output written to $filePath" -Color "Green" } @@ -131,28 +112,19 @@ Function Get-AdminUsers { .DESCRIPTION Retrieves Administrator directory roles, including the identification of users associated with each specific role. - The output will be written to: Output\UserInfo\ .PARAMETER OutputDir OutputDir is the parameter specifying the output directory. - Default: Output\UserInfo + Default: Output\Admins .PARAMETER Encoding Encoding is the parameter specifying the encoding of the CSV output file. Default: UTF8 - - .PARAMETER Application - Application is the parameter specifying App-only access (access without a user) for authentication and authorization. - Default: Delegated access (access on behalf a user) .EXAMPLE Get-AdminUsers Retrieves Administrator directory roles, including the identification of users associated with each specific role. - .EXAMPLE - Get-AdminUsers -Application - Retrieves Administrator directory roles, including the identification of users associated with each specific role via application authentication. - .EXAMPLE Get-AdminUsers -Encoding utf32 Retrieves Administrator directory roles, including the identification of users associated with each specific role and exports the output to a CSV file with UTF-32 encoding. @@ -164,40 +136,23 @@ Function Get-AdminUsers { [CmdletBinding()] param( - [string]$outputDir, - [string]$Encoding, - [switch]$Application + [string]$outputDir = "Output\Admins", + [string]$Encoding = "UTF8" ) - if (!($Application.IsPresent)) { - Connect-MgGraph -Scopes User.Read.All, Directory.AccessAsUser.All, Directory.Read.All -NoWelcome - } - - try { - $areYouConnected = Get-MgDirectoryRole -ErrorAction stop - } - catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes 'User.Read.All, Directory.AccessAsUser.All, Directory.Read.All' before running this script" -Color "Red" - break + $authType = Get-GraphAuthType + if ($authType -eq "Delegated") { + Connect-MgGraph -Scopes User.Read.All, Directory.AccessAsUser.All, User.ReadBasic.All, Directory.Read.All > $null } - if ($Encoding -eq "" ){ - $Encoding = "UTF8" + if (!(test-path $OutputDir)) { + New-Item -ItemType Directory -Force -Name $OutputDir > $null + write-logFile -Message "[INFO] Creating the following directory: $OutputDir" } - - if ($OutputDir -eq "" ){ - $OutputDir = "Output\UserInfo" - if (!(test-path $OutputDir)) { - New-Item -ItemType Directory -Force -Name $OutputDir | Out-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" @@ -205,69 +160,76 @@ Function Get-AdminUsers { } Write-logFile -Message "[INFO] Running Get-AdminUsers" -Color "Green" + try { + $getRoles = Get-MgDirectoryRole -all + foreach ($role in $getRoles) { + $roleId = $role.Id + $roleName = $role.DisplayName - $getRoles = Get-MgDirectoryRole -all - foreach ($role in $getRoles) { - $roleId = $role.Id - $roleName = $role.DisplayName - - if ($roleName -like "*Admin*") { - $areThereUsers = Get-MgDirectoryRoleMember -DirectoryRoleId $roleId + if ($roleName -like "*Admin*") { + $areThereUsers = Get-MgDirectoryRoleMember -DirectoryRoleId $roleId - if ($null -eq $areThereUsers) { - write-host "[INFO] $roleName - No users found" - } + if ($null -eq $areThereUsers) { + write-host "[INFO] $roleName - No users found" + } - else { - $results=@(); + else { + $results=@(); - $myObject = [PSCustomObject]@{ - UserName = "-" - UserId = "_" - Role = "-" - } + $myObject = [PSCustomObject]@{ + UserName = "-" + UserId = "_" + Role = "-" + } - $count = 0 - $areThereUsers | ForEach-Object { + $count = 0 + $areThereUsers | ForEach-Object { - $userid = $_.Id + $userid = $_.Id - if ($userid -eq ".") { - write-host "." - } - - else { - $count = $count +1 - try{ - $getUserName = Get-MgUser -Filter ("Id eq '$userid'") - $userName = $getUserName.UserPrincipalName - - $myObject.UserName = $userName - $myObject.UserId = $userid - $myObject.Role = $roleName - - $results+= $myObject; + if ($userid -eq ".") { + write-host "." } - catch{ - Write-logFile -Message "[INFO] Resource $userid does not exist or one of its queried reference-property objects are not present." -Color "Yellow" + + else { + $count = $count +1 + try{ + $getUserName = Get-MgUser -Filter ("Id eq '$userid'") + $userName = $getUserName.UserPrincipalName + + $myObject.UserName = $userName + $myObject.UserId = $userid + $myObject.Role = $roleName + + $results+= $myObject; + } + catch{ + Write-logFile -Message "[INFO] Resource $userid does not exist or one of its queried reference-property objects are not present." -Color "Yellow" + } } } - } - Write-logFile -Message "[info] $roleName - $count users found" -Color "Yellow" - $filePath = "$OutputDir\$($date)-$roleName.csv" - $results | Export-Csv -Path $filePath -NoTypeInformation -Encoding $Encoding - Write-logFile -Message "[INFO] Output written to $filePath" -Color "Green" + Write-logFile -Message "[info] $roleName - $count users found" -Color "Yellow" + $filePath = "$OutputDir\$($date)-$roleName.csv" + $results | Export-Csv -Path $filePath -NoTypeInformation -Encoding $Encoding + Write-logFile -Message "[INFO] Output written to $filePath" -Color "Green" + } } } } + catch { + Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes 'User.Read.All, Directory.AccessAsUser.All, Directory.Read.All' before running this script" -Color "Red" + Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" + break + } $outputDirMerged = "$OutputDir\Merged\" If (!(test-path $outputDirMerged)) { Write-LogFile -Message "[INFO] Creating the following directory: $outputDirMerged" - New-Item -ItemType Directory -Force -Path $outputDirMerged | Out-Null + New-Item -ItemType Directory -Force -Path $outputDirMerged > $null } Write-LogFile -Message "[INFO] Merging Administrator CSV Ouput Files" -Color "Green" - Get-ChildItem $OutputDir -Filter "*Administrator.csv" | Select-Object -ExpandProperty FullName | Import-Csv | Export-Csv "$outputDirMerged/All-Administrators.csv" -NoTypeInformation -Append + $date = Get-Date -Format "yyyyMMddHHmm" + Get-ChildItem $OutputDir -Filter "*Administrator.csv" | Select-Object -ExpandProperty FullName | Import-Csv | Export-Csv "$outputDirMerged/$($date)-All-Administrators.csv" -NoTypeInformation -Append } \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index 620f38f..a34dea6 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -3,11 +3,11 @@ # -- Project information project = 'Microsoft-Extractor-Suite' -copyright = 'Copyright (c) 2024 Invictus Incident Response' +copyright = 'Copyright 2024 Invictus Incident Response' author = 'Joey Rentenaar & Korstiaan Stam' -release = '1.3.5' -version = '1.3.5' +release = '2.0.0' +version = '2.0.0' # -- General configuration diff --git a/docs/source/functionality/AzureActiveDirectoryAuditLog.rst b/docs/source/functionality/AzureActiveDirectoryAuditLog.rst index 6e801cf..4dccb5c 100644 --- a/docs/source/functionality/AzureActiveDirectoryAuditLog.rst +++ b/docs/source/functionality/AzureActiveDirectoryAuditLog.rst @@ -48,4 +48,4 @@ Parameters Output """""""""""""""""""""""""" -The output will be saved to the 'AzureAD' directory within the 'Output' directory, with the file name 'Auditlogs.json'. Each time an acquisition is performed, the output JSON file will be overwritten. Therefore, if you perform multiple acquisitions, the JSON file will only contain the results from the latest acquisition. \ No newline at end of file +The output will be saved to the 'AzureAD' directory within the 'Output' directory, with the file name 'Auditlogs.json'. \ No newline at end of file diff --git a/docs/source/functionality/AzureActiveDirectorysign-inlogs.rst b/docs/source/functionality/AzureActiveDirectorysign-inlogs.rst index 9478c39..28348a3 100644 --- a/docs/source/functionality/AzureActiveDirectorysign-inlogs.rst +++ b/docs/source/functionality/AzureActiveDirectorysign-inlogs.rst @@ -55,4 +55,4 @@ Parameters Output """""""""""""""""""""""""" -The output will be saved to the 'AzureAD' directory within the 'Output' directory, with the file name 'SignInLogs.json'. Each time an acquisition is performed, the output JSON file will be overwritten. Therefore, if you perform multiple acquisitions, the JSON file will only contain the results from the latest acquisition. +The output will be saved to the 'AzureAD' directory within the 'Output' directory, with the file name 'SignInLogs.json'. \ No newline at end of file diff --git a/docs/source/functionality/AzureActivityLogs.rst b/docs/source/functionality/AzureActivityLogs.rst index ea1e7f4..82baf65 100644 --- a/docs/source/functionality/AzureActivityLogs.rst +++ b/docs/source/functionality/AzureActivityLogs.rst @@ -2,10 +2,6 @@ Azure Activity Logs ======= Use **Get-ActivityLogs** to collect the contents of the Azure Activity Log. -.. note:: - - This functionality is currently in beta. If you encounter any issues or have suggestions for improvements please let us know. - Usage """""""""""""""""""""""""" Running the script without any parameters will gather the Azure Activity Logs for all subscriptions for the last 89 days: @@ -13,15 +9,15 @@ Running the script without any parameters will gather the Azure Activity Logs fo Get-ActivityLogs -Get all the activity logs before 2023-04-12: +Get all the activity logs before 2024-03-04: :: - Get-ActivityLogs -EndDate 2023-04-12 + Get-ActivityLogs -EndDate 2024-06-05 -Get all the activity logs after 2023-04-12: +Get all the activity logs after 2024-06-05: :: - Get-ActivityLogs -StartDate 2023-04-12 + Get-ActivityLogs -StartDate 2024-06-05 Get all the activity logs for the subscription 4947f939-cf12-4329-960d-4dg68a3eb66f: :: @@ -44,7 +40,7 @@ Parameters -OutputDir (optional) - OutputDir is the parameter specifying the output directory. - - Default: Output\AzureActivityLogs + - Default: Output\ActivityLogs -Encoding (optional) - Encoding is the parameter specifying the encoding of the JSON output file. @@ -52,4 +48,4 @@ Parameters Output """""""""""""""""""""""""" -The output will be saved to the 'AzureAD' directory within the 'Output' directory, with the file name 'SignInLogs.json'. Each time an acquisition is performed, the output JSON file will be overwritten. Therefore, if you perform multiple acquisitions, the JSON file will only contain the results from the latest acquisition. +The output will be saved to the 'ActivityLogs' directory within the 'Output' directory, with the file name 'ActivityLog.json'. \ No newline at end of file diff --git a/docs/source/functionality/AzureAuditLogsGraph.rst b/docs/source/functionality/AzureAuditLogsGraph.rst index d9d7f87..080fc26 100644 --- a/docs/source/functionality/AzureAuditLogsGraph.rst +++ b/docs/source/functionality/AzureAuditLogsGraph.rst @@ -2,10 +2,6 @@ Azure Audit Logs via Graph API ======= Use **Get-ADAuditLogsGraph** to collect the contents of the Azure Active Directory Audit Log. -.. note:: - - This GraphAPI functionality is currently in beta. If you encounter any issues or have suggestions for improvements please let us know. - Usage """""""""""""""""""""""""" Running the script without any parameters will gather the Azure Active Directory Audit Log for the last 90 days: @@ -35,13 +31,6 @@ Parameters - OutputDir is the parameter specifying the output directory. - Default: The output will be written to: "Output\AzureAD\{date_AuditLogs}\Auditlogs.json --Application (optional) - - Application is the parameter specifying App-only access (access without a user) for authentication and authorization. - - Default: Delegated access (access on behalf a user) - --MergeOutput (optional) - - MergeOutput is the parameter specifying if you wish to merge CSV outputs to a single file. - -Encoding (optional) - Encoding is the parameter specifying the encoding of the JSON output file. - Default: UTF8 diff --git a/docs/source/functionality/AzureDirectoryActivityLogs.rst b/docs/source/functionality/AzureDirectoryActivityLogs.rst new file mode 100644 index 0000000..f917f0b --- /dev/null +++ b/docs/source/functionality/AzureDirectoryActivityLogs.rst @@ -0,0 +1,51 @@ +Azure Directory Activity Logs +======= +Use **Get-DirectoryActivityLogs** to collect the contents of the Azure Activity Log. + +.. note:: + + This functionality is currently in beta. If you encounter any issues or have suggestions for improvements please let us know. + +Usage +"""""""""""""""""""""""""" +Running the script without any parameters will gather the Azure Directory Activity Logs for the last 90 days: +:: + + Get-DirectoryActivityLogs + +Get all the Directory Activity Logs before 2024-03-05: +:: + + Get-DirectoryActivityLogs -EndDate 2024-06-05 + +Get all the Directory Activity Logs after 2024-06-05: +:: + + Get-DirectoryActivityLogs -StartDate 2024-06-05 + + +Parameters +"""""""""""""""""""""""""" +-StartDate (optional) + - StartDate is the parameter specifying the start date of the date range. + - Default: Today -90 days + +-EndDate (optional) + - EndDate is the parameter specifying the end date of the date range. + - Default: Now + +-Output (optional) + - Output is the parameter specifying the CSV or JSON output type. + - Default: CSV + +-OutputDir (optional) + - OutputDir is the parameter specifying the output directory. + - Default: Output\DirectoryActivityLogs + +-Encoding (optional) + - Encoding is the parameter specifying the encoding of the JSON output file. + - Default: UTF8 + +Output +"""""""""""""""""""""""""" +The output will be saved to the 'DirectoryActivityLogs' directory within the 'Output' directory, with the file name 'DirectoryActivityLogs.csv'. \ No newline at end of file diff --git a/docs/source/functionality/AzureSignInLogsGraph.rst b/docs/source/functionality/AzureSignInLogsGraph.rst index 1007604..1dc2be9 100644 --- a/docs/source/functionality/AzureSignInLogsGraph.rst +++ b/docs/source/functionality/AzureSignInLogsGraph.rst @@ -2,10 +2,6 @@ Azure Sign-in Logs via Graph API ======= Use **Get-ADSignInLogsGraph** to collect the contents of the Azure Active Directory sign-in log. -.. note:: - - This GraphAPI functionality is currently in beta. If you encounter any issues or have suggestions for improvements please let us know. - Usage """""""""""""""""""""""""" Running the script without any parameters will gather the Azure Active Directory sign-in log for the last 30 days: @@ -39,24 +35,12 @@ Parameters - Encoding is the parameter specifying the encoding of the JSON output file. - Default: UTF8 --Application (optional) - - Application is the parameter specifying App-only access (access without a user) for authentication and authorization. - - Default: Delegated access (access on behalf a user) - --MergeOutput (optional) - - MergeOutput is the parameter specifying if you wish to merge CSV outputs to a single file. - - Default: No - --Interval (optional) - - Interval is the parameter specifying the interval in which the logs are being gathered. - - Default: 1440 minutes - -UserIds (optional) - UserIds is the UserIds parameter filtering the log entries by the account of the user who performed the actions. Output """""""""""""""""""""""""" -The output will be saved to the 'AzureAD' directory within the 'Output' directory, with the file name 'SignInLogsGraph.json'. Each time an acquisition is performed, the output JSON file will be overwritten. Therefore, if you perform multiple acquisitions, the JSON file will only contain the results from the latest acquisition. +The output will be saved to the 'AzureAD' directory within the 'Output' directory, with the file name 'SignInLogsGraph.json'. Permissions """""""""""""""""""""""""" diff --git a/docs/source/functionality/ConditionalAccessPolicies.rst b/docs/source/functionality/ConditionalAccessPolicies.rst index a5f0410..593ffff 100644 --- a/docs/source/functionality/ConditionalAccessPolicies.rst +++ b/docs/source/functionality/ConditionalAccessPolicies.rst @@ -13,7 +13,7 @@ Parameters """""""""""""""""""""""""" -OutputDir (optional) - OutputDir is the parameter specifying the output directory. - - Default: UserInfo + - Default: ConditionalAccessPolicies -Encoding (optional) - Encoding is the parameter specifying the encoding of the CSV/JSON output file. @@ -25,7 +25,7 @@ Parameters Output """""""""""""""""""""""""" -The output will be saved to the 'UserInfo' directory within the 'Output' directory. +The output will be saved to the 'ConditionalAccessPolicies' directory within the 'Output' directory. Permissions """""""""""""""""""""""""" diff --git a/docs/source/functionality/GetUserInfo.rst b/docs/source/functionality/GetUserInfo.rst index 11fdc30..f018bd4 100644 --- a/docs/source/functionality/GetUserInfo.rst +++ b/docs/source/functionality/GetUserInfo.rst @@ -27,19 +27,15 @@ Parameters """""""""""""""""""""""""" -OutputDir (optional) - OutputDir is the parameter specifying the output directory. - - Default: UserInfo + - Default: Users -Encoding (optional) - Encoding is the parameter specifying the encoding of the CSV/JSON output file. - Default: UTF8 --Application (optional) - - Application is the parameter specifying App-only access (access without a user) for authentication and authorization. - - Default: Delegated access (access on behalf a user) - Output """""""""""""""""""""""""" -The output will be saved to the 'UserInfo' directory within the 'Output' directory. +The output will be saved to the 'Users' directory within the 'Output' directory. Permissions """""""""""""""""""""""""" @@ -72,19 +68,15 @@ Parameters """""""""""""""""""""""""" -OutputDir (optional) - OutputDir is the parameter specifying the output directory. - - Default: UserInfo + - Default: Admins -Encoding (optional) - Encoding is the parameter specifying the encoding of the CSV/JSON output file. - Default: UTF8 --Application (optional) - - Application is the parameter specifying App-only access (access without a user) for authentication and authorization. - - Default: Delegated access (access on behalf a user) - Output """""""""""""""""""""""""" -The output will be saved to the 'UserInfo' directory within the 'Output' directory. +The output will be saved to the 'Admins' directory within the 'Output' directory. Permissions """""""""""""""""""""""""" @@ -112,19 +104,15 @@ Parameters """""""""""""""""""""""""" -OutputDir (optional) - OutputDir is the parameter specifying the output directory. - - Default: UserInfo + - Default: MFA -Encoding (optional) - Encoding is the parameter specifying the encoding of the CSV/JSON output file. - Default: UTF8 --Application (optional) - - Application is the parameter specifying App-only access (access without a user) for authentication and authorization. - - Default: Delegated access (access on behalf a user) - Output """""""""""""""""""""""""" -The output will be saved to the 'UserInfo' directory within the 'Output' directory. +The output will be saved to the 'MFA' directory within the 'Output' directory. Permissions """""""""""""""""""""""""" @@ -147,19 +135,15 @@ Parameters """""""""""""""""""""""""" -OutputDir (optional) - OutputDir is the parameter specifying the output directory. - - Default: UserInfo + - Default: RiskyEvents -Encoding (optional) - Encoding is the parameter specifying the encoding of the CSV/JSON output file. - Default: UTF8 --Application (optional) - - Application is the parameter specifying App-only access (access without a user) for authentication and authorization. - - Default: Delegated access (access on behalf a user) - Output """""""""""""""""""""""""" -The output will be saved to the 'UserInfo' directory within the 'Output' directory. +The output will be saved to the 'RiskyEvents' directory within the 'Output' directory. Permissions """""""""""""""""""""""""" @@ -182,19 +166,15 @@ Parameters """""""""""""""""""""""""" -OutputDir (optional) - OutputDir is the parameter specifying the output directory. - - Default: UserInfo + - Default: RiskyEvents -Encoding (optional) - Encoding is the parameter specifying the encoding of the CSV/JSON output file. - Default: UTF8 --Application (optional) - - Application is the parameter specifying App-only access (access without a user) for authentication and authorization. - - Default: Delegated access (access on behalf a user) - Output """""""""""""""""""""""""" -The output will be saved to the 'UserInfo' directory within the 'Output' directory. +The output will be saved to the 'RiskyEvents' directory within the 'Output' directory. Permissions """""""""""""""""""""""""" diff --git a/docs/source/index.rst b/docs/source/index.rst index cb83df5..52a0bb0 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -25,7 +25,8 @@ Supported sources Transport Rules Transport rules take action on messages while they're in transit. Azure Active Directory sign-in log Gets the Azure Active Directory sign-in log. Azure Active Directory Audit Log Gets the Azure Active Directory audit log. - Azure Activity Log Gets the Azure Activity log. + Azure Activity Log Gets the Azure Activity log. + Azure Directory Activity Log Gets the Azure Directory Activity log ===================================== =========================================================================================================================================================================== Retrieve other relevant information @@ -96,6 +97,7 @@ Have a bug report or feature request? Open an issue on the Github repository. functionality/AzureActiveDirectorysign-inlogs functionality/AzureActiveDirectoryAuditLog functionality/AzureActivityLogs + functionality/AzureDirectoryActivityLogs.rst functionality/AzureSignInLogsGraph functionality/AzureAuditLogsGraph functionality/ConditionalAccessPolicies From ae533f398d6dc6476354cb36ad76565e1b66a5ba Mon Sep 17 00:00:00 2001 From: JoeyInvictus <129975292+JoeyInvictus@users.noreply.github.com> Date: Wed, 26 Jun 2024 10:06:26 +0200 Subject: [PATCH 2/2] Update 2.0 Acquisition for new log source added: - Added Azure Directory Activity logs. Output Structure Improvements: Restructured the output to write results to specific folders instead of multiple outputs in Userinfo. For instance, the MFA functionality now generates a new folder named MFA for its output. Performance improvements inspired by Calvindd2f: - Replaced | Out-Null with > $null across multiple instances in the script for improved performance. - Redesigned the log-writing mechanism for increased speed. - Set default parameters in CmdletBindings across all scripts. - Created a function for combining logs outside of the function utilizing this. Authentication Type Detection: - Implemented functionality to automatically detect whether the user is logged in via Graph with delegated or application permissions. This prevents unnecessary errors by ensuring that the script connects with the appropriate scopes based on the authentication type. - Removed the need for the Application parameter. The script now automatically determines the authentication type in the background. Optimized Graph API Requests: Updated certain functionalities to use Invoke-MgGraphRequest instead of the Graph PowerShell Module cmdlets for improved performance and reliability. - The Get-MFA script now utilizes Invoke-MgGraphRequest to ensure all output is captured, including nested objects that were previously missed. - Get-Email, Get-Attachment, Show-Email are now using Invoke-MgGraphRequests for better performence and reliability. - Get-UALGraph has been reworked and is now using Invoke-MgGraphrequest for better performence. - Get-RiskyUsers & Get-RiskyDetections are both using Invoke-MgGraphrequest now. Get-ADSignInLogsGraph and Get-ADAuditLogsGraph: Inspired by code snippets from Calvindd2f, both functionalities have been reworked to use Invoke-MgGraphRequest. This change simplifies paging, eliminates the need for guessing the correct intervals, and allows for more efficient log retrieval. As a result, the script is now easier to use, faster, and should mitigate memory issues. Get-ActivityLogs This functionality now uses Invoke-RestMethod instead of the AzureAZ PowerShell Module cmdlets for improved performance and reliability. This change simplifies paging, eliminates the need to guess correct intervals, and enables more efficient log retrieval. Read The Docs: Updated Read The Docs so it's up to date with the latest changes. areYouConnected: The $areYouConnected function, which checks if the script can run the necessary actions and provides an error if it cannot, has been improved. It now displays the actual error message when the script fails, rather than a custom error message. Additionally, it no longer checks the module before running the main code (for most functions), making the script faster by avoiding unnecessary module checks. --- Scripts/Get-AdminAuditLog.ps1 | 2 +- Scripts/Get-AzureADGraphLogs.ps1 | 4 ++-- Scripts/Get-AzureADLogs.ps1 | 4 ++-- Scripts/Get-AzureActivityLogs.ps1 | 6 ++---- Scripts/Get-ConditionalAccessPolicy.ps1 | 2 +- Scripts/Get-Emails.ps1 | 6 +++--- Scripts/Get-MailItemsAccessed.ps1 | 18 +++++++++--------- Scripts/Get-MailboxAuditLog.ps1 | 2 +- Scripts/Get-MessageTraceLog.ps1 | 2 +- Scripts/Get-OAuthPermissions.ps1 | 2 +- Scripts/Get-RiskyEvents.ps1 | 4 ++-- Scripts/Get-UAL.ps1 | 8 ++++---- Scripts/Get-UALGraph.ps1 | 2 +- Scripts/Get-UALStatistics.ps1 | 2 +- Scripts/Get-UsersInfo.ps1 | 4 ++-- .../functionality/UnifiedAuditLogGraph.rst | 2 +- 16 files changed, 34 insertions(+), 36 deletions(-) diff --git a/Scripts/Get-AdminAuditLog.ps1 b/Scripts/Get-AdminAuditLog.ps1 index b2d8c11..cdcf295 100644 --- a/Scripts/Get-AdminAuditLog.ps1 +++ b/Scripts/Get-AdminAuditLog.ps1 @@ -68,7 +68,7 @@ function Get-AdminAuditLog { $results | Export-Csv $outputDirectory -NoTypeInformation -Append -Encoding UTF8 } catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to M365 by running the Connect-M365 command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } diff --git a/Scripts/Get-AzureADGraphLogs.ps1 b/Scripts/Get-AzureADGraphLogs.ps1 index 6e1cd30..6df5bb3 100644 --- a/Scripts/Get-AzureADGraphLogs.ps1 +++ b/Scripts/Get-AzureADGraphLogs.ps1 @@ -102,7 +102,7 @@ function Get-ADSignInLogsGraph { } While ($apiUrl) } catch { - Write-LogFile -Message "[INFO] Make sure you are connected to Connect-MgGraph before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to Microsoft Graph by running the Connect-MgGraph command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" } Write-LogFile -Message "[INFO] Acquisition complete, check the $($OutputDir) directory for your files.." -Color "Green" @@ -212,7 +212,7 @@ function Get-ADAuditLogsGraph { } While ($apiUrl) } catch { - Write-LogFile -Message "[INFO] Make sure you are connected to Connect-MgGraph before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to Microsoft Graph by running the Connect-MgGraph command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" } Write-LogFile -Message "[INFO] Acquisition complete, check the $($OutputDir) directory for your files.." -Color "Green" diff --git a/Scripts/Get-AzureADLogs.ps1 b/Scripts/Get-AzureADLogs.ps1 index f8bbed9..d0e3817 100644 --- a/Scripts/Get-AzureADLogs.ps1 +++ b/Scripts/Get-AzureADLogs.ps1 @@ -114,7 +114,7 @@ function Get-ADSignInLogs { Write-LogFile -Message "[WARNING] Failed to acquire logs. Retrying... Attempt $retryCount of $maxRetries" -Color "Yellow" } else { Write-LogFile -Message "[ERROR] Failed to acquire logs after $maxRetries attempts. Moving on." -Color "Red" - Write-logFile -Message "[INFO] You must call Connect-Azure or install AzureADPreview before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to Azure by running the Connect-Azure command or install AzureADPreview before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" } } @@ -273,7 +273,7 @@ function Get-ADAuditLogs { Write-LogFile -Message "[WARNING] Failed to acquire logs. Retrying... Attempt $retryCount of $maxRetries" -Color "Yellow" } else { Write-LogFile -Message "[ERROR] Failed to acquire logs after $maxRetries attempts. Moving on." -Color "Red" - Write-logFile -Message "[WARNING] You must call Connect-Azure or install AzureADPreview before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to Azure by running the Connect-Azure command or install AzureADPreview before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" } } diff --git a/Scripts/Get-AzureActivityLogs.ps1 b/Scripts/Get-AzureActivityLogs.ps1 index 4154d03..416f18a 100644 --- a/Scripts/Get-AzureActivityLogs.ps1 +++ b/Scripts/Get-AzureActivityLogs.ps1 @@ -74,7 +74,6 @@ function Get-ActivityLogs { $azureRmProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile $profileClient = [Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient]::new($azureRmProfile) $token = $profileClient.AcquireAccessToken($currentContext.Tenant.Id) - #$token if ($SubscriptionID -eq "") { write-logFile -Message "[INFO] Retrieving all subscriptions linked to the logged-in user account" -Color "Green" @@ -90,7 +89,7 @@ function Get-ActivityLogs { $subScription = $subscriptionsResponse.value } catch { - write-logFile -Message "[INFO] You must call Connect-AzureAZ before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to Azure by running the Connect-Az command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -105,7 +104,7 @@ function Get-ActivityLogs { $subScription = Get-AzSubscription -SubscriptionId $SubscriptionID } catch { - write-logFile -Message "[INFO] You must call Connect-AzureAZ before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to Azure by running the Connect-Az command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -115,7 +114,6 @@ function Get-ActivityLogs { $subId = $sub.subscriptionId write-logFile -Message "[INFO] Retrieving all Activity Logs for $subId" -Color "Green" - #$subId = $sub.Id $date = [datetime]::Now.ToString('yyyyMMddHHmmss') $filePath = "$OutputDir\$($date)-$subId-ActivityLog.json" diff --git a/Scripts/Get-ConditionalAccessPolicy.ps1 b/Scripts/Get-ConditionalAccessPolicy.ps1 index 481a05f..8b99888 100644 --- a/Scripts/Get-ConditionalAccessPolicy.ps1 +++ b/Scripts/Get-ConditionalAccessPolicy.ps1 @@ -104,7 +104,7 @@ Function Get-ConditionalAccessPolicies { } catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes Policy.Read.All before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to Microsoft Graph by running the Connect-MgGraph -Scopes Policy.Read.All command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" break } diff --git a/Scripts/Get-Emails.ps1 b/Scripts/Get-Emails.ps1 index 1e5a1cb..f882652 100644 --- a/Scripts/Get-Emails.ps1 +++ b/Scripts/Get-Emails.ps1 @@ -138,7 +138,7 @@ Function Get-Email { } } catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes Mail.ReadBasic.All before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to Microsoft Graph by running the Connect-MgGraph -Scopes Mail.ReadBasic.All command before executing this script" -Color "Yellow" Write-logFile -Message "[WARNING] The 'Mail.ReadBasic.All' is an application-level permission, requiring an application-based connection through the 'Connect-MgGraph' command for its use." -Color "Red" return } @@ -192,7 +192,7 @@ Function Get-Attachment { $getMessage = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/users/$userIds/messages?filter=internetMessageId eq '$internetMessageId'" -ErrorAction stop } catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes Mail.ReadBasic.All before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to Microsoft Graph by running the Connect-MgGraph -Scopes Mail.ReadBasic.All command before executing this script" -Color "Yellow" Write-logFile -Message "[WARNING] The 'Mail.ReadBasic.All' is an application-level permission, requiring an application-based connection through the 'Connect-MgGraph' command for its use." -Color "Red" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break @@ -259,7 +259,7 @@ Function Show-Email { $message = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/users/$userIds/messages?filter=internetMessageId eq '$internetMessageId'" -ErrorAction stop } catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes Mail.ReadBasic.All before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to Microsoft Graph by running the Connect-MgGraph -Scopes Mail.ReadBasic.All command before executing this script" -Color "Yellow" Write-logFile -Message "[WARNING] The 'Mail.ReadBasic.All' is an application-level permission, requiring an application-based connection through the 'Connect-MgGraph' command for its use." -Color "Red" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break diff --git a/Scripts/Get-MailItemsAccessed.ps1 b/Scripts/Get-MailItemsAccessed.ps1 index 4c702c3..8d40c0a 100644 --- a/Scripts/Get-MailItemsAccessed.ps1 +++ b/Scripts/Get-MailItemsAccessed.ps1 @@ -73,7 +73,7 @@ Function Get-Sessions { $amountResults = (Search-UnifiedAuditLog -StartDate $StartDate -UserIds $UserIds -EndDate $EndDate -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount) } catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to M365 by running the Connect-M365 command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -116,7 +116,7 @@ Function Get-Sessions { $amountResults = (Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -FreeText $IP -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount) } catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to M365 by running the Connect-M365 command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -161,7 +161,7 @@ Function Get-Sessions { $amountResults = (Search-UnifiedAuditLog -UserIds $UserIds -FreeText $IP -StartDate $StartDate -EndDate $EndDate -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount) } catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to M365 by running the Connect-M365 command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -206,7 +206,7 @@ Function Get-Sessions { $amountResults = (Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount) } catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to M365 by running the Connect-M365 command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -326,7 +326,7 @@ function Get-MessageIDs { $amountResults = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount } catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to M365 by running the Connect-M365 command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -406,7 +406,7 @@ function Get-MessageIDs { $amountResults = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -FreeText $IP -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount } catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to M365 by running the Connect-M365 command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -493,7 +493,7 @@ function Get-MessageIDs { $amountResults = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -FreeText $Sessions -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount } catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to M365 by running the Connect-M365 command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -575,7 +575,7 @@ function Get-MessageIDs { $amountResults = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -FreeText $IP -Operations "MailItemsAccessed" -ResultSize 1 | Select-Object -First 1 -ExpandProperty ResultCount } catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to M365 by running the Connect-M365 command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -697,7 +697,7 @@ function DownloadMails($iMessageID,$UserIds){ } } catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes Mail.ReadBasic.All before running the -Download flag" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to Microsoft Graph by running the Connect-MgGraph -Scopes Mail.ReadBasic.All command before executing this script" -Color "Yellow" Write-logFile -Message "[WARNING] The 'Mail.ReadBasic.All' is an application-level permission, requiring an application-based connection through the 'Connect-MgGraph' command for its use." -Color "Red" Write-Host "[WARNING] Error Message: $($_.Exception.Message)" -Color "Red" break diff --git a/Scripts/Get-MailboxAuditLog.ps1 b/Scripts/Get-MailboxAuditLog.ps1 index 3059d4f..d8bf6ea 100644 --- a/Scripts/Get-MailboxAuditLog.ps1 +++ b/Scripts/Get-MailboxAuditLog.ps1 @@ -56,7 +56,7 @@ function Get-MailboxAuditLog $areYouConnected = Search-MailboxAuditlog -ErrorAction stop } catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to M365 by running the Connect-M365 command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } diff --git a/Scripts/Get-MessageTraceLog.ps1 b/Scripts/Get-MessageTraceLog.ps1 index c3a7edb..15c39f3 100644 --- a/Scripts/Get-MessageTraceLog.ps1 +++ b/Scripts/Get-MessageTraceLog.ps1 @@ -89,7 +89,7 @@ function Get-MessageTraceLog $areYouConnected = Get-MessageTrace -ErrorAction stop } catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to M365 by running the Connect-M365 command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } diff --git a/Scripts/Get-OAuthPermissions.ps1 b/Scripts/Get-OAuthPermissions.ps1 index f5ffff3..b3b9e5d 100644 --- a/Scripts/Get-OAuthPermissions.ps1 +++ b/Scripts/Get-OAuthPermissions.ps1 @@ -93,7 +93,7 @@ Lists delegated permissions (OAuth2PermissionGrants) and application permissions try { $tenant_details = Get-AzureADTenantDetail -ErrorAction stop } catch { - write-logFile -Message "[WARNING] You must call Connect-Azure before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to Azure by running the Connect-Azure command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } diff --git a/Scripts/Get-RiskyEvents.ps1 b/Scripts/Get-RiskyEvents.ps1 index 88f1414..7b4d05c 100644 --- a/Scripts/Get-RiskyEvents.ps1 +++ b/Scripts/Get-RiskyEvents.ps1 @@ -82,7 +82,7 @@ function Get-RiskyUsers { $uri = $response.'@odata.nextLink' } while ($uri -ne $null) } catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes IdentityRiskEvent.Read.All,IdentityRiskyServicePrincipal.Read.All,IdentityRiskyUser.Read.All before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to Microsoft Graph by running the Connect-MgGraph -Scopes IdentityRiskEvent.Read.All,IdentityRiskyServicePrincipal.Read.All,IdentityRiskyUser.Read.All command before executing this script" -Color "Yellow" Write-LogFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -197,7 +197,7 @@ function Get-RiskyDetections { $uri = $response.'@odata.nextLink' } while ($uri -ne $null) } catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes IdentityRiskEvent.Read.All,IdentityRiskyServicePrincipal.Read.All,IdentityRiskyUser.Read.All before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to Microsoft Graph by running the Connect-MgGraph -Scopes IdentityRiskEvent.Read.All,IdentityRiskyServicePrincipal.Read.All,IdentityRiskyUser.Read.All command before executing this script" -Color "Yellow" Write-LogFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } diff --git a/Scripts/Get-UAL.ps1 b/Scripts/Get-UAL.ps1 index 33c3b78..e6277b0 100644 --- a/Scripts/Get-UAL.ps1 +++ b/Scripts/Get-UAL.ps1 @@ -86,7 +86,7 @@ function Get-UALAll $areYouConnected = Get-AdminAuditLogConfig -ErrorAction stop } catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to M365 by running the Connect-M365 command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -300,7 +300,7 @@ function Get-UALGroup $areYouConnected = Get-AdminAuditLogConfig -ErrorAction stop } catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to M365 by running the Connect-M365 command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -554,7 +554,7 @@ function Get-UALSpecific $areYouConnected = Get-AdminAuditLogConfig -ErrorAction stop } catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to M365 by running the Connect-M365 command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -781,7 +781,7 @@ function Get-UALSpecificActivity $areYouConnected = Get-AdminAuditLogConfig -ErrorAction stop } catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to M365 by running the Connect-M365 command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } diff --git a/Scripts/Get-UALGraph.ps1 b/Scripts/Get-UALGraph.ps1 index a9c5a3a..20b32df 100644 --- a/Scripts/Get-UALGraph.ps1 +++ b/Scripts/Get-UALGraph.ps1 @@ -153,7 +153,7 @@ Function Get-UALGraph { } } catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes 'AuditLogsQuery.Read.All' before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to Microsoft Graph by running the Connect-MgGraph -Scopes 'AuditLogsQuery.Read.All' command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } diff --git a/Scripts/Get-UALStatistics.ps1 b/Scripts/Get-UALStatistics.ps1 index dd6da9b..7e5c2f2 100644 --- a/Scripts/Get-UALStatistics.ps1 +++ b/Scripts/Get-UALStatistics.ps1 @@ -70,7 +70,7 @@ function Get-UALStatistics $totalCount = Search-UnifiedAuditLog -Userids $UserIds -StartDate $script:StartDate -EndDate $script:EndDate -ResultSize 1 | Format-List -Property ResultCount| out-string -Stream | select-string ResultCount } catch { - write-logFile -Message "[WARNING] You must call Connect-M365 before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to M365 by running the Connect-M365 command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } diff --git a/Scripts/Get-UsersInfo.ps1 b/Scripts/Get-UsersInfo.ps1 index 6bfe286..cd99afe 100644 --- a/Scripts/Get-UsersInfo.ps1 +++ b/Scripts/Get-UsersInfo.ps1 @@ -97,7 +97,7 @@ function Get-Users { $mgUsers | select-object $selectobjects | Export-Csv -Path $filePath -NoTypeInformation -Encoding $Encoding } catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes 'User.Read.All, Directory.AccessAsUser.All, User.ReadBasic.All, Directory.Read.All' before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to Microsoft Graph by running the Connect-MgGraph -Scopes 'User.Read.All, Directory.AccessAsUser.All, User.ReadBasic.All, Directory.Read.All' command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } @@ -218,7 +218,7 @@ Function Get-AdminUsers { } } catch { - Write-logFile -Message "[WARNING] You must call Connect-MgGraph -Scopes 'User.Read.All, Directory.AccessAsUser.All, Directory.Read.All' before running this script" -Color "Red" + write-logFile -Message "[INFO] Ensure you are connected to Microsoft Graph by running the Connect-MgGraph -Scopes 'User.Read.All, Directory.AccessAsUser.All, User.ReadBasic.All, Directory.Read.All' command before executing this script" -Color "Yellow" Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" break } diff --git a/docs/source/functionality/UnifiedAuditLogGraph.rst b/docs/source/functionality/UnifiedAuditLogGraph.rst index c939acc..1faa314 100644 --- a/docs/source/functionality/UnifiedAuditLogGraph.rst +++ b/docs/source/functionality/UnifiedAuditLogGraph.rst @@ -1,4 +1,4 @@ -Unified Audit Log via Graph API (BETA functionality) +Unified Audit Log via Graph API ======= The UAL is a critical piece of evidence in a BEC investigation because it is a centralized source for