Powershell : Monitor Audit logs for events.....

I was interested to see if I could monitor some audit logs, that were stored as a text file on a server for certain access violations and permission escalations, so first we need to find the type of data we are looking for with all the variables required, then we need to look at an e-mail notification system that outlines the actions detected.

This is the results that will land in your inbox, obviously certain details are redacted.


We now need to look at how this works and the flow, so I have already said this will monitor log files and then report on certain events, in this example we are looking for the even that contains the following match, in this instance we are looking for policy updates where assignment are edited by pokey.bear - below you see the name and the samAccountName:

$matches = $content | Select-String 'assigned to.*pokey.bear|was assigned to "pokey.bear1'

This is the event in the aduit log we are looking for, so lets get coding to manage this and get that email notification.

Script : AuditLogMonitor.ps1

# Define servers to monitor
$servers = @(
    "entry1.bear.local",
    "entry2.bear.local"
)

# Create timestamp for log file
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$logFolder = ".\Logging"
$logFile = Join-Path $logFolder "logmonitor_$timestamp.txt"

# Create logging directory if it doesn't exist
if (-not (Test-Path $logFolder)) {
    New-Item -ItemType Directory -Path $logFolder -Force
    Write-Host "Created logging directory: $logFolder"
}

# Function to write to both console and log file
function Write-OutputAndLog {
    param(
        [string]$Message,
        [string]$LogFile
    )
    Write-Host $Message
    Add-Content -Path $LogFile -Value $Message
}

# Function to scan logs on a remote server
function Scan-RemoteLogs {
    param (
        [string]$ServerName,
        [string]$LogFile
    )   
  Write-OutputAndLog -Message "`n=== Starting scan on server: $ServerName ===" -LogFile $LogFile   
    try {
        # Test server connectivity
        Write-OutputAndLog -Message "Testing connection to $ServerName..." -LogFile $LogFile
        if (-not (Test-Connection -ComputerName $ServerName -Count 1 -Quiet)) {
            Write-OutputAndLog -Message "ERROR: Unable to connect to server: $ServerName" -LogFile $LogFile
            return
        }
        Write-OutputAndLog -Message "Successfully connected to $ServerName" -LogFile $LogFile

        # Define the path to scan
        $path = "\\$ServerName\C$\Program Files\Twingate\logs"       

        # Verify path exists
        Write-OutputAndLog -Message "Checking path: $path" -LogFile $LogFile
        if (-not (Test-Path $path)) {
            Write-OutputAndLog -Message "ERROR: Path does not exist on server $ServerName`: $path" -LogFile $LogFile
            return
        }
        Write-OutputAndLog -Message "Path verified successfully" -LogFile $LogFile

        # Get all matching log files from the last 24 hours
        Write-OutputAndLog -Message "Scanning for Mobility_events_Log_* files from the last 24 hours..." -LogFile $LogFile
        $cutoffTime = (Get-Date).AddDays(-1)
        $files = Get-ChildItem -Path $path -Filter "Mobility_events_Log_*" -File | 
                Where-Object { $_.LastWriteTime -gt $cutoffTime }
        Write-OutputAndLog -Message "Found $($files.Count) matching files from last 24 hours" -LogFile $LogFile
               $matchCount = 0
        foreach ($file in $files) {
            Write-OutputAndLog -Message "Scanning file: $($file.Name) (Last modified: $($file.LastWriteTime))" -LogFile $LogFile           

            # Get file content and search for role assignments
            $content = Get-Content $file.FullName
             $matches = $content | Select-String 'assigned to.*pokey.bear|was assigned to "pokey.bear1'
              foreach ($match in $matches) {
                    $matchCount++
                    $matchInfo = @{
                        DateTime = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
                        Server = $ServerName
                        File = $file.Name
                        LineNumber = $match.LineNumber
                        Line = $match.Line.Trim()
                    }                   

                    # Extract the role being assigned (if possible)
                    $roleName = ""
                    if ($match.Line -match 'role "([^"]+)"') {
                        $roleName = $matches[1]
                    }                    

                    # Create detailed log entry
                    $logMessage = "[$($matchInfo.DateTime)] ROLE ASSIGNMENT FOUND`n" + `
                                "  Server: $($matchInfo.Server)`n" + `
                                "  File: $($matchInfo.File)`n" + `
                                "  Line Number: $($matchInfo.LineNumber)`n" + `
                                $(if ($roleName) {"  Role: $roleName`n"}) + `
                                "  Full Entry: $($matchInfo.Line)`n"                   
                    Write-OutputAndLog -Message $logMessage -LogFile $LogFile
                }
            }
        }       
        Write-OutputAndLog -Message "=== Scan completed for $ServerName - Found $matchCount role assignments ===" -LogFile $LogFile
    }
    catch {
        Write-OutputAndLog -Message "ERROR processing server $ServerName`: $_" -LogFile $LogFile
    }
}

# Main scanning process
Write-OutputAndLog -Message "=== Starting log scanning process at $(Get-Date) ===" -LogFile $logFile
Write-OutputAndLog -Message "Log file created at: $logFile" -LogFile $logFile
foreach ($server in $servers) {
    Scan-RemoteLogs -ServerName $server -LogFile $logFile
}
Write-OutputAndLog -Message "`n=== Scanning process completed at $(Get-Date) ===" -LogFile $logFile

This will then output all the events that match to a log file in the Logging folder however with the limitation to the act that these logs are within 24 hours, to avoid false positives.

The script find a match that match will be extracted to this look file and will look something like this:

[2024-12-17 16:42:34] ROLE ASSIGNMENT MODIFICATION
  Server: access1.bear.local
  File: Twingate_events_Log_2024-12-11.log

The next step is the next script needs to read all the log files in the logging folder and look for the phrase from earlier of "role assignment modification" it will then from the log file extract the server name and the date and time, if a valid event is found it will sent an email and then rewrite the name of the file from "log" to "temp" so it will not be included in the next check.

Script : ReportMailer.ps1

# Set the path to the Logging folder
$loggingPath = ".\Logging"

# Email configuration
$smtpServer = "smtpbear.local" 
$fromAddress = "Role.Monitoring@croucher.cloud"
$toAddress = "lee@croucher.cloud"

# Get all txt and log files in the Logging folder and subfolders
$files = Get-ChildItem -Path $loggingPath -Include *.txt,*.log -Recurse

# Initialize array to store found events
$foundEvents = @()
# Keep track of processed files
$processedFiles = @()

foreach ($file in $files) {
    # Read file content and look for "ROLE ASSIGNMENT MODIFICATION"
    $content = Get-Content $file.FullName
    $fileHasEvents = $false   

    # Process content line by line
    for ($i = 0; $i -lt $content.Count; $i++) {
        if ($content[$i] -match "ROLE ASSIGNMENT MODIFICATION") {
            $fileHasEvents = $true
            # Extract information from the next few lines
            $serverLine = $content[$i + 1]
            $fileLine = $content[$i + 2]
            $lineNumLine = $content[$i + 3]
            $roleLine = $content[$i + 4]
            $fullEntryLine = $content[$i + 5]

            # Extract server name
            $server = ($serverLine -split "Server: ")[1].Trim()

            # Extract date and time from the full entry
            if ($fullEntryLine -match "\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}") {
                $datetime = [datetime]($matches[0])
                $date = $datetime.ToString("yyyy-MM-dd")
                $time = $datetime.ToString("HH:mm:ss")
            }

            # Create event object
            $event = [PSCustomObject]@{
                Date = $date
                Time = $time
                Server = $server
                FullEntry = ($fullEntryLine -split "Full Entry: ")[1].Trim()
            }
            $foundEvents += $event
        }
    }   
    if ($fileHasEvents) {
        $processedFiles += $file
    }
}

# If events were found, send email
if ($foundEvents.Count -gt 0) {
    # Create HTML email body
    $htmlHeader = @"
    <html>
    <head>
        <style>
            body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
            .header { background-color: #cc0000; color: white; padding: 20px; margin-bottom: 20px; }
            .event-container { margin-bottom: 30px; border: 1px solid #ddd; padding: 20px; border-radius: 5px; }
            .event-header { background-color: #cc0000; color: white; padding: 10px; margin: -20px -20px 20px -20px; border-radius: 5px 5px 0 0; }
            .event-detail { margin: 10px 0; }
            .label { font-weight: bold; color: #666; }
            .value { margin-left: 10px; }
        </style>
    </head>
    <body>
        <div class="header">
            <h2>Role Assignment Changes Detected</h2>
        </div>
"@
    $htmlBody = ""
    foreach ($event in $foundEvents) {
        $htmlBody += @"
        <div class="event-container">
            <div class="event-header">
                <h3>Role Assignment Change</h3>
            </div>
            <div class="event-detail">
                <span class="label">Date:</span>
                <span class="value">$($event.Date)</span>
            </div>
            <div class="event-detail">
                <span class="label">Time:</span>
                <span class="value">$($event.Time)</span>
            </div>
            <div class="event-detail">
                <span class="label">Reporting Server:</span>
                <span class="value">$($event.Server)</span>
            </div>
            <div class="event-detail">
                <span class="label">Event:</span>
                <span class="value">$($event.FullEntry)</span>
            </div>
        </div>
"@
    }
    $htmlFooter = @"
    </body>
    </html>
"@
    $htmlEmail = $htmlHeader + $htmlBody + $htmlFooter
    # Send email
    try {
        Send-MailMessage -SmtpServer $smtpServer `
            -From $fromAddress `
            -To $toAddress `
            -Subject "Role Assignment Changes Detected" `
            -Body $htmlEmail `
            -BodyAsHtml `
            -Priority High
        Write-Host "Email notification sent successfully"       

        # After successful email, rename processed files
        foreach ($file in $processedFiles) {
            $newName = Join-Path -Path $file.DirectoryName -ChildPath ($file.BaseName + ".temp")
            try {
                Rename-Item -Path $file.FullName -NewName $newName -Force
                Write-Host "Renamed $($file.Name) to $($file.BaseName).temp"
            }
            catch {
                Write-Host "Error renaming file $($file.Name): $_"
            }
        }
    }
    catch {
        Write-Host "Error sending email: $_"
    }
}
else {
    Write-Host "No role assignment changes found"
}

Previous Post Next Post

نموذج الاتصال