Chapter 12Lesson 04~160 minutes

Event Logs, System Information, and Operational Evidence

Collect evidence-first incident data with platform-aware event sources, selective system information, UTC correlation, structured reports, and integrity hashes.

Learning objectives

  • Explain evidence-first troubleshooting and why remediation can destroy clues.
  • Use Get-WinEvent efficiently on Windows with bounded source-side filters.
  • Recognize Get-WinEvent and Get-ComputerInfo as Windows-only.
  • Normalize timestamps and add correlation identifiers.
  • Use native Linux/macOS evidence sources through isolated adapters.
  • Build a read-only incident snapshot with file-integrity hashes.

1. Incident response starts by preserving evidence

When a host looks unhealthy, immediate changes can erase the clues that explain what happened. An evidence-first workflow records time, host identity, process state, relevant events, and configuration facts before remediation—unless an urgent safety/security runbook requires immediate containment.

The goal is not to collect everything. It is to collect enough structured, timestamped evidence to test a hypothesis and correlate events.

2. Get-WinEvent is Windows-only

Get-WinEvent reads Windows Event Log and Event Tracing for Windows (ETW) data. Microsoft documents it as Windows-only. On Linux, journalctl is common on systemd hosts; macOS provides the unified logging system through the native log command. Those are distinct native contracts.

[pscustomobject]@{
    IsWindows      = $IsWindows
    GetWinEvent    = [bool](Get-Command Get-WinEvent -ErrorAction SilentlyContinue)
    Journalctl     = [bool](Get-Command journalctl -ErrorAction SilentlyContinue)
    MacUnifiedLog  = $IsMacOS -and [bool](Get-Command log -CommandType Application -ErrorAction SilentlyContinue)
}

3. Discover logs/providers before guessing names

if ($IsWindows) {
    Get-WinEvent -ListLog 'System','Application' |
        Select-Object LogName,RecordCount,IsEnabled,FileSize,MaximumSizeInBytes

    Get-WinEvent -ListProvider 'Microsoft-Windows-*' |
        Select-Object -First 10 Name
}

4. Filter at the event source

Get-WinEvent -FilterHashtable can filter by log, provider, event ID, level, and time window before returning records. This is usually more efficient than reading a large log and then using Where-Object.

if ($IsWindows) {
    $filter = @{
        LogName   = 'System'
        StartTime = (Get-Date).AddHours(-2)
    }
    Get-WinEvent -FilterHashtable $filter -MaxEvents 20 -ErrorAction SilentlyContinue |
        Select-Object TimeCreated,Id,LevelDisplayName,ProviderName,Message
}

5. An event record is structured evidence, not only a message string

Useful event properties include TimeCreated, Id, LevelDisplayName, ProviderName, RecordId, MachineName, and Message. The message is important for humans, but IDs/providers/timestamps are often more stable for machine correlation.

6. Normalize time before correlation

Different systems and tools may display local time, UTC, or an offset. Keep the original timestamp when preserving evidence, and add a normalized UTC field when correlating records across hosts.

$now = Get-Date
[pscustomobject]@{
    LocalTime = $now
    UtcTime   = $now.ToUniversalTime()
    Offset    = [DateTimeOffset]$now
}

7. Get-ComputerInfo is useful—but Windows-only and very wide

Get-ComputerInfo returns a consolidated Windows system-information object. Query only properties relevant to the incident instead of dumping the entire object.

if ($IsWindows) {
    Get-ComputerInfo -Property CsName,OsName,OsVersion,OsBuildNumber |
        Select-Object CsName,OsName,OsVersion,OsBuildNumber
} else {
    [pscustomobject]@{
        Computer = [Environment]::MachineName
        OS       = [System.Runtime.InteropServices.RuntimeInformation]::OSDescription
        Arch     = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
    }
}

8. Cross-platform event sources stay behind adapters

if ($IsLinux -and (Get-Command journalctl -ErrorAction SilentlyContinue)) {
    $sample = journalctl --since '-30 minutes' --no-pager -n 20 2>$null
    [pscustomobject]@{ Source='journalctl'; Lines=@($sample).Count; ExitCode=$LASTEXITCODE }
}

if ($IsMacOS -and (Get-Command log -CommandType Application -ErrorAction SilentlyContinue)) {
    # Bounded read-only query. Native macOS syntax is intentionally isolated here.
    $sample = & log show --last 10m --style compact 2>$null | Select-Object -First 20
    [pscustomobject]@{ Source='macOS unified log'; Lines=@($sample).Count; ExitCode=$LASTEXITCODE }
}

9. Correlation turns separate clues into an investigation

A correlation identifier is a value used to connect evidence from the same request, deployment, or troubleshooting run. If your application already emits request IDs, preserve them. For a local collection run, generate an ID and attach it to every evidence file/object so later analysis knows which snapshot belongs together.

$correlationId = [guid]::NewGuid().ToString('N')
$capturedAtUtc = [datetime]::UtcNow
[pscustomobject]@{ CorrelationId=$correlationId; CapturedAtUtc=$capturedAtUtc }

10. Lab: create a read-only incident snapshot

This lab changes no host configuration. It writes only to a disposable temporary directory, records a portable system/process snapshot, adds Windows event evidence when available, and hashes every evidence file.

$root = Join-Path ([System.IO.Path]::GetTempPath()) ('ps-ch12-evidence-' + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $root | Out-Null
$correlationId = [guid]::NewGuid().ToString('N')
$capturedAt = [datetime]::UtcNow

$hostSummary = [pscustomobject]@{
    CorrelationId = $correlationId
    CapturedAtUtc  = $capturedAt
    Computer       = [Environment]::MachineName
    PowerShell     = $PSVersionTable.PSVersion.ToString()
    OS             = [System.Runtime.InteropServices.RuntimeInformation]::OSDescription
    Architecture   = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()
}
$hostSummary | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $root 'host.json') -Encoding utf8

Get-Process |
    Sort-Object WorkingSet64 -Descending |
    Select-Object -First 25 Id,ProcessName,CPU,WorkingSet64 |
    ConvertTo-Json -Depth 4 |
    Set-Content -LiteralPath (Join-Path $root 'processes.json') -Encoding utf8

if ($IsWindows -and (Get-Command Get-WinEvent -ErrorAction SilentlyContinue)) {
    $events = Get-WinEvent -FilterHashtable @{
        LogName='System'; StartTime=(Get-Date).AddHours(-1)
    } -MaxEvents 50 -ErrorAction SilentlyContinue |
        Select-Object TimeCreated,Id,LevelDisplayName,ProviderName,RecordId,Message
    $events | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $root 'events-system.json') -Encoding utf8
}

$manifest = Get-ChildItem -LiteralPath $root -File | ForEach-Object {
    $hash = Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256
    [pscustomobject]@{ File=$_.Name; SHA256=$hash.Hash; Length=$_.Length }
}
$manifest | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $root 'manifest.json') -Encoding utf8
Get-ChildItem -LiteralPath $root | Select-Object Name,Length

11. Preserve evidence before cleanup

In a real incident, do not delete the snapshot until your retention policy says it is safe. The temporary directory is appropriate for a training lab, but production evidence belongs in a controlled location with access, integrity, retention, and privacy rules.

# Training cleanup only, after you have inspected the files:
# Remove-Item -LiteralPath $root -Recurse -Force

12. Common mistakes

  • Changing the system before collecting enough evidence to explain the initial state.
  • Reading an entire event log and filtering locally when source-side filters are available.
  • Dumping every property from wide system-information objects.
  • Comparing timestamps without normalizing time zones.
  • Assuming Windows event APIs exist on Linux/macOS.
  • Storing incident evidence in uncontrolled locations that may contain sensitive data.

13. Knowledge check

Question 1. Why collect evidence before remediation?

Question 2. Is Get-WinEvent cross-platform?

Question 3. Why is -FilterHashtable usually preferable to filtering a huge event set locally?

Question 4. Why add UTC timestamps to evidence?

Question 5. What does hashing evidence files provide?

14. Summary and next bridge

Operational evidence should be narrow, structured, timestamped, platform-aware, and preserved before change. Windows provides Get-WinEvent and Get-ComputerInfo; Linux/macOS use different native evidence sources. The stable cross-platform layer is the object contract you produce. In the final lesson we apply that adapter pattern to schedulers and local administration boundaries.

15. Authoritative references

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.