1- <#
1+ <#
22. SYNOPSIS
33 Migrates Windows Event Log storage to a new target root and updates BOTH:
44 - Classic EventLog registry paths (HKLM:\SYSTEM\CCS\Services\EventLog)
@@ -81,17 +81,17 @@ if (-not (Test-Administrator)) {
8181}
8282
8383# --- Logging ---
84- $scriptName = [System.IO.Path ]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name )
85- $logDir = ' C:\Logs-TEMP'
84+ $scriptName = [System.IO.Path ]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name )
85+ $logDir = ' C:\Logs-TEMP'
8686$logFileName = " ${scriptName} _$ ( Get-Date - Format ' yyyyMMddHHmmss' ) .log"
87- $logPath = Join-Path $logDir $logFileName
87+ $logPath = Join-Path $logDir $logFileName
8888if (-not (Test-Path - LiteralPath $logDir )) { New-Item - Path $logDir - ItemType Directory - Force | Out-Null }
8989
9090function Write-Log {
9191 [CmdletBinding ()]
9292 param (
9393 [Parameter (Mandatory )][string ]$Message ,
94- [Parameter ()][ValidateSet (' INFO' , ' WARN' , ' ERROR' )] [string ]$Level = ' INFO'
94+ [Parameter ()][ValidateSet (' INFO' , ' WARN' , ' ERROR' )] [string ]$Level = ' INFO'
9595 )
9696 $timestamp = Get-Date - Format " yyyy-MM-dd HH:mm:ss"
9797 $logEntry = " [$timestamp ] [$Level ] $Message "
@@ -102,12 +102,12 @@ function Format-ExceptionDetail {
102102 param ([Parameter (Mandatory )][System.Management.Automation.ErrorRecord ]$ErrorRecord )
103103 $ex = $ErrorRecord.Exception
104104 $type = if ($ex ) { $ex.GetType ().FullName } else { " UnknownExceptionType" }
105- $msg = if ($ex ) { $ex.Message } else { [string ]$ErrorRecord }
105+ $msg = if ($ex ) { $ex.Message } else { [string ]$ErrorRecord }
106106 $pos = " "
107107 try { $pos = $ErrorRecord.InvocationInfo.PositionMessage } catch { }
108108 $stack = " "
109109 try { $stack = $ex.StackTrace } catch { }
110- @"
110+ @"
111111ExceptionType: $type
112112Message: $msg
113113
@@ -149,19 +149,19 @@ $DefaultLogsFolder = Join-Path $env:SystemRoot 'System32\winevt\Logs'
149149
150150function Get-SafeName {
151151 param ([Parameter (Mandatory )][string ]$Name )
152- $n = $Name -replace ' %4' , ' -'
153- $n = $n -replace ' /' , ' -'
152+ $n = $Name -replace ' %4' , ' -'
153+ $n = $n -replace ' /' , ' -'
154154 $invalid = ([IO.Path ]::GetInvalidFileNameChars() + [IO.Path ]::GetInvalidPathChars()) | Sort-Object - Unique
155- foreach ($c in $invalid ){ $n = $n -replace [Regex ]::Escape([string ]$c ), ' -' }
156- $n = ($n -replace ' [\s\-]+' , ' -' ).Trim().Trim(' .' ).Trim(' -' )
157- if ([string ]::IsNullOrWhiteSpace($n )){ $n = ' Log' }
155+ foreach ($c in $invalid ) { $n = $n -replace [Regex ]::Escape([string ]$c ), ' -' }
156+ $n = ($n -replace ' [\s\-]+' , ' -' ).Trim().Trim(' .' ).Trim(' -' )
157+ if ([string ]::IsNullOrWhiteSpace($n )) { $n = ' Log' }
158158 return $n
159159}
160160
161161function Expand-EnvPath { param ([Parameter (Mandatory )][string ]$Path ) try { [Environment ]::ExpandEnvironmentVariables($Path ) } catch { $Path } }
162162
163163function New-UniqueArchiveName {
164- param ([Parameter (Mandatory )][string ]$Dir , [Parameter (Mandatory )][string ]$Base )
164+ param ([Parameter (Mandatory )][string ]$Dir , [Parameter (Mandatory )][string ]$Base )
165165 do {
166166 $stamp = Get-Date - Format ' yyyyMMddHHmmssfff'
167167 $candidate = Join-Path $Dir (" {0}_{1}.evtx" -f $Base , $stamp )
@@ -178,7 +178,7 @@ function Invoke-Retry {
178178 [int ]$MaxAttempts = 8 ,
179179 [int ]$DelayMs = 350
180180 )
181- for ($i = 1 ; $i -le $MaxAttempts ; $i ++ ) {
181+ for ($i = 1 ; $i -le $MaxAttempts ; $i ++ ) {
182182 try { return & $Action }
183183 catch {
184184 if ($i -eq $MaxAttempts ) { throw }
@@ -199,17 +199,17 @@ function Get-TargetRouting {
199199 # Archive-<LogOrChannel>-YYYY-MM-DD-HH-MM-SS-mmm.evtx
200200 $rx = ' ^Archive-(?<log>.+)-\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2}-\d{3}$'
201201 if ($base -match $rx ) {
202- $logical = $matches [' log' ]
203- $folderName = Get-SafeName - Name $logical
202+ $logical = $matches [' log' ]
203+ $folderName = Get-SafeName - Name $logical
204204 $targetFolder = Join-Path - Path $TargetRoot - ChildPath $folderName
205- $destFile = Join-Path - Path $targetFolder - ChildPath $LogFile.Name
206- return [pscustomobject ]@ { TargetFolder = $targetFolder ; DestFile = $destFile ; IsArchive = $true }
205+ $destFile = Join-Path - Path $targetFolder - ChildPath $LogFile.Name
206+ return [pscustomobject ]@ { TargetFolder = $targetFolder ; DestFile = $destFile ; IsArchive = $true }
207207 }
208208
209- $folderName = Get-SafeName - Name $base
209+ $folderName = Get-SafeName - Name $base
210210 $targetFolder = Join-Path - Path $TargetRoot - ChildPath $folderName
211- $destFile = Join-Path - Path $targetFolder - ChildPath (" {0}.evtx" -f $folderName )
212- return [pscustomobject ]@ { TargetFolder = $targetFolder ; DestFile = $destFile ; IsArchive = $false }
211+ $destFile = Join-Path - Path $targetFolder - ChildPath (" {0}.evtx" -f $folderName )
212+ return [pscustomobject ]@ { TargetFolder = $targetFolder ; DestFile = $destFile ; IsArchive = $false }
213213}
214214
215215# --- NEW: ACL Baseline (REQUIRED) ---
@@ -243,13 +243,13 @@ function Set-LogRepositoryAclBaseline {
243243 }
244244
245245 $inherit = [System.Security.AccessControl.InheritanceFlags ]' ContainerInherit, ObjectInherit'
246- $prop = [System.Security.AccessControl.PropagationFlags ]::None
247- $allow = [System.Security.AccessControl.AccessControlType ]::Allow
246+ $prop = [System.Security.AccessControl.PropagationFlags ]::None
247+ $allow = [System.Security.AccessControl.AccessControlType ]::Allow
248248
249249 # --- Well-known SIDs (language independent) ---
250- $sidSystem = New-Object System.Security.Principal.SecurityIdentifier ' S-1-5-18' # LocalSystem
251- $sidAdmins = New-Object System.Security.Principal.SecurityIdentifier ' S-1-5-32-544' # Builtin Administrators
252- $sidAuthUsers = New-Object System.Security.Principal.SecurityIdentifier ' S-1-5-11' # Authenticated Users
250+ $sidSystem = New-Object System.Security.Principal.SecurityIdentifier ' S-1-5-18' # LocalSystem
251+ $sidAdmins = New-Object System.Security.Principal.SecurityIdentifier ' S-1-5-32-544' # Builtin Administrators
252+ $sidAuthUsers = New-Object System.Security.Principal.SecurityIdentifier ' S-1-5-11' # Authenticated Users
253253
254254 # --- Service SID: NT SERVICE\EventLog ---
255255 # Resolve dynamically to avoid translation issues
@@ -324,7 +324,7 @@ function Snapshot-ServiceState {
324324
325325function Restore-ServiceState {
326326 foreach ($kvp in $Global :ServiceState.GetEnumerator ()) {
327- $name = $kvp.Key
327+ $name = $kvp.Key
328328 $state = $kvp.Value
329329 try {
330330 $svc = Get-Service - Name $name - ErrorAction Stop
@@ -411,7 +411,7 @@ function Invoke-WevtutilTimed {
411411 $psi.Arguments = ($Arguments -join ' ' )
412412 $psi.UseShellExecute = $false
413413 $psi.RedirectStandardOutput = $true
414- $psi.RedirectStandardError = $true
414+ $psi.RedirectStandardError = $true
415415 $psi.CreateNoWindow = $true
416416
417417 $p = New-Object System.Diagnostics.Process
@@ -420,14 +420,14 @@ function Invoke-WevtutilTimed {
420420
421421 if (-not $p.WaitForExit ($TimeoutSeconds * 1000 )) {
422422 try { $p.Kill () } catch { }
423- return [pscustomobject ]@ { TimedOut = $true ; ExitCode = $null ; StdOut = " " ; StdErr = " Timed out after $TimeoutSeconds seconds." }
423+ return [pscustomobject ]@ { TimedOut = $true ; ExitCode = $null ; StdOut = " " ; StdErr = " Timed out after $TimeoutSeconds seconds." }
424424 }
425425
426426 return [pscustomobject ]@ {
427427 TimedOut = $false
428428 ExitCode = $p.ExitCode
429- StdOut = $p.StandardOutput.ReadToEnd ()
430- StdErr = $p.StandardError.ReadToEnd ()
429+ StdOut = $p.StandardOutput.ReadToEnd ()
430+ StdErr = $p.StandardError.ReadToEnd ()
431431 }
432432}
433433
@@ -441,7 +441,7 @@ function Get-ChannelLogFileName {
441441 $lines = $r.StdOut -split " `r ?`n "
442442 $line = $lines | Where-Object { $_ -match ' ^\s*logFileName\s*:\s*' } | Select-Object - First 1
443443 if (-not $line ) { return $null }
444- (($line -replace ' ^\s*logFileName\s*:\s*' , ' ' ).Trim())
444+ (($line -replace ' ^\s*logFileName\s*:\s*' , ' ' ).Trim())
445445}
446446
447447function Set-ChannelLogFileName {
@@ -490,9 +490,9 @@ function Update-WinevtChannels {
490490
491491 if (-not $inDefault ) { $skipped ++ ; continue }
492492
493- $safe = Get-SafeName - Name ($ch -replace ' /' , ' -' )
493+ $safe = Get-SafeName - Name ($ch -replace ' /' , ' -' )
494494 $folder = Join-Path - Path $TargetRoot - ChildPath $safe
495- $file = Join-Path - Path $folder - ChildPath (" {0}.evtx" -f $safe )
495+ $file = Join-Path - Path $folder - ChildPath (" {0}.evtx" -f $safe )
496496
497497 if (-not (Test-Path - LiteralPath $folder )) {
498498 try { New-Item - Path $folder - ItemType Directory - Force | Out-Null } catch { }
@@ -527,7 +527,7 @@ function Update-ClassicRegistryPaths {
527527 $safeLog = Get-SafeName - Name $logName
528528
529529 $newFolder = Join-Path - Path $TargetRoot - ChildPath $safeLog
530- $newFile = Join-Path - Path $newFolder - ChildPath (" {0}.evtx" -f $safeLog )
530+ $newFile = Join-Path - Path $newFolder - ChildPath (" {0}.evtx" -f $safeLog )
531531
532532 if (-not (Test-Path - LiteralPath $newFolder )) {
533533 New-Item - Path $newFolder - ItemType Directory - Force | Out-Null
@@ -555,8 +555,8 @@ function Set-ClassicLogSizes153600KB {
555555 [CmdletBinding ()]
556556 param ()
557557
558- $classic = @ (' Application' , ' Security' , ' Setup' , ' System' )
559- $maxKb = 153600
558+ $classic = @ (' Application' , ' Security' , ' Setup' , ' System' )
559+ $maxKb = 153600
560560
561561 foreach ($name in $classic ) {
562562 try {
@@ -574,7 +574,7 @@ function Copy-Or-Move-EventLogs {
574574 param (
575575 [Parameter (Mandatory )][string ]$TargetRoot ,
576576 [Parameter (Mandatory )][bool ]$StagingCopyOnly ,
577- [Parameter (Mandatory = $false )][object ]$ProgressBar
577+ [Parameter (Mandatory = $false )][object ]$ProgressBar
578578 )
579579
580580 if (-not (Test-Path - LiteralPath $TargetRoot )) {
@@ -586,7 +586,7 @@ function Copy-Or-Move-EventLogs {
586586 if ($ProgressBar -and $ProgressBar -is [System.Windows.Forms.ProgressBar ]) {
587587 $ProgressBar.Minimum = 0
588588 $ProgressBar.Maximum = $logFiles.Count
589- $ProgressBar.Value = 0
589+ $ProgressBar.Value = 0
590590 }
591591
592592 $mappings = New-Object System.Collections.Generic.List[pscustomobject ]
@@ -605,7 +605,7 @@ function Copy-Or-Move-EventLogs {
605605 # If destination exists, generate a unique file name and copy.
606606 $dest = $route.DestFile
607607 if (Test-Path - LiteralPath $dest ) {
608- $dir = Split-Path - Path $dest - Parent
608+ $dir = Split-Path - Path $dest - Parent
609609 $base = [IO.Path ]::GetFileNameWithoutExtension($dest )
610610 $dest = New-UniqueArchiveName - Dir $dir - Base $base
611611 }
@@ -614,12 +614,12 @@ function Copy-Or-Move-EventLogs {
614614 Copy-Item - LiteralPath $logFile.FullName - Destination $dest - Force - ErrorAction Stop
615615 } | Out-Null
616616
617- Write-Log - Message (" Copied{0}: {1} -> {2}" -f ($ (if ($StagingCopyOnly ){ " (staging)" }else { " " })), $logFile.Name , $dest )
617+ Write-Log - Message (" Copied{0}: {1} -> {2}" -f ($ (if ($StagingCopyOnly ) { " (staging)" }else { " " })), $logFile.Name , $dest )
618618
619619 $mappings.Add ([pscustomobject ]@ {
620- SourceFullPath = $logFile.FullName
621- DestinationFullPath = $dest
622- }) | Out-Null
620+ SourceFullPath = $logFile.FullName
621+ DestinationFullPath = $dest
622+ }) | Out-Null
623623
624624 } catch {
625625 Write-Log - Message (" Copy error: {0} ({1})" -f $logFile.FullName , $_.Exception.Message ) - Level " WARN"
@@ -637,7 +637,7 @@ function Copy-Or-Move-EventLogs {
637637
638638function Cleanup-SourceEvtx {
639639 [CmdletBinding ()]
640- param ([Parameter (Mandatory = $false )][object ]$Mappings )
640+ param ([Parameter (Mandatory = $false )][object ]$Mappings )
641641
642642 if ($null -eq $Mappings ) { Write-Log - Message " Cleanup skipped: mappings null." - Level " WARN" ; return }
643643 $items = @ ($Mappings )
@@ -716,63 +716,63 @@ function Setup-GUI {
716716 $form.Controls.Add ($buttonClose )
717717
718718 $buttonRun.Add_Click ({
719- $targetRoot = $textBox.Text.Trim ()
720- $staging = [bool ]$chkStaging.Checked
721- $mappings = @ ()
722-
723- if ([string ]::IsNullOrWhiteSpace($targetRoot )) {
724- [System.Windows.Forms.MessageBox ]::Show(" Please enter the target root folder." , " Input Error" ,
725- [System.Windows.Forms.MessageBoxButtons ]::OK, [System.Windows.Forms.MessageBoxIcon ]::Error) | Out-Null
726- return
727- }
719+ $targetRoot = $textBox.Text.Trim ()
720+ $staging = [bool ]$chkStaging.Checked
721+ $mappings = @ ()
722+
723+ if ([string ]::IsNullOrWhiteSpace($targetRoot )) {
724+ [System.Windows.Forms.MessageBox ]::Show(" Please enter the target root folder." , " Input Error" ,
725+ [System.Windows.Forms.MessageBoxButtons ]::OK, [System.Windows.Forms.MessageBoxIcon ]::Error) | Out-Null
726+ return
727+ }
728728
729- try {
730- $statusLabel.Text = " Applying ACL baseline to target..."
731- Set-LogRepositoryAclBaseline - RootPath $targetRoot
729+ try {
730+ $statusLabel.Text = " Applying ACL baseline to target..."
731+ Set-LogRepositoryAclBaseline - RootPath $targetRoot
732732
733- $statusLabel.Text = " Stopping services..."
734- Stop-For - Migration
733+ $statusLabel.Text = " Stopping services..."
734+ Stop-For - Migration
735735
736- $statusLabel.Text = " Copying .evtx files..."
737- $mappings = Copy-Or - Move-EventLogs - TargetRoot $targetRoot - StagingCopyOnly $staging - ProgressBar $progressBar
738- if ($null -eq $mappings ) { $mappings = @ () }
736+ $statusLabel.Text = " Copying .evtx files..."
737+ $mappings = Copy-Or - Move-EventLogs - TargetRoot $targetRoot - StagingCopyOnly $staging - ProgressBar $progressBar
738+ if ($null -eq $mappings ) { $mappings = @ () }
739739
740- $statusLabel.Text = " Updating classic registry paths..."
741- $okClassic = Update-ClassicRegistryPaths - TargetRoot $targetRoot
740+ $statusLabel.Text = " Updating classic registry paths..."
741+ $okClassic = Update-ClassicRegistryPaths - TargetRoot $targetRoot
742742
743- $statusLabel.Text = " Updating WINEVT channels..."
744- $okWinevt = Update-WinevtChannels - TargetRoot $targetRoot
743+ $statusLabel.Text = " Updating WINEVT channels..."
744+ $okWinevt = Update-WinevtChannels - TargetRoot $targetRoot
745745
746- $statusLabel.Text = " Enforcing classic log size (153600 KB)..."
747- Set-ClassicLogSizes153600KB
746+ $statusLabel.Text = " Enforcing classic log size (153600 KB)..."
747+ Set-ClassicLogSizes153600KB
748748
749- $statusLabel.Text = " Restoring services..."
750- Start-After - Migration
749+ $statusLabel.Text = " Restoring services..."
750+ Start-After - Migration
751751
752- if (-not $staging -and $okClassic -and $okWinevt -and @ ($mappings ).Count -gt 0 ) {
753- $statusLabel.Text = " Deleting sources..."
754- Cleanup- SourceEvtx - Mappings $mappings
755- } else {
756- Write-Log - Message " StagingCopyOnly enabled or update failures; sources NOT deleted." - Level " WARN"
757- }
752+ if (-not $staging -and $okClassic -and $okWinevt -and @ ($mappings ).Count -gt 0 ) {
753+ $statusLabel.Text = " Deleting sources..."
754+ Cleanup- SourceEvtx - Mappings $mappings
755+ } else {
756+ Write-Log - Message " StagingCopyOnly enabled or update failures; sources NOT deleted." - Level " WARN"
757+ }
758758
759- $progressBar.Value = $progressBar.Maximum
760- $buttonRun.Enabled = $false
761- $buttonClose.Enabled = $true
762- $statusLabel.Text = " Completed."
759+ $progressBar.Value = $progressBar.Maximum
760+ $buttonRun.Enabled = $false
761+ $buttonClose.Enabled = $true
762+ $statusLabel.Text = " Completed."
763763
764- [System.Windows.Forms.MessageBox ]::Show(
765- " Completed.`n`n Log: $logPath " ,
766- " Done" ,
767- [System.Windows.Forms.MessageBoxButtons ]::OK,
768- [System.Windows.Forms.MessageBoxIcon ]::Information
769- ) | Out-Null
764+ [System.Windows.Forms.MessageBox ]::Show(
765+ " Completed.`n`n Log: $logPath " ,
766+ " Done" ,
767+ [System.Windows.Forms.MessageBoxButtons ]::OK,
768+ [System.Windows.Forms.MessageBoxIcon ]::Information
769+ ) | Out-Null
770770
771- } catch {
772- Handle- Error - Message " Migration failed. Review the log for details." - Exception $_
773- try { Start-After - Migration } catch { }
774- }
775- })
771+ } catch {
772+ Handle- Error - Message " Migration failed. Review the log for details." - Exception $_
773+ try { Start-After - Migration } catch { }
774+ }
775+ })
776776
777777 $form.ShowDialog () | Out-Null
778778}
0 commit comments