From aed04e8ca470cd3e024054104deb154b65a187e4 Mon Sep 17 00:00:00 2001 From: sysadmin Date: Fri, 3 Apr 2026 09:48:59 -0700 Subject: [PATCH] Add AD scripts and stage import instructions Co-Authored-By: Claude Opus 4.6 (1M context) --- STAGE-IMPORT-INSTRUCTIONS.md | 68 ++++++++++ scripts/Configure-TranscriptLogging.ps1 | 88 ++++++++++++ scripts/Get-ADComputerReport.ps1 | 87 ++++++++++++ scripts/Get-ADUserReport.ps1 | 92 +++++++++++++ scripts/Get-GPOStatus.ps1 | 111 +++++++++++++++ scripts/Get-ReplicationHealth.ps1 | 173 ++++++++++++++++++++++++ scripts/Invoke-LogRotation.ps1 | 107 +++++++++++++++ 7 files changed, 726 insertions(+) create mode 100644 STAGE-IMPORT-INSTRUCTIONS.md create mode 100644 scripts/Configure-TranscriptLogging.ps1 create mode 100644 scripts/Get-ADComputerReport.ps1 create mode 100644 scripts/Get-ADUserReport.ps1 create mode 100644 scripts/Get-GPOStatus.ps1 create mode 100644 scripts/Get-ReplicationHealth.ps1 create mode 100644 scripts/Invoke-LogRotation.ps1 diff --git a/STAGE-IMPORT-INSTRUCTIONS.md b/STAGE-IMPORT-INSTRUCTIONS.md new file mode 100644 index 0000000..5218f0e --- /dev/null +++ b/STAGE-IMPORT-INSTRUCTIONS.md @@ -0,0 +1,68 @@ +# Stage TXT Import Task +# Date: 2026-03-28 +# Context: CTONWTXT.BAT now uploads C:\STAGE\*.TXT from DOS machines to T:\STAGE\%MACHINE%\ + +## What happened + +1. CTONWTXT.BAT was never being called -- fixed, now called from CTONW.BAT on every boot +2. Destination changed from broken X: (Novell serve.sys check) to T:\STAGE\%MACHINE%\ +3. DOS 6.22 can't MD on existing dirs without error, so dirs are pre-created on NAS +4. All TS-* machine folders pre-created under /data/test/STAGE/ on D2TESTNAS + +## What needs to run + +Save the script below as C:\Shares\testdatadb\import-all-stage.js and run it: + + cd C:\Shares\testdatadb + node import-all-stage.js + +## What it does + +- Scans \\D2TESTNAS\test\STAGE\TS-*\*.TXT (~8,100 files across 10 machines) +- Parses each TXT datasheet (Date, Model, SN) +- Decodes hex-prefix serial numbers for 8.3 filename encoding: + - Letter prefix = hex digit: A=10, B=11, C=12, ..., H=17, etc. + - Example: H8236-12.TXT has SN: 178236-12 inside the file + - Example: A819-1.TXT has SN: A819-1 inside -> decoded to 10819-1 + - The SN line inside H-prefix files already has the full numeric serial + - The SN line inside A-prefix files still has the encoded serial +- Cross-references against testdata.db by (serial_number, model_number) +- Inserts MISSING records as log_type='SHT' with test_station from folder name +- Copies ALL files to X:\For_Web\{decoded_serial}.TXT (the web share) + +## Machines with data + +TS-4L: 3,082 files (largest) +TS-4R: 2,741 files +TS-1R: 509 files +TS-8R: 478 files +TS-3R: 435 files +TS-11R: 325 files +TS-8L: 285 files +TS-11L: 248 files +TS-27: 10 files (already imported this session) +TS-1L: 1 file + +## Serial number encoding (8.3 filename scheme) + +The QuickBASIC ATE software encodes long serial numbers to fit DOS 8.3 filenames. +The first two digits get replaced with a hex letter if the serial is too long: + + 178236-12 -> H8236-12.TXT (17 -> H, which is char code 72, 72-55=17) + 10819-1 -> A819-1.TXT (10 -> A, which is char code 65, 65-55=10) + +Decode: letter.charCodeAt(0) - 55 = numeric prefix +Only applies if filename starts with [A-Z] followed by digits. + +## TS-27 already done + +10 files from TS-27 were already imported earlier this session into the DB as SHT records. +The import script uses INSERT OR REPLACE so re-running is safe. + +## Previous CTONWTXT.BAT issues (resolved) + +- v1.0: Never called, checked for Novell serve.sys, used X: drive parameter +- v2.0: Called from CTONW, but used mixed-case "Stage" path -> failed on DOS +- v2.1: All uppercase STAGE, but had MD commands that fail on existing dirs +- v2.2: Same issue +- v2.3: Removed MD entirely, dirs pre-created on NAS. CURRENT VERSION. diff --git a/scripts/Configure-TranscriptLogging.ps1 b/scripts/Configure-TranscriptLogging.ps1 new file mode 100644 index 0000000..a1751b2 --- /dev/null +++ b/scripts/Configure-TranscriptLogging.ps1 @@ -0,0 +1,88 @@ +<# +.SYNOPSIS + Configures PowerShell transcript logging for remote sessions. + +.DESCRIPTION + Enables comprehensive transcript logging via registry settings, + creates the logging directory with proper permissions, and sets up + automatic log rotation. + +.NOTES + Author: ClaudeTools Automation + Version: 1.0 + Run as Administrator +#> + +$ErrorActionPreference = 'Stop' +$transcriptPath = "C:\ClaudeTools\Logs\Transcripts" + +Write-Host "Configuring PowerShell Transcript Logging..." -ForegroundColor Cyan + +# Create transcript directory +if (-not (Test-Path $transcriptPath)) { + New-Item -ItemType Directory -Path $transcriptPath -Force | Out-Null + Write-Host "Created transcript directory: $transcriptPath" -ForegroundColor Green +} + +# Set permissions on transcript directory +# Administrators: Full Control, SYSTEM: Full Control, Remote Management Users: Read/Write +$acl = Get-Acl $transcriptPath +$acl.SetAccessRuleProtection($true, $false) # Disable inheritance + +# Add Administrators - Full Control +$adminRule = New-Object System.Security.AccessControl.FileSystemAccessRule( + "Administrators", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow" +) +$acl.AddAccessRule($adminRule) + +# Add SYSTEM - Full Control +$systemRule = New-Object System.Security.AccessControl.FileSystemAccessRule( + "SYSTEM", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow" +) +$acl.AddAccessRule($systemRule) + +# Add Remote Management Users - Modify (so they can write transcripts) +$rmRule = New-Object System.Security.AccessControl.FileSystemAccessRule( + "Remote Management Users", "Modify", "ContainerInherit,ObjectInherit", "None", "Allow" +) +$acl.AddAccessRule($rmRule) + +Set-Acl $transcriptPath $acl +Write-Host "Set permissions on transcript directory" -ForegroundColor Green + +# Configure PowerShell transcript logging via registry +$psPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" + +if (-not (Test-Path $psPath)) { + New-Item -Path $psPath -Force | Out-Null +} + +# Enable transcription +Set-ItemProperty -Path $psPath -Name "EnableTranscripting" -Value 1 -Type DWord +Set-ItemProperty -Path $psPath -Name "EnableInvocationHeader" -Value 1 -Type DWord +Set-ItemProperty -Path $psPath -Name "OutputDirectory" -Value $transcriptPath -Type String + +Write-Host "Enabled PowerShell transcription via registry" -ForegroundColor Green + +# Also enable module logging for additional audit trail +$modulePath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging" +if (-not (Test-Path $modulePath)) { + New-Item -Path $modulePath -Force | Out-Null +} +Set-ItemProperty -Path $modulePath -Name "EnableModuleLogging" -Value 1 -Type DWord + +# Enable script block logging +$scriptPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" +if (-not (Test-Path $scriptPath)) { + New-Item -Path $scriptPath -Force | Out-Null +} +Set-ItemProperty -Path $scriptPath -Name "EnableScriptBlockLogging" -Value 1 -Type DWord + +Write-Host "Enabled module and script block logging" -ForegroundColor Green + +Write-Host "`nTranscript logging configuration complete!" -ForegroundColor Green +Write-Host "Transcripts will be saved to: $transcriptPath" + +# Display current settings +Write-Host "`n--- Current Settings ---" -ForegroundColor Yellow +Get-ItemProperty -Path $psPath | Select-Object EnableTranscripting, EnableInvocationHeader, OutputDirectory diff --git a/scripts/Get-ADComputerReport.ps1 b/scripts/Get-ADComputerReport.ps1 new file mode 100644 index 0000000..a0c981e --- /dev/null +++ b/scripts/Get-ADComputerReport.ps1 @@ -0,0 +1,87 @@ +<# +.SYNOPSIS + Generates a report of all Active Directory computers. + +.DESCRIPTION + This script queries Active Directory for all computer accounts and exports + key properties including name, operating system, last logon, and OU location. + +.PARAMETER OutputPath + Optional. Path to export CSV report. If not specified, outputs to console. + +.PARAMETER OperatingSystem + Optional. Filter by operating system (e.g., "Windows Server*", "*Windows 10*"). + +.EXAMPLE + .\Get-ADComputerReport.ps1 + Lists all computers to console. + +.EXAMPLE + .\Get-ADComputerReport.ps1 -OperatingSystem "Windows Server*" -OutputPath "C:\ClaudeTools\Logs\servers.csv" + Exports all Windows Server computers to CSV. + +.NOTES + Author: ClaudeTools Automation + Version: 1.0 + Requires: ActiveDirectory PowerShell module +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory=$false)] + [string]$OutputPath, + + [Parameter(Mandatory=$false)] + [string]$OperatingSystem = "*" +) + +# Import AD module +Import-Module ActiveDirectory -ErrorAction Stop + +Write-Host "Querying Active Directory computers..." -ForegroundColor Cyan + +# Get computers with properties +$computers = Get-ADComputer -Filter "OperatingSystem -like '$OperatingSystem'" -Properties ` + OperatingSystem, + OperatingSystemVersion, + LastLogonDate, + Created, + Enabled, + IPv4Address, + Description, + DistinguishedName | + Select-Object ` + @{N='Name';E={$_.Name}}, + @{N='OperatingSystem';E={$_.OperatingSystem}}, + @{N='OSVersion';E={$_.OperatingSystemVersion}}, + @{N='Enabled';E={$_.Enabled}}, + @{N='IPv4Address';E={$_.IPv4Address}}, + @{N='LastLogon';E={$_.LastLogonDate}}, + @{N='Created';E={$_.Created}}, + @{N='OU';E={($_.DistinguishedName -split ',',2)[1]}}, + @{N='Description';E={$_.Description}} + +$computerCount = ($computers | Measure-Object).Count +Write-Host "Found $computerCount computers." -ForegroundColor Green + +if ($OutputPath) { + $computers | Export-Csv -Path $OutputPath -NoTypeInformation + Write-Host "Report exported to: $OutputPath" -ForegroundColor Green +} else { + $computers | Format-Table -AutoSize +} + +# Summary by OS +Write-Host "`n--- Operating System Summary ---" -ForegroundColor Yellow +$computers | Group-Object OperatingSystem | Sort-Object Count -Descending | + Format-Table @{N='Operating System';E={$_.Name}}, Count -AutoSize + +# Summary by status +$enabledCount = ($computers | Where-Object { $_.Enabled -eq $true } | Measure-Object).Count +$disabledCount = ($computers | Where-Object { $_.Enabled -eq $false } | Measure-Object).Count +Write-Host "Enabled: $enabledCount | Disabled: $disabledCount" + +# Stale computers (no logon in 90 days) +$staleDate = (Get-Date).AddDays(-90) +$staleCount = ($computers | Where-Object { $_.LastLogon -lt $staleDate -or $null -eq $_.LastLogon } | Measure-Object).Count +Write-Host "Stale (no logon 90+ days): $staleCount" -ForegroundColor $(if ($staleCount -gt 0) { 'Yellow' } else { 'Green' }) diff --git a/scripts/Get-ADUserReport.ps1 b/scripts/Get-ADUserReport.ps1 new file mode 100644 index 0000000..240ecff --- /dev/null +++ b/scripts/Get-ADUserReport.ps1 @@ -0,0 +1,92 @@ +<# +.SYNOPSIS + Generates a report of all Active Directory users with key properties. + +.DESCRIPTION + This script queries Active Directory for all user accounts and exports + key properties including name, email, last logon, account status, and group memberships. + +.PARAMETER OutputPath + Optional. Path to export CSV report. If not specified, outputs to console. + +.PARAMETER IncludeDisabled + Switch to include disabled accounts in the report. + +.EXAMPLE + .\Get-ADUserReport.ps1 + Lists all enabled users to console. + +.EXAMPLE + .\Get-ADUserReport.ps1 -OutputPath "C:\ClaudeTools\Logs\users.csv" -IncludeDisabled + Exports all users (including disabled) to CSV file. + +.NOTES + Author: ClaudeTools Automation + Version: 1.0 + Requires: ActiveDirectory PowerShell module +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory=$false)] + [string]$OutputPath, + + [Parameter(Mandatory=$false)] + [switch]$IncludeDisabled +) + +# Import AD module +Import-Module ActiveDirectory -ErrorAction Stop + +Write-Host "Querying Active Directory users..." -ForegroundColor Cyan + +# Build filter +$filter = if ($IncludeDisabled) { "*" } else { "Enabled -eq 'True'" } + +# Get users with properties +$users = Get-ADUser -Filter $filter -Properties ` + DisplayName, + EmailAddress, + Department, + Title, + Manager, + LastLogonDate, + PasswordLastSet, + PasswordNeverExpires, + Enabled, + Created, + MemberOf, + Description | + Select-Object ` + @{N='SamAccountName';E={$_.SamAccountName}}, + @{N='DisplayName';E={$_.DisplayName}}, + @{N='Email';E={$_.EmailAddress}}, + @{N='Department';E={$_.Department}}, + @{N='Title';E={$_.Title}}, + @{N='Enabled';E={$_.Enabled}}, + @{N='LastLogon';E={$_.LastLogonDate}}, + @{N='PasswordLastSet';E={$_.PasswordLastSet}}, + @{N='PasswordNeverExpires';E={$_.PasswordNeverExpires}}, + @{N='Created';E={$_.Created}}, + @{N='GroupCount';E={($_.MemberOf | Measure-Object).Count}}, + @{N='Description';E={$_.Description}} + +$userCount = ($users | Measure-Object).Count +Write-Host "Found $userCount users." -ForegroundColor Green + +if ($OutputPath) { + $users | Export-Csv -Path $OutputPath -NoTypeInformation + Write-Host "Report exported to: $OutputPath" -ForegroundColor Green +} else { + $users | Format-Table -AutoSize +} + +# Summary statistics +Write-Host "`n--- Summary ---" -ForegroundColor Yellow +Write-Host "Total Users: $userCount" +$enabledCount = ($users | Where-Object { $_.Enabled -eq $true } | Measure-Object).Count +$disabledCount = ($users | Where-Object { $_.Enabled -eq $false } | Measure-Object).Count +Write-Host "Enabled: $enabledCount" +Write-Host "Disabled: $disabledCount" +$neverExpire = ($users | Where-Object { $_.PasswordNeverExpires -eq $true } | Measure-Object).Count +Write-Host "Password Never Expires: $neverExpire" diff --git a/scripts/Get-GPOStatus.ps1 b/scripts/Get-GPOStatus.ps1 new file mode 100644 index 0000000..53aee68 --- /dev/null +++ b/scripts/Get-GPOStatus.ps1 @@ -0,0 +1,111 @@ +<# +.SYNOPSIS + Reports on Group Policy Object status and replication. + +.DESCRIPTION + This script checks all GPOs in the domain and reports their status, + including version information, links, and replication status between + AD and SYSVOL. + +.PARAMETER OutputPath + Optional. Path to export CSV report. If not specified, outputs to console. + +.PARAMETER CheckReplication + Switch to perform detailed replication check between AD and SYSVOL. + +.EXAMPLE + .\Get-GPOStatus.ps1 + Lists all GPOs with basic status. + +.EXAMPLE + .\Get-GPOStatus.ps1 -CheckReplication -OutputPath "C:\ClaudeTools\Logs\gpo-status.csv" + Full replication check with CSV export. + +.NOTES + Author: ClaudeTools Automation + Version: 1.0 + Requires: GroupPolicy PowerShell module +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory=$false)] + [string]$OutputPath, + + [Parameter(Mandatory=$false)] + [switch]$CheckReplication +) + +# Import required modules +Import-Module GroupPolicy -ErrorAction Stop +Import-Module ActiveDirectory -ErrorAction Stop + +Write-Host "Querying Group Policy Objects..." -ForegroundColor Cyan + +# Get all GPOs +$gpos = Get-GPO -All | Select-Object ` + @{N='Name';E={$_.DisplayName}}, + @{N='ID';E={$_.Id}}, + @{N='Status';E={$_.GpoStatus}}, + @{N='CreationTime';E={$_.CreationTime}}, + @{N='ModificationTime';E={$_.ModificationTime}}, + @{N='UserVersion';E={$_.User.DSVersion}}, + @{N='ComputerVersion';E={$_.Computer.DSVersion}}, + @{N='WMIFilter';E={$_.WmiFilter.Name}} + +$gpoCount = ($gpos | Measure-Object).Count +Write-Host "Found $gpoCount GPOs." -ForegroundColor Green + +# Check GPO links +Write-Host "`nChecking GPO links..." -ForegroundColor Cyan +$gpoLinks = @() +foreach ($gpo in (Get-GPO -All)) { + $report = [xml](Get-GPOReport -Guid $gpo.Id -ReportType Xml) + $links = $report.GPO.LinksTo.SOMPath + + $gpoLinks += [PSCustomObject]@{ + Name = $gpo.DisplayName + LinkCount = if ($links) { ($links | Measure-Object).Count } else { 0 } + Links = if ($links) { $links -join "; " } else { "Not Linked" } + } +} + +if ($CheckReplication) { + Write-Host "`nChecking SYSVOL replication status..." -ForegroundColor Cyan + + $domain = (Get-ADDomain).DNSRoot + $dcs = Get-ADDomainController -Filter * + + foreach ($dc in $dcs) { + Write-Host " Checking $($dc.HostName)..." -ForegroundColor Gray + $sysvolPath = "\\$($dc.HostName)\SYSVOL\$domain\Policies" + + if (Test-Path $sysvolPath) { + $sysvolGPOs = Get-ChildItem $sysvolPath -Directory | Where-Object { $_.Name -match '^{' } + Write-Host " SYSVOL GPO count: $($sysvolGPOs.Count)" -ForegroundColor Green + } else { + Write-Host " Unable to access SYSVOL" -ForegroundColor Red + } + } +} + +# Output results +if ($OutputPath) { + $gpos | Export-Csv -Path $OutputPath -NoTypeInformation + Write-Host "`nReport exported to: $OutputPath" -ForegroundColor Green +} else { + Write-Host "`n--- GPO List ---" -ForegroundColor Yellow + $gpos | Format-Table Name, Status, ModificationTime, UserVersion, ComputerVersion -AutoSize + + Write-Host "`n--- GPO Links ---" -ForegroundColor Yellow + $gpoLinks | Format-Table Name, LinkCount, Links -AutoSize +} + +# Summary +Write-Host "`n--- Summary ---" -ForegroundColor Yellow +Write-Host "Total GPOs: $gpoCount" +$unlinked = ($gpoLinks | Where-Object { $_.LinkCount -eq 0 } | Measure-Object).Count +Write-Host "Unlinked GPOs: $unlinked" -ForegroundColor $(if ($unlinked -gt 0) { 'Yellow' } else { 'Green' }) + +$disabled = ($gpos | Where-Object { $_.Status -ne 'AllSettingsEnabled' } | Measure-Object).Count +Write-Host "Disabled/Partial GPOs: $disabled" -ForegroundColor $(if ($disabled -gt 0) { 'Yellow' } else { 'Green' }) diff --git a/scripts/Get-ReplicationHealth.ps1 b/scripts/Get-ReplicationHealth.ps1 new file mode 100644 index 0000000..34374e2 --- /dev/null +++ b/scripts/Get-ReplicationHealth.ps1 @@ -0,0 +1,173 @@ +<# +.SYNOPSIS + Checks Active Directory replication health across domain controllers. + +.DESCRIPTION + This script performs comprehensive AD replication health checks including + replication status, partner connectivity, and identifies any replication failures. + +.PARAMETER OutputPath + Optional. Path to export results. If not specified, outputs to console. + +.PARAMETER Detailed + Switch to show detailed replication information per DC. + +.EXAMPLE + .\Get-ReplicationHealth.ps1 + Basic replication health check. + +.EXAMPLE + .\Get-ReplicationHealth.ps1 -Detailed -OutputPath "C:\ClaudeTools\Logs\repl-health.txt" + Detailed check with output to file. + +.NOTES + Author: ClaudeTools Automation + Version: 1.0 + Requires: ActiveDirectory PowerShell module, repadmin.exe +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory=$false)] + [string]$OutputPath, + + [Parameter(Mandatory=$false)] + [switch]$Detailed +) + +# Import AD module +Import-Module ActiveDirectory -ErrorAction Stop + +$output = @() +$output += "=" * 60 +$output += "AD REPLICATION HEALTH REPORT" +$output += "Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" +$output += "=" * 60 + +Write-Host "Checking AD Replication Health..." -ForegroundColor Cyan + +# Get all DCs +$dcs = Get-ADDomainController -Filter * +$output += "`nDomain Controllers Found: $($dcs.Count)" + +foreach ($dc in $dcs) { + $output += "`n--- $($dc.HostName) ---" + Write-Host "Checking $($dc.HostName)..." -ForegroundColor Gray +} + +# Check replication summary using repadmin +$output += "`n" + "=" * 60 +$output += "REPLICATION SUMMARY (repadmin /replsummary)" +$output += "=" * 60 + +try { + $replSummary = repadmin /replsummary 2>&1 + $output += $replSummary + Write-Host "Replication summary retrieved." -ForegroundColor Green +} catch { + $output += "ERROR: Unable to run repadmin /replsummary" + Write-Host "Error running repadmin" -ForegroundColor Red +} + +# Check for replication failures +$output += "`n" + "=" * 60 +$output += "REPLICATION FAILURES (repadmin /showrepl * /errorsonly)" +$output += "=" * 60 + +try { + $replErrors = repadmin /showrepl * /errorsonly 2>&1 + if ($replErrors -match "error" -or $replErrors -match "fail") { + $output += $replErrors + Write-Host "Replication ERRORS detected!" -ForegroundColor Red + } else { + $output += "No replication errors detected." + Write-Host "No replication errors." -ForegroundColor Green + } +} catch { + $output += "ERROR: Unable to check replication errors" +} + +# Queue length +$output += "`n" + "=" * 60 +$output += "REPLICATION QUEUE (repadmin /queue)" +$output += "=" * 60 + +try { + $replQueue = repadmin /queue 2>&1 + $output += $replQueue +} catch { + $output += "ERROR: Unable to check replication queue" +} + +if ($Detailed) { + $output += "`n" + "=" * 60 + $output += "DETAILED REPLICATION STATUS (repadmin /showrepl)" + $output += "=" * 60 + + try { + $replDetail = repadmin /showrepl 2>&1 + $output += $replDetail + } catch { + $output += "ERROR: Unable to get detailed replication status" + } + + # DFSR Health (if applicable) + $output += "`n" + "=" * 60 + $output += "DFSR SYSVOL REPLICATION STATUS" + $output += "=" * 60 + + try { + $dfsrStatus = Get-DfsrMember -ErrorAction SilentlyContinue + if ($dfsrStatus) { + $output += "DFSR Members:" + foreach ($member in $dfsrStatus) { + $output += " - $($member.ComputerName): $($member.DomainName)" + } + } else { + $output += "DFSR not configured or FRS in use." + } + } catch { + $output += "Unable to query DFSR status (may be using FRS)" + } +} + +# AD Database health +$output += "`n" + "=" * 60 +$output += "AD DATABASE INTEGRITY" +$output += "=" * 60 + +$adDb = Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters" -ErrorAction SilentlyContinue +if ($adDb) { + $dbPath = $adDb.'DSA Database file' + $logPath = $adDb.'Database log files path' + $output += "Database Path: $dbPath" + $output += "Log Path: $logPath" + + if (Test-Path $dbPath) { + $dbSize = (Get-Item $dbPath).Length / 1MB + $output += "Database Size: $([math]::Round($dbSize, 2)) MB" + } +} + +# Final summary +$output += "`n" + "=" * 60 +$output += "HEALTH CHECK COMPLETE" +$output += "=" * 60 + +# Output results +if ($OutputPath) { + $output | Out-File -FilePath $OutputPath -Encoding UTF8 + Write-Host "`nReport saved to: $OutputPath" -ForegroundColor Green +} else { + $output | ForEach-Object { Write-Host $_ } +} + +# Quick status summary +Write-Host "`n--- Quick Status ---" -ForegroundColor Yellow +Write-Host "Domain Controllers: $($dcs.Count)" +$errorMatch = $replErrors -match "error|fail" +if ($errorMatch) { + Write-Host "Replication Status: ERRORS DETECTED" -ForegroundColor Red +} else { + Write-Host "Replication Status: HEALTHY" -ForegroundColor Green +} diff --git a/scripts/Invoke-LogRotation.ps1 b/scripts/Invoke-LogRotation.ps1 new file mode 100644 index 0000000..2c0de88 --- /dev/null +++ b/scripts/Invoke-LogRotation.ps1 @@ -0,0 +1,107 @@ +<# +.SYNOPSIS + Rotates and cleans up old log files. + +.DESCRIPTION + Removes transcript and log files older than the specified retention period. + Designed to run as a scheduled task daily. + +.PARAMETER RetentionDays + Number of days to retain log files. Default is 30. + +.PARAMETER LogPath + Path to the logs directory. Default is C:\ClaudeTools\Logs. + +.PARAMETER WhatIf + Shows what would be deleted without actually deleting. + +.EXAMPLE + .\Invoke-LogRotation.ps1 + Removes logs older than 30 days. + +.EXAMPLE + .\Invoke-LogRotation.ps1 -RetentionDays 14 -WhatIf + Shows what would be deleted with 14-day retention. + +.NOTES + Author: ClaudeTools Automation + Version: 1.0 +#> + +[CmdletBinding(SupportsShouldProcess)] +param( + [Parameter(Mandatory=$false)] + [int]$RetentionDays = 30, + + [Parameter(Mandatory=$false)] + [string]$LogPath = "C:\ClaudeTools\Logs" +) + +$rotationLog = Join-Path $LogPath "rotation.log" +$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + +function Write-Log { + param([string]$Message) + $logEntry = "[$timestamp] $Message" + Add-Content -Path $rotationLog -Value $logEntry + Write-Host $logEntry +} + +Write-Log "=== Log Rotation Started ===" +Write-Log "Retention Period: $RetentionDays days" +Write-Log "Log Path: $LogPath" + +$cutoffDate = (Get-Date).AddDays(-$RetentionDays) +$totalDeleted = 0 +$totalSizeFreed = 0 + +# Find and delete old files +$oldFiles = Get-ChildItem -Path $LogPath -Recurse -File | + Where-Object { $_.LastWriteTime -lt $cutoffDate -and $_.Name -ne "rotation.log" } + +$fileCount = ($oldFiles | Measure-Object).Count +Write-Log "Found $fileCount files older than $RetentionDays days" + +foreach ($file in $oldFiles) { + $fileSize = $file.Length + $filePath = $file.FullName + + if ($PSCmdlet.ShouldProcess($filePath, "Delete")) { + try { + Remove-Item $filePath -Force + $totalDeleted++ + $totalSizeFreed += $fileSize + Write-Log "Deleted: $filePath ($([math]::Round($fileSize/1KB, 2)) KB)" + } catch { + Write-Log "ERROR deleting $filePath : $_" + } + } else { + Write-Log "WhatIf: Would delete $filePath ($([math]::Round($fileSize/1KB, 2)) KB)" + } +} + +# Delete empty subdirectories +$emptyDirs = Get-ChildItem -Path $LogPath -Directory -Recurse | + Where-Object { (Get-ChildItem $_.FullName -Force).Count -eq 0 } + +foreach ($dir in $emptyDirs) { + if ($PSCmdlet.ShouldProcess($dir.FullName, "Remove empty directory")) { + try { + Remove-Item $dir.FullName -Force + Write-Log "Removed empty directory: $($dir.FullName)" + } catch { + Write-Log "ERROR removing directory $($dir.FullName) : $_" + } + } +} + +# Summary +$sizeMB = [math]::Round($totalSizeFreed / 1MB, 2) +Write-Log "=== Rotation Complete ===" +Write-Log "Files Deleted: $totalDeleted" +Write-Log "Space Freed: $sizeMB MB" + +# Show current disk usage +$currentSize = (Get-ChildItem -Path $LogPath -Recurse -File | Measure-Object -Property Length -Sum).Sum +$currentSizeMB = [math]::Round($currentSize / 1MB, 2) +Write-Log "Current Log Directory Size: $currentSizeMB MB"