Compliance/Export-ComplianceEvidence.ps1
|
function Export-ComplianceEvidence { <# .SYNOPSIS Collects and packages compliance audit evidence .DESCRIPTION Gathers configuration files, logs, screenshots, and system information for compliance audit purposes. Creates timestamped ZIP package with chain of custody documentation and integrity hashes. .PARAMETER OutputPath Path where evidence package will be created .PARAMETER EvidenceTypes Types of evidence to collect: Configuration, Logs, Screenshots, SystemInfo, All .PARAMETER IncludeEventLogs Include Windows Event Log exports .PARAMETER DaysBack Number of days back to collect event logs (default: 7) .PARAMETER AuditorName Name of person collecting evidence .PARAMETER CaseReference Case or audit reference number .EXAMPLE Export-ComplianceEvidence -OutputPath "C:\Audits" -AuditorName "John Doe" -CaseReference "AUD-2025-001" .OUTPUTS String path to created evidence package #> [CmdletBinding()] param( [Parameter(Mandatory=$true)] [string]$OutputPath, [Parameter()] [ValidateSet('All', 'Configuration', 'Logs', 'Screenshots', 'SystemInfo')] [string[]]$EvidenceTypes = @('All'), [Parameter()] [switch]$IncludeEventLogs, [Parameter()] [int]$DaysBack = 7, [Parameter()] [string]$AuditorName = $env:USERNAME, [Parameter()] [string]$CaseReference = "AUDIT-$(Get-Date -Format 'yyyyMMdd')" ) begin { Write-Host "=== COMPLIANCE EVIDENCE COLLECTION ===" -ForegroundColor Cyan Write-Host "Start Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Yellow Write-Host "Auditor: $AuditorName" -ForegroundColor Yellow Write-Host "Case Reference: $CaseReference" -ForegroundColor Yellow if ($EvidenceTypes -contains 'All') { $EvidenceTypes = @('Configuration', 'Logs', 'Screenshots', 'SystemInfo') } # Create timestamp and working directory $timestamp = Get-Date -Format "yyyyMMdd_HHmmss" $evidenceName = "ComplianceEvidence_${CaseReference}_$timestamp" $workingDir = Join-Path $OutputPath $evidenceName # Create directory structure $directories = @{ Root = $workingDir Configuration = Join-Path $workingDir "01_Configuration" Logs = Join-Path $workingDir "02_Logs" Screenshots = Join-Path $workingDir "03_Screenshots" SystemInfo = Join-Path $workingDir "04_SystemInfo" Metadata = Join-Path $workingDir "00_Metadata" } foreach ($dir in $directories.Values) { New-Item -Path $dir -ItemType Directory -Force | Out-Null } # Initialize evidence manifest $manifest = [PSCustomObject]@{ CollectionDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" ComputerName = $env:COMPUTERNAME Domain = $env:USERDOMAIN Auditor = $AuditorName CaseReference = $CaseReference EvidenceTypes = $EvidenceTypes CollectedFiles = @() Hashes = @() Notes = @() } } process { try { # 1. CONFIGURATION EVIDENCE if ($EvidenceTypes -contains 'Configuration') { Write-Host "`n[1/4] Collecting Configuration Evidence..." -ForegroundColor Cyan try { # Security Policy Export Write-Host " Exporting security policy..." -ForegroundColor Gray $secpolPath = Join-Path $directories.Configuration "SecurityPolicy.inf" $null = secedit /export /cfg $secpolPath /quiet Start-Sleep -Milliseconds 500 if (Test-Path $secpolPath) { $manifest.CollectedFiles += "01_Configuration\SecurityPolicy.inf" $hash = (Get-FileHash $secpolPath -Algorithm SHA256).Hash $manifest.Hashes += [PSCustomObject]@{ File = "SecurityPolicy.inf" SHA256 = $hash } } # Audit Policy Export Write-Host " Exporting audit policies..." -ForegroundColor Gray $auditPath = Join-Path $directories.Configuration "AuditPolicy.csv" auditpol /backup /file:$auditPath | Out-Null if (Test-Path $auditPath) { $manifest.CollectedFiles += "01_Configuration\AuditPolicy.csv" $hash = (Get-FileHash $auditPath -Algorithm SHA256).Hash $manifest.Hashes += [PSCustomObject]@{ File = "AuditPolicy.csv" SHA256 = $hash } } # Firewall Rules Export Write-Host " Exporting firewall rules..." -ForegroundColor Gray $fwPath = Join-Path $directories.Configuration "FirewallRules.csv" Get-NetFirewallRule | Select-Object Name, DisplayName, Enabled, Direction, Action, Profile | Export-Csv $fwPath -NoTypeInformation $manifest.CollectedFiles += "01_Configuration\FirewallRules.csv" $hash = (Get-FileHash $fwPath -Algorithm SHA256).Hash $manifest.Hashes += [PSCustomObject]@{ File = "FirewallRules.csv" SHA256 = $hash } # Registry Exports (Key Security Settings) Write-Host " Exporting registry settings..." -ForegroundColor Gray $regExports = @{ 'PasswordPolicy' = 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' 'AuditSettings' = 'HKLM\SYSTEM\CurrentControlSet\Control\Lsa' 'WindowsDefender' = 'HKLM\SOFTWARE\Microsoft\Windows Defender' } foreach ($regName in $regExports.Keys) { $regPath = Join-Path $directories.Configuration "Registry_$regName.reg" $regKey = $regExports[$regName] try { $null = reg export $regKey $regPath /y 2>$null if (Test-Path $regPath) { $manifest.CollectedFiles += "01_Configuration\Registry_$regName.reg" } } catch { Write-Warning "Could not export registry key: $regKey" } } # Group Policy Results - FIXED: Use uppercase /H /F Write-Host " Generating Group Policy report..." -ForegroundColor Gray $gpPath = Join-Path $directories.Configuration "GroupPolicy.html" try { gpresult /H $gpPath /F | Out-Null if (Test-Path $gpPath) { $manifest.CollectedFiles += "01_Configuration\GroupPolicy.html" } } catch { Write-Warning "Could not generate Group Policy report" } Write-Host " Configuration evidence collected" -ForegroundColor Green } catch { Write-Warning "Error collecting configuration evidence: $_" $manifest.Notes += "Configuration collection had errors: $_" } } # 2. LOG EVIDENCE if ($EvidenceTypes -contains 'Logs') { Write-Host "`n[2/4] Collecting Log Evidence..." -ForegroundColor Cyan try { # Export Event Logs if requested if ($IncludeEventLogs) { Write-Host " Exporting event logs (last $DaysBack days)..." -ForegroundColor Gray $logNames = @('Security', 'System', 'Application') $startDate = (Get-Date).AddDays(-$DaysBack) foreach ($logName in $logNames) { try { $logPath = Join-Path $directories.Logs "EventLog_$logName.evtx" $query = "*[System[TimeCreated[@SystemTime>='$($startDate.ToUniversalTime().ToString('o'))']]]" wevtutil epl $logName $logPath "/q:$query" 2>$null if (Test-Path $logPath) { $manifest.CollectedFiles += "02_Logs\EventLog_$logName.evtx" Write-Host " Exported $logName log" -ForegroundColor Gray } } catch { Write-Warning "Could not export $logName log" } } } # Recent Failed Logon Attempts Write-Host " Collecting failed logon attempts..." -ForegroundColor Gray $failedLogons = Get-WinEvent -FilterHashtable @{ LogName = 'Security' ID = 4625 StartTime = (Get-Date).AddDays(-$DaysBack) } -ErrorAction SilentlyContinue | Select-Object -First 100 if ($failedLogons) { $logonPath = Join-Path $directories.Logs "FailedLogons.csv" $failedLogons | Select-Object TimeCreated, Id, Message | Export-Csv $logonPath -NoTypeInformation $manifest.CollectedFiles += "02_Logs\FailedLogons.csv" } # PowerShell Script Block Logs Write-Host " Collecting PowerShell logs..." -ForegroundColor Gray $psLogs = Get-WinEvent -FilterHashtable @{ LogName = 'Microsoft-Windows-PowerShell/Operational' StartTime = (Get-Date).AddDays(-$DaysBack) } -ErrorAction SilentlyContinue | Select-Object -First 100 if ($psLogs) { $psPath = Join-Path $directories.Logs "PowerShellLogs.csv" $psLogs | Select-Object TimeCreated, Id, LevelDisplayName, Message | Export-Csv $psPath -NoTypeInformation $manifest.CollectedFiles += "02_Logs\PowerShellLogs.csv" } Write-Host " Log evidence collected" -ForegroundColor Green } catch { Write-Warning "Error collecting log evidence: $_" $manifest.Notes += "Log collection had errors: $_" } } # 3. SCREENSHOTS if ($EvidenceTypes -contains 'Screenshots') { Write-Host "`n[3/4] Capturing Screenshots..." -ForegroundColor Cyan try { # Windows Defender Status Screenshot Write-Host " Capturing Windows Defender status..." -ForegroundColor Gray $defenderStatus = Get-MpComputerStatus -ErrorAction SilentlyContinue if ($defenderStatus) { $defPath = Join-Path $directories.Screenshots "WindowsDefender_Status.txt" $defenderStatus | Format-List | Out-File $defPath $manifest.CollectedFiles += "03_Screenshots\WindowsDefender_Status.txt" } # Firewall Status Write-Host " Capturing firewall status..." -ForegroundColor Gray $fwStatus = Get-NetFirewallProfile | Select-Object Name, Enabled, DefaultInboundAction, DefaultOutboundAction $fwStatusPath = Join-Path $directories.Screenshots "Firewall_Status.txt" $fwStatus | Format-Table -AutoSize | Out-File $fwStatusPath $manifest.CollectedFiles += "03_Screenshots\Firewall_Status.txt" # BitLocker Status Write-Host " Capturing BitLocker status..." -ForegroundColor Gray $blStatus = Get-BitLockerVolume -ErrorAction SilentlyContinue if ($blStatus) { $blPath = Join-Path $directories.Screenshots "BitLocker_Status.txt" $blStatus | Format-List | Out-File $blPath $manifest.CollectedFiles += "03_Screenshots\BitLocker_Status.txt" } Write-Host " Screenshots captured" -ForegroundColor Green } catch { Write-Warning "Error capturing screenshots: $_" $manifest.Notes += "Screenshot capture had errors: $_" } } # 4. SYSTEM INFORMATION if ($EvidenceTypes -contains 'SystemInfo') { Write-Host "`n[4/4] Collecting System Information..." -ForegroundColor Cyan try { # System Information Report Write-Host " Generating system information report..." -ForegroundColor Gray $sysInfoPath = Join-Path $directories.SystemInfo "SystemInfo.txt" systeminfo | Out-File $sysInfoPath $manifest.CollectedFiles += "04_SystemInfo\SystemInfo.txt" # Installed Software Write-Host " Collecting installed software list..." -ForegroundColor Gray $softwarePath = Join-Path $directories.SystemInfo "InstalledSoftware.csv" Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, DisplayVersion, Publisher, InstallDate | Where-Object { $_.DisplayName } | Export-Csv $softwarePath -NoTypeInformation $manifest.CollectedFiles += "04_SystemInfo\InstalledSoftware.csv" # Local Users Write-Host " Collecting local users information..." -ForegroundColor Gray $usersPath = Join-Path $directories.SystemInfo "LocalUsers.csv" Get-LocalUser | Select-Object Name, Enabled, LastLogon, PasswordLastSet, PasswordExpires | Export-Csv $usersPath -NoTypeInformation $manifest.CollectedFiles += "04_SystemInfo\LocalUsers.csv" # Local Groups - FIXED: Proper error handling with foreach Write-Host " Collecting local groups information..." -ForegroundColor Gray $groupsPath = Join-Path $directories.SystemInfo "LocalGroups.csv" $groupData = @() $localGroups = @(Get-LocalGroup -ErrorAction SilentlyContinue) foreach ($group in $localGroups) { if ($null -eq $group) { continue } try { $members = @(Get-LocalGroupMember -Group $group.Name -ErrorAction SilentlyContinue) $groupData += [PSCustomObject]@{ GroupName = $group.Name Members = if ($members.Count -gt 0) { ($members.Name -join '; ') } else { 'No members' } } } catch { $groupData += [PSCustomObject]@{ GroupName = $group.Name Members = 'Error retrieving members' } } } if ($groupData.Count -gt 0) { $groupData | Export-Csv $groupsPath -NoTypeInformation $manifest.CollectedFiles += "04_SystemInfo\LocalGroups.csv" } # Network Configuration Write-Host " Collecting network configuration..." -ForegroundColor Gray $netPath = Join-Path $directories.SystemInfo "NetworkConfig.txt" ipconfig /all | Out-File $netPath $manifest.CollectedFiles += "04_SystemInfo\NetworkConfig.txt" # Services Write-Host " Collecting services list..." -ForegroundColor Gray $servicesPath = Join-Path $directories.SystemInfo "Services.csv" Get-Service | Select-Object Name, DisplayName, Status, StartType | Export-Csv $servicesPath -NoTypeInformation $manifest.CollectedFiles += "04_SystemInfo\Services.csv" # Windows Updates Write-Host " Collecting Windows Update history..." -ForegroundColor Gray try { $updatesPath = Join-Path $directories.SystemInfo "WindowsUpdates.csv" $session = New-Object -ComObject Microsoft.Update.Session $searcher = $session.CreateUpdateSearcher() $historyCount = $searcher.GetTotalHistoryCount() if ($historyCount -gt 0) { $history = $searcher.QueryHistory(0, [Math]::Min($historyCount, 100)) $history | Select-Object Title, Date, @{N='Result';E={ switch($_.ResultCode) { 0 { 'NotStarted' } 1 { 'InProgress' } 2 { 'Succeeded' } 3 { 'SucceededWithErrors' } 4 { 'Failed' } 5 { 'Aborted' } } }} | Export-Csv $updatesPath -NoTypeInformation $manifest.CollectedFiles += "04_SystemInfo\WindowsUpdates.csv" } } catch { Write-Warning "Could not collect Windows Update history" } Write-Host " System information collected" -ForegroundColor Green } catch { Write-Warning "Error collecting system information: $_" $manifest.Notes += "System information collection had errors: $_" } } # 5. GENERATE CHAIN OF CUSTODY & MANIFEST Write-Host "`n[*] Generating chain of custody documentation..." -ForegroundColor Cyan # Save manifest $manifestPath = Join-Path $directories.Metadata "Evidence_Manifest.json" $manifest | ConvertTo-Json -Depth 10 | Out-File $manifestPath -Encoding UTF8 # Generate Chain of Custody $custodyPath = Join-Path $directories.Metadata "Chain_of_Custody.txt" $ipInfo = Get-NetIPAddress -AddressFamily IPv4 -ErrorAction SilentlyContinue | Where-Object {$null -ne $_ -and $_.InterfaceAlias -notlike '*Loopback*'} | Select-Object -First 1 $ipAddress = if ($ipInfo) { $ipInfo.IPAddress } else { 'N/A' } $osCaptionCustody = $null try { $osCaptionCustody = (Get-CimInstance Win32_OperatingSystem -ErrorAction Stop).Caption } catch { $osCaptionCustody = $null } if ($null -eq $osCaptionCustody) { $osCaptionCustody = 'Unknown' } $custody = @" CHAIN OF CUSTODY DOCUMENTATION ================================ Case Reference: $CaseReference Collection Date: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') Time Zone: $([System.TimeZoneInfo]::Local.DisplayName) SYSTEM INFORMATION: Computer Name: $($env:COMPUTERNAME) Domain: $($env:USERDOMAIN) IP Address: $ipAddress Operating System: $osCaptionCustody EVIDENCE COLLECTOR: Name: $AuditorName Account: $($env:USERDOMAIN)\$($env:USERNAME) Collection Method: WindowsSecurityAudit PowerShell Module EVIDENCE COLLECTED: Total Files: $($manifest.CollectedFiles.Count) Evidence Types: $($EvidenceTypes -join ', ') FILE INTEGRITY HASHES: $($manifest.Hashes | ForEach-Object { " $($_.File): $($_.SHA256)" } | Out-String) NOTES: $($manifest.Notes -join "`n") CERTIFICATION: I certify that this evidence was collected using automated tools in a forensically sound manner. All timestamps are accurate, and file integrity hashes have been recorded for verification purposes. Collector Signature: ___________________________ Date: __________ Reviewer Signature: ____________________________ Date: __________ "@ $custody | Out-File $custodyPath -Encoding UTF8 # Generate README $readmePath = Join-Path $directories.Root "README.txt" $readme = @" COMPLIANCE EVIDENCE PACKAGE =========================== Case Reference: $CaseReference Collection Date: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') Computer: $($env:COMPUTERNAME) DIRECTORY STRUCTURE: 00_Metadata/ - Chain of custody and evidence manifest 01_Configuration/ - System configuration exports 02_Logs/ - Event logs and security logs 03_Screenshots/ - Status captures and documentation 04_SystemInfo/ - System inventory and information INTEGRITY VERIFICATION: All collected files have been hashed with SHA-256. See 00_Metadata/Evidence_Manifest.json for hash values. IMPORTANT NOTES: - This evidence package should be stored securely - Maintain chain of custody at all times - Do not modify files after collection - Verify file hashes before using as audit evidence For questions contact: $AuditorName "@ $readme | Out-File $readmePath -Encoding UTF8 # 6. CREATE ZIP PACKAGE Write-Host "[*] Creating evidence package..." -ForegroundColor Cyan $zipPath = "$workingDir.zip" if (Test-Path $zipPath) { Remove-Item $zipPath -Force } Add-Type -AssemblyName System.IO.Compression.FileSystem [System.IO.Compression.ZipFile]::CreateFromDirectory($workingDir, $zipPath, 'Optimal', $false) # Generate ZIP hash $zipHash = (Get-FileHash $zipPath -Algorithm SHA256).Hash # Clean up working directory Remove-Item $workingDir -Recurse -Force Write-Host "`nEvidence package created successfully!" -ForegroundColor Green Write-Host "Package: $zipPath" -ForegroundColor White Write-Host "SHA-256: $zipHash" -ForegroundColor Gray Write-Host "Size: $([math]::Round((Get-Item $zipPath).Length / 1MB, 2)) MB" -ForegroundColor Gray } catch { Write-Error "Error during evidence collection: $_" # Clean up on error if (Test-Path $workingDir) { Remove-Item $workingDir -Recurse -Force -ErrorAction SilentlyContinue } throw } } end { Write-Host "`n=== EVIDENCE COLLECTION COMPLETE ===" -ForegroundColor Cyan Write-Host "Package Location: $zipPath" -ForegroundColor Green Write-Host "Files Collected: $($manifest.CollectedFiles.Count)" -ForegroundColor White Write-Host "Package Hash: $zipHash" -ForegroundColor Gray return [PSCustomObject]@{ PackagePath = $zipPath PackageHash = $zipHash FilesCollected = $manifest.CollectedFiles.Count CollectionDate = $manifest.CollectionDate CaseReference = $CaseReference Auditor = $AuditorName } } } |