Windows Event Log Project - Snowboundport37/champlain GitHub Wiki
Windows Event Log Project
This repository documents the complete implementation of the Functions and Event Logs assignment, including step-by-step progression, code files, and screenshots.
Project Overview
The assignment involves creating PowerShell functions to retrieve and process Windows Event Log data for logon/logoff events and system startup/shutdown events, with proper SID-to-username translation and modular function design.
Assignment Requirements
- Get login and logoff records from Windows Events
- Create custom objects with Time, Id, Event, and User properties
- Translate SID to username using System.Security.Principal.SecurityIdentifier
- Create functions with parameterized day ranges
- Add startup/shutdown event tracking
- Use dot-sourcing to load functions from separate files
Project Structure
├── Event-Logs.ps1 # Core functions for event log processing
├── Main.ps1 # Main script using dot-sourcing
└── README.md # This documentation
Implementation Steps
Step 1: Get Logon/Logoff Records
Initial retrieval of raw 7001 (Logon) and 7002 (Logoff) events from the System event log.
Get-EventLog -LogName System -After (Get-Date).AddDays(-14) |
Where-Object { $_.EventID -in 7001,7002 }
Key Learning: Understanding Windows Event IDs and filtering event logs by date range.
Step 2: Create Custom Table Structure
Built a loop to process events and create custom PowerShell objects with standardized properties:
- Time:
TimeGeneratedfrom original event - Id:
InstanceIdfrom original event - Event: "Logon" or "Logoff" based on InstanceId
- User: Extracted from
ReplacementStringsarray
$events = Get-EventLog -LogName System -After (Get-Date).AddDays(-14) |
Where-Object EventID -in 7001,7002
$rows = foreach ($e in $events) {
$eventName = if ($e.InstanceId -eq 7001) { 'Logon' }
elseif ($e.InstanceId -eq 7002) { 'Logoff' }
else { 'Other' }
$userRaw = if ($e.ReplacementStrings.Count -ge 2) { $e.ReplacementStrings[1] }
elseif ($e.ReplacementStrings.Count -ge 1) { $e.ReplacementStrings[0] }
else { '' }
[pscustomobject]@{
Time = $e.TimeGenerated
Id = $e.InstanceId
Event = $eventName
User = $userRaw
}
}
Step 3: SID to Username Translation
Implemented SID (Security Identifier) to readable username conversion using .NET System.Security.Principal classes.
function Convert-SidToName {
param([string]$SidOrName)
try {
$sid = New-Object System.Security.Principal.SecurityIdentifier($SidOrName)
return ($sid.Translate([System.Security.Principal.NTAccount])).Value
} catch {
return $SidOrName
}
}
Key Learning: Converting Windows SIDs to human-readable DOMAIN\Username format with proper error handling.
Step 4: Function Wrapper
Converted the script into a reusable function with parameterized day range:
function Get-LoginLogoffTable {
param([int]$Days = 14)
# Complete implementation with SID translation
# Returns sorted table of logon/logoff events
}
Step 5: Startup/Shutdown Function
Added system event tracking for 6005 (Startup) and 6006 (Shutdown) events:
function Get-StartStopTable {
param([int]$Days = 25)
$events = Get-EventLog -LogName System -After (Get-Date).AddDays(-$Days) |
Where-Object { $_.EventID -in 6005,6006 }
$rows = foreach ($e in $events) {
$eventName = if ($e.EventID -eq 6005) { 'Startup' }
elseif ($e.EventID -eq 6006) { 'Shutdown' }
else { 'Other' }
[pscustomobject]@{
Time = $e.TimeGenerated
Id = $e.EventID # Note: EventID, not InstanceId
Event = $eventName
User = 'System'
}
}
$rows | Sort-Object Time -Descending
}
Key Differences:
- Uses
EventIDinstead ofInstanceIdfor system events - Sets User to constant "System" value
- Tracks different event types (6005/6006 vs 7001/7002)
Step 6: Dot-Sourcing Implementation
Created modular design using dot-sourcing to load functions from separate files:
Main.ps1:
# Load functions using dot-sourcing
. (Join-Path $PSScriptRoot 'Event-Logs.ps1')
Clear-Host
# Configure day windows
$daysLogin = 14
$daysSystem = 25
Write-Host "`n==== Logon and Logoff ====" -ForegroundColor Cyan
Get-LoginLogoffTable -Days $daysLogin | Format-Table -Auto
Write-Host "`n==== Shutdowns ====" -ForegroundColor Cyan
Get-StartStopTable -Days $daysSystem | Where-Object Event -eq 'Shutdown' | Format-Table -Auto
Write-Host "`n==== Startups ====" -ForegroundColor Cyan
Get-StartStopTable -Days $daysSystem | Where-Object Event -eq 'Startup' | Format-Table -Auto
📸 SCREENSHOT NEEDED: Take a final screenshot showing the complete Main.ps1 execution with all three sections displayed:
- Logon and Logoff table with translated usernames
- Shutdowns table filtered to show only shutdown events
- Startups table filtered to show only startup events
This screenshot should demonstrate the final working solution with proper formatting, colored headers, and all data correctly processed.
Complete Code Files
Event-Logs.ps1
# Helper: SID to DOMAIN\User
function Convert-SidToName {
param([string]$SidOrName)
try {
$sid = New-Object System.Security.Principal.SecurityIdentifier($SidOrName)
return ($sid.Translate([System.Security.Principal.NTAccount])).Value
} catch {
return $SidOrName
}
}
# Logon and Logoff Events (7001, 7002)
function Get-LoginLogoffTable {
param([int]$Days = 14)
$events = Get-EventLog -LogName System -After (Get-Date).AddDays(-$Days) |
Where-Object { $_.EventID -in 7001,7002 }
$rows = foreach ($e in $events) {
$eventName = if ($e.InstanceId -eq 7001) { 'Logon' }
elseif ($e.InstanceId -eq 7002) { 'Logoff' }
else { 'Other' }
$userRaw = if ($e.ReplacementStrings.Count -ge 2) { $e.ReplacementStrings[1] }
elseif ($e.ReplacementStrings.Count -ge 1) { $e.ReplacementStrings[0] }
else { '' }
[pscustomobject]@{
Time = $e.TimeGenerated
Id = $e.InstanceId
Event = $eventName
User = Convert-SidToName $userRaw
}
}
$rows | Sort-Object Time -Descending
}
# Startup and Shutdown Events (6005, 6006)
function Get-StartStopTable {
param([int]$Days = 25)
$events = Get-EventLog -LogName System -After (Get-Date).AddDays(-$Days) |
Where-Object { $_.EventID -in 6005,6006 }
$rows = foreach ($e in $events) {
$eventName = if ($e.EventID -eq 6005) { 'Startup' }
elseif ($e.EventID -eq 6006) { 'Shutdown' }
else { 'Other' }
[pscustomobject]@{
Time = $e.TimeGenerated
Id = $e.EventID # Note: EventID for system events
Event = $eventName
User = 'System'
}
}
$rows | Sort-Object Time -Descending
}
Main.ps1
# Load functions using dot-sourcing
. (Join-Path $PSScriptRoot 'Event-Logs.ps1')
Clear-Host
# Adjust day windows if needed
$daysLogin = 14
$daysSystem = 25
Write-Host "`n==== Logon and Logoff ====" -ForegroundColor Cyan
Get-LoginLogoffTable -Days $daysLogin | Format-Table -Auto
Write-Host "`n==== Shutdowns ====" -ForegroundColor Cyan
Get-StartStopTable -Days $daysSystem | Where-Object Event -eq 'Shutdown' | Format-Table -Auto
Write-Host "`n==== Startups ====" -ForegroundColor Cyan
Get-StartStopTable -Days $daysSystem | Where-Object Event -eq 'Startup' | Format-Table -Auto
Usage Instructions
- Clone or download both script files to the same directory
- Run Main.ps1 to execute the complete demonstration
- Adjust day ranges by modifying
$daysLoginand$daysSystemvariables - Run individual functions by dot-sourcing Event-Logs.ps1 and calling functions directly
# Example of running individual functions
. .\Event-Logs.ps1
Get-LoginLogoffTable -Days 7
Get-StartStopTable -Days 30 | Where-Object Event -eq 'Startup'
Final Results
Key Technical Concepts
Event Log Types
- 7001/7002: User logon/logoff events (uses InstanceId)
- 6005/6006: System startup/shutdown events (uses EventId)
SID Translation
- Converts Windows Security Identifiers to readable DOMAIN\Username format
- Handles errors gracefully by returning original value if translation fails
PowerShell Techniques
- Dot-sourcing: Loading functions from external files with
. script.ps1 - Custom objects: Creating structured data with
[pscustomobject]@{} - Pipeline filtering: Using
Where-Objectfor efficient data filtering - Error handling: Try-catch blocks for robust SID translation
Assignment Fulfillment
✅ Login/Logoff logs obtained - Events 7001/7002 retrieved and processed
✅ Shutdown/Startup logs obtained - Events 6006/6005 retrieved and processed
✅ Functions and dot-sourcing working - Modular design with proper function loading
✅ SID translation successful - User identifiers converted to readable usernames