Skip to content

Commit

Permalink
Adds abilityt to parse and write simple_user_changes and user_changes…
Browse files Browse the repository at this point in the history
…. User changes still has raw auditlog blob in it.
  • Loading branch information
jonnybottles committed Dec 8, 2024
1 parent 51546a6 commit e96ee88
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 100 deletions.
95 changes: 52 additions & 43 deletions Hawk/functions/User/Get-HawkUserAdminAudit.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,69 +8,78 @@
.PARAMETER UserPrincipalName
UserPrincipalName of the user you're investigating
.OUTPUTS
File: Simple_User_Changes.csv
Path: <user>
Path: \<user>
Description: All cmdlets that were run against the user in a simple format.
File: User_Changes.csv
Path: \<user>
Description: Raw data of all changes made to the user.
File: User_Changes_Raw.json
Path: \<user>
Description: Raw JSON data from audit logs.
File: User_Changes_Raw.txt
Path: \<user>
Description: Human readable format of raw audit data.
.EXAMPLE
Get-HawkUserAdminAudit -UserPrincipalName [email protected]
Gets all changes made to [email protected] and outputs them to the csv and json files.
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[array]$UserPrincipalName
)

param
(
[Parameter(Mandatory = $true)]
[array]$UserPrincipalName
)
Test-EXOConnection
Send-AIEvent -Event "CmdRun"

Test-EXOConnection
Send-AIEvent -Event "CmdRun"
# Verify our UPN input
[array]$UserArray = Test-UserObject -ToTest $UserPrincipalName

# Verify our UPN input
[array]$UserArray = Test-UserObject -ToTest $UserPrincipalName
foreach ($Object in $UserArray) {
[string]$User = $Object.UserPrincipalName

foreach ($Object in $UserArray) {
[string]$User = $Object.UserPrincipalName
# Get the mailbox name since that is what we store in the admin audit log
$MailboxName = (Get-Mailbox -Identity $User).Name

# Get the mailbox name (used previously)
$MailboxName = (Get-Mailbox -Identity $User).Name
Out-LogFile ("Searching for changes made to: " + $MailboxName) -action

Out-LogFile ("Searching for changes made to: " + $MailboxName) -action
# Get all changes for this user
[array]$UserChanges = Search-UnifiedAuditLog -UserIds $User -StartDate $Hawk.StartDate -EndDate $Hawk.EndDate -RecordType ExchangeAdmin -Operations "*" -ResultSize 5000

# Get all changes for this user from the Unified Audit Logs
[array]$UserChanges = Search-UnifiedAuditLog -UserIds $User -StartDate $Hawk.StartDate -EndDate $Hawk.EndDate -RecordType ExchangeAdmin -Operations "*" -ResultSize 5000
# If there are any results push them to an output file
if ($UserChanges.Count -gt 0) {
Out-LogFile ("Found " + $UserChanges.Count + " changes made to this user")

# If there are any results, handle them
if ($UserChanges.Count -gt 0) {
Out-LogFile ("Found " + $UserChanges.Count + " changes made to this user")
# Get the user's output folder path
$UserFolder = Get-HawkUserPath -User $User

# Determine the user's output folder
$UserFolder = (Get-HawkUserPath -User $User)
# Write raw AuditData to files for verification/debugging
$RawJsonPath = Join-Path -Path $UserFolder -ChildPath "User_Changes_Raw.json"
$UserChanges | Select-Object -ExpandProperty AuditData | Out-File -FilePath $RawJsonPath

# Write raw AuditData JSON to a JSON file for verification
$RawJsonPath = Join-Path $UserFolder "User_Changes_Raw.json"
$UserChanges | Select-Object -ExpandProperty AuditData | Out-File $RawJsonPath
$RawTxtPath = Join-Path -Path $UserFolder -ChildPath "User_Changes_Raw.txt"
"User: $User" | Out-File -FilePath $RawTxtPath
$UserChanges | Select-Object -ExpandProperty AuditData | Out-File -FilePath $RawTxtPath -Append
"------------------------------------" | Out-File -FilePath $RawTxtPath -Append

# Also write raw data to a text file (similar to previous testing snippet)
$RawTxtPath = Join-Path $UserFolder "User_Changes_Raw.txt"
"User: $User" | Out-File $RawTxtPath
$UserChanges | Select-Object -ExpandProperty AuditData | Out-File $RawTxtPath -Append
"------------------------------------" | Out-File $RawTxtPath -Append
# Parse and format the changes using Get-SimpleUnifiedAuditLog
$ParsedChanges = $UserChanges | Get-SimpleUnifiedAuditLog

# Parse the results with the new Get-SimpleUnifiedAuditLog function
$ParsedChanges = $UserChanges | ForEach-Object {
$AuditDataJson = $_.AuditData
$AuditDataObj = $AuditDataJson | ConvertFrom-Json
$AuditDataObj
} | Get-SimpleUnifiedAuditLog

# Output the parsed results
# Output the processed results
if ($ParsedChanges) {
$ParsedChanges | Out-MultipleFileType -FilePrefix "Simple_User_Changes" -csv -json -User $User
$UserChanges | Out-MultipleFileType -FilePrefix "User_Changes" -csv -json -User $User
}
else {
Out-LogFile "No User Changes found."
}

# Output the raw changes
$UserChanges | Out-MultipleFileType -FilePrefix "User_Changes" -csv -json -User $User
}
else {
Out-LogFile "No User Changes found."
}
}
}
85 changes: 28 additions & 57 deletions Hawk/internal/functions/Get-SimpleUnifiedAuditLog.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2,79 +2,50 @@
[CmdletBinding()]
Param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[PSObject]$UALRecord
[PSObject]$Record
)

Begin {
Write-Verbose "Starting Get-SimpleUnifiedAuditLog processing"
$Results = @()
}

Process {
foreach ($record in $UALRecord) {
try {
Write-Verbose "Processing record with ID: $($record.Identity)"
try {
# Convert the AuditData JSON string to an object
$AuditData = $Record | Select-Object -ExpandProperty AuditData | ConvertFrom-Json

# The AuditData is a JSON string, so convert it
if ($record.AuditData) {
$AuditRecord = $record.AuditData | ConvertFrom-Json

# Create result object with data from audit record
$obj = [PSCustomObject]@{
Caller = if ($AuditRecord.UserId) { $AuditRecord.UserId } else { "***" }
Cmdlet = $AuditRecord.Operation
FullCommand = $AuditRecord.Operation
'RunDate(UTC)' = $AuditRecord.CreationTime
ObjectModified = $AuditRecord.ObjectId
}
if ($AuditData) {
$obj = [PSCustomObject]@{
Caller = $AuditData.UserId
Cmdlet = $AuditData.Operation
FullCommand = $AuditData.Operation
'RunDate(UTC)' = $AuditData.CreationTime
ObjectModified = $AuditData.ObjectId
}

# Add parameters to FullCommand if they exist
if ($AuditRecord.Parameters) {
$paramString = foreach ($param in $AuditRecord.Parameters) {
# Handle different parameter value types appropriately
$value = if ($param.Value -match '\s') {
# If value contains spaces, quote it
"'$($param.Value)'"
} elseif ($param.Value -match '^(True|False)$') {
# If boolean, format with $
"`$$($param.Value.ToLower())"
} else {
$param.Value
}
"-$($param.Name) $value"
# Add parameters to FullCommand
if ($AuditData.Parameters) {
$paramStrings = foreach ($param in $AuditData.Parameters) {
$value = switch -Regex ($param.Value) {
'^\s+|\s+$' { "'$($param.Value)'" } # Has leading/trailing spaces
'\s' { "'$($param.Value)'" } # Contains spaces
'^True$|^False$' { "`$$($param.Value.ToLower())" } # Boolean
default { $param.Value }
}
$obj.FullCommand = "$($obj.Cmdlet) $($paramString -join ' ')"
}

$Results += $obj
Write-Verbose "Successfully processed record"
}
else {
Write-Verbose "No AuditData found for record"
$Results += [PSCustomObject]@{
Caller = "***"
Cmdlet = "Unknown"
FullCommand = "No audit data available"
'RunDate(UTC)' = $null
ObjectModified = $null
"-$($param.Name) $value"
}
$obj.FullCommand = "$($AuditData.Operation) $($paramStrings -join ' ')"
}

$Results += $obj
}
catch {
Write-Verbose "Error processing record: $_"
$Results += [PSCustomObject]@{
Caller = "***"
Cmdlet = "Error"
FullCommand = "Error processing audit record: $_"
'RunDate(UTC)' = $null
ObjectModified = $null
}
}
}
catch {
Write-Verbose "Error processing record: $_"
}
}

End {
Write-Verbose "Completed processing. Returning $($Results.Count) records"
return $Results
$Results
}
}

0 comments on commit e96ee88

Please sign in to comment.