Enterprise/Get-MultiSystemAudit.ps1

function Get-MultiSystemAudit {
    <#
    .SYNOPSIS
        Audits multiple systems and generates comparative analysis
    .DESCRIPTION
        Performs security audits on multiple computers and creates comparative reports
        showing security posture across the enterprise
    .PARAMETER ComputerName
        Array of computer names to audit
    .PARAMETER Credential
        Credentials for remote access
    .PARAMETER OutputPath
        Path to save audit results
    .PARAMETER GenerateReport
        Generate HTML comparison report
    .EXAMPLE
        Get-MultiSystemAudit -ComputerName "SERVER01","SERVER02","WS001" -GenerateReport
        Get-MultiSystemAudit -ComputerName (Get-Content servers.txt) -Credential $cred -OutputPath "C:\Audits"
    .OUTPUTS
        PSCustomObject with multi-system audit results
    #>

    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string[]]$ComputerName,
        
        [Parameter()]
        [PSCredential]$Credential,
        
        [Parameter()]
        [string]$OutputPath = "C:\MultiSystemAudit_$(Get-Date -Format 'yyyyMMdd_HHmmss')",
        
        [Parameter()]
        [switch]$GenerateReport
    )
    
    begin {
        Write-Host "=== MULTI-SYSTEM SECURITY AUDIT ===" -ForegroundColor Cyan
        Write-Host "Target Systems: $($ComputerName.Count)" -ForegroundColor Yellow
        Write-Host "Start Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Yellow
        
        if (-not (Test-Path $OutputPath)) {
            New-Item -Path $OutputPath -ItemType Directory -Force | Out-Null
        }
        
        $auditResults = [PSCustomObject]@{
            AuditDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
            TotalSystems = $ComputerName.Count
            SystemAudits = @()
            ComparativeAnalysis = @{
                AverageSecurityScore = 0
                HighestScore = 0
                LowestScore = 100
                MostSecureSystem = ""
                LeastSecureSystem = ""
                CommonFindings = @()
                CriticalSystems = @()
            }
            Summary = @{
                TotalFindings = 0
                CriticalFindings = 0
                HighFindings = 0
                MediumFindings = 0
                LowFindings = 0
                SystemsAudited = 0
                SystemsFailed = 0
            }
        }
    }
    
    process {
        try {
            Write-Host "`nAuditing systems..." -ForegroundColor Cyan
            $progress = 0
            
            foreach ($computer in $ComputerName) {
                $progress++
                Write-Progress -Activity "Multi-System Audit" -Status "Auditing: $computer" -PercentComplete (($progress / $ComputerName.Count) * 100)
                
                Write-Host "`n[$progress/$($ComputerName.Count)] Auditing: $computer" -ForegroundColor Yellow
                
                $systemAudit = [PSCustomObject]@{
                    ComputerName = $computer
                    Status = 'Success'
                    SecurityScore = 0
                    SecurityLevel = 'Unknown'
                    Findings = @()
                    Details = @{}
                    AuditDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
                }
                
                try {
                    # Test connectivity
                    $pingTest = Test-Connection -ComputerName $computer -Count 1 -Quiet -ErrorAction SilentlyContinue
                    
                    if (-not $pingTest) {
                        $systemAudit.Status = 'Unreachable'
                        $systemAudit.Details.Error = 'System is unreachable'
                        $auditResults.Summary.SystemsFailed++
                        $auditResults.SystemAudits += $systemAudit
                        continue
                    }
                    
                    # Collect basic system information
                    $sysInfo = if ($Credential) {
                        Invoke-Command -ComputerName $computer -Credential $Credential -ScriptBlock {
                            [PSCustomObject]@{
                                OSVersion = (Get-CimInstance Win32_OperatingSystem).Caption
                                LastBoot = (Get-CimInstance Win32_OperatingSystem).LastBootUpTime
                                TotalMemory = [math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 2)
                                DiskSpace = (Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | 
                                    Select-Object DeviceID, @{N='FreeGB';E={[math]::Round($_.FreeSpace/1GB,2)}}, 
                                    @{N='TotalGB';E={[math]::Round($_.Size/1GB,2)}})
                            }
                        } -ErrorAction Stop
                    } else {
                        Invoke-Command -ComputerName $computer -ScriptBlock {
                            [PSCustomObject]@{
                                OSVersion = (Get-CimInstance Win32_OperatingSystem).Caption
                                LastBoot = (Get-CimInstance Win32_OperatingSystem).LastBootUpTime
                                TotalMemory = [math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 2)
                                DiskSpace = (Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | 
                                    Select-Object DeviceID, @{N='FreeGB';E={[math]::Round($_.FreeSpace/1GB,2)}}, 
                                    @{N='TotalGB';E={[math]::Round($_.Size/1GB,2)}})
                            }
                        } -ErrorAction Stop
                    }
                    
                    $systemAudit.Details = $sysInfo
                    
                    # Security checks
                    $securityChecks = if ($Credential) {
                        Invoke-Command -ComputerName $computer -Credential $Credential -ScriptBlock {
                            $score = 0
                            $findings = @()
                            
                            # Windows Defender
                            $defender = Get-MpComputerStatus -ErrorAction SilentlyContinue
                            if ($defender -and $defender.RealTimeProtectionEnabled) {
                                $score += 25
                            } else {
                                $findings += [PSCustomObject]@{
                                    Severity = 'Critical'
                                    Finding = 'Windows Defender disabled'
                                }
                            }
                            
                            # Firewall
                            $firewall = Get-NetFirewallProfile -ErrorAction SilentlyContinue
                            $enabledProfiles = @($firewall | Where-Object { $_.Enabled }).Count
                            if ($enabledProfiles -eq 3) {
                                $score += 25
                            } else {
                                $findings += [PSCustomObject]@{
                                    Severity = 'High'
                                    Finding = "Only $enabledProfiles firewall profiles enabled"
                                }
                            }
                            
                            # UAC
                            $uac = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -ErrorAction SilentlyContinue).EnableLUA
                            if ($uac -eq 1) {
                                $score += 15
                            } else {
                                $findings += [PSCustomObject]@{
                                    Severity = 'High'
                                    Finding = 'UAC disabled'
                                }
                            }
                            
                            # Updates
                            try {
                                $updateSession = New-Object -ComObject Microsoft.Update.Session
                                $updateSearcher = $updateSession.CreateUpdateSearcher()
                                $pendingUpdates = $updateSearcher.Search("IsInstalled=0").Updates.Count
                                
                                if ($pendingUpdates -eq 0) {
                                    $score += 20
                                } else {
                                    $findings += [PSCustomObject]@{
                                        Severity = 'Medium'
                                        Finding = "$pendingUpdates pending updates"
                                    }
                                }
                            } catch {
                                $findings += [PSCustomObject]@{
                                    Severity = 'Low'
                                    Finding = 'Cannot check updates'
                                }
                            }
                            
                            # Admin accounts
                            $admins = @()
                            $lookupOk = $false
                            try {
                                $admins = @(Get-LocalGroupMember -SID ([System.Security.Principal.SecurityIdentifier]'S-1-5-32-544') -ErrorAction Stop)
                                $lookupOk = $true
                            } catch {
                                try {
                                    $adminGroupName = ([System.Security.Principal.SecurityIdentifier]'S-1-5-32-544').Translate([System.Security.Principal.NTAccount]).Value.Split('\')[-1]
                                    $group = ([ADSI]"WinNT://./$adminGroupName,group")
                                    $admins = @($group.psbase.Invoke('Members'))
                                    $lookupOk = $true
                                } catch { $lookupOk = $false }
                            }
                            if (-not $lookupOk) {
                                $findings += [PSCustomObject]@{
                                    Severity = 'Low'
                                    Finding = 'Unable to determine administrator count'
                                }
                            } elseif ($admins.Count -le 2) {
                                $score += 15
                            } else {
                                $findings += [PSCustomObject]@{
                                    Severity = 'Medium'
                                    Finding = "$($admins.Count) administrator accounts"
                                }
                            }
                            
                            [PSCustomObject]@{
                                Score = $score
                                Findings = $findings
                            }
                        } -ErrorAction Stop
                    } else {
                        Invoke-Command -ComputerName $computer -ScriptBlock {
                            $score = 0
                            $findings = @()
                            
                            # Windows Defender
                            $defender = Get-MpComputerStatus -ErrorAction SilentlyContinue
                            if ($defender -and $defender.RealTimeProtectionEnabled) {
                                $score += 25
                            } else {
                                $findings += [PSCustomObject]@{
                                    Severity = 'Critical'
                                    Finding = 'Windows Defender disabled'
                                }
                            }
                            
                            # Firewall
                            $firewall = Get-NetFirewallProfile -ErrorAction SilentlyContinue
                            $enabledProfiles = @($firewall | Where-Object { $_.Enabled }).Count
                            if ($enabledProfiles -eq 3) {
                                $score += 25
                            } else {
                                $findings += [PSCustomObject]@{
                                    Severity = 'High'
                                    Finding = "Only $enabledProfiles firewall profiles enabled"
                                }
                            }
                            
                            # UAC
                            $uac = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -ErrorAction SilentlyContinue).EnableLUA
                            if ($uac -eq 1) {
                                $score += 15
                            } else {
                                $findings += [PSCustomObject]@{
                                    Severity = 'High'
                                    Finding = 'UAC disabled'
                                }
                            }
                            
                            # Updates
                            try {
                                $updateSession = New-Object -ComObject Microsoft.Update.Session
                                $updateSearcher = $updateSession.CreateUpdateSearcher()
                                $pendingUpdates = $updateSearcher.Search("IsInstalled=0").Updates.Count
                                
                                if ($pendingUpdates -eq 0) {
                                    $score += 20
                                } else {
                                    $findings += [PSCustomObject]@{
                                        Severity = 'Medium'
                                        Finding = "$pendingUpdates pending updates"
                                    }
                                }
                            } catch {
                                $findings += [PSCustomObject]@{
                                    Severity = 'Low'
                                    Finding = 'Cannot check updates'
                                }
                            }
                            
                            # Admin accounts
                            $admins = @()
                            $lookupOk = $false
                            try {
                                $admins = @(Get-LocalGroupMember -SID ([System.Security.Principal.SecurityIdentifier]'S-1-5-32-544') -ErrorAction Stop)
                                $lookupOk = $true
                            } catch {
                                try {
                                    $adminGroupName = ([System.Security.Principal.SecurityIdentifier]'S-1-5-32-544').Translate([System.Security.Principal.NTAccount]).Value.Split('\')[-1]
                                    $group = ([ADSI]"WinNT://./$adminGroupName,group")
                                    $admins = @($group.psbase.Invoke('Members'))
                                    $lookupOk = $true
                                } catch { $lookupOk = $false }
                            }
                            if (-not $lookupOk) {
                                $findings += [PSCustomObject]@{
                                    Severity = 'Low'
                                    Finding = 'Unable to determine administrator count'
                                }
                            } elseif ($admins.Count -le 2) {
                                $score += 15
                            } else {
                                $findings += [PSCustomObject]@{
                                    Severity = 'Medium'
                                    Finding = "$($admins.Count) administrator accounts"
                                }
                            }
                            
                            [PSCustomObject]@{
                                Score = $score
                                Findings = $findings
                            }
                        } -ErrorAction Stop
                    }
                    
                    $systemAudit.SecurityScore = $securityChecks.Score
                    $systemAudit.Findings = $securityChecks.Findings
                    
                    # Determine security level
                    if ($systemAudit.SecurityScore -ge 80) {
                        $systemAudit.SecurityLevel = 'High'
                    } elseif ($systemAudit.SecurityScore -ge 60) {
                        $systemAudit.SecurityLevel = 'Medium'
                    } elseif ($systemAudit.SecurityScore -ge 40) {
                        $systemAudit.SecurityLevel = 'Low'
                    } else {
                        $systemAudit.SecurityLevel = 'Critical'
                        $auditResults.ComparativeAnalysis.CriticalSystems += $computer
                    }
                    
                    $auditResults.Summary.SystemsAudited++
                    $auditResults.Summary.TotalFindings += $systemAudit.Findings.Count
                    
                    # Count findings by severity
                    foreach ($finding in $systemAudit.Findings) {
                        switch ($finding.Severity) {
                            'Critical' { $auditResults.Summary.CriticalFindings++ }
                            'High' { $auditResults.Summary.HighFindings++ }
                            'Medium' { $auditResults.Summary.MediumFindings++ }
                            'Low' { $auditResults.Summary.LowFindings++ }
                        }
                    }
                    
                    Write-Host " Security Score: $($systemAudit.SecurityScore)/100" -ForegroundColor $(
                        if ($systemAudit.SecurityScore -ge 80) { 'Green' }
                        elseif ($systemAudit.SecurityScore -ge 60) { 'Yellow' }
                        else { 'Red' }
                    )
                    
                }
                catch {
                    $systemAudit.Status = 'Failed'
                    $systemAudit.Details = @{ Error = $_.Exception.Message }
                    $auditResults.Summary.SystemsFailed++
                    Write-Warning " Failed to audit $computer : $_"
                }
                
                $auditResults.SystemAudits += $systemAudit
            }
            
            Write-Progress -Activity "Multi-System Audit" -Completed
            
            # Comparative analysis
            Write-Host "`nPerforming comparative analysis..." -ForegroundColor Cyan
            
            $successfulAudits = @($auditResults.SystemAudits | Where-Object { $_.Status -eq 'Success' })
            
            if ($successfulAudits.Count -gt 0) {
                $auditResults.ComparativeAnalysis.AverageSecurityScore = [math]::Round(($successfulAudits | Measure-Object -Property SecurityScore -Average).Average, 2)
                $auditResults.ComparativeAnalysis.HighestScore = ($successfulAudits | Measure-Object -Property SecurityScore -Maximum).Maximum
                $auditResults.ComparativeAnalysis.LowestScore = ($successfulAudits | Measure-Object -Property SecurityScore -Minimum).Minimum
                
                $bestSystem = $successfulAudits | Sort-Object -Property SecurityScore -Descending | Select-Object -First 1
                $worstSystem = $successfulAudits | Sort-Object -Property SecurityScore | Select-Object -First 1
                
                $auditResults.ComparativeAnalysis.MostSecureSystem = $bestSystem.ComputerName
                $auditResults.ComparativeAnalysis.LeastSecureSystem = $worstSystem.ComputerName
                
                # Find common findings
                $allFindings = $successfulAudits | ForEach-Object { $_.Findings.Finding }
                $commonFindings = @($allFindings | Group-Object | Where-Object { $_.Count -ge ($successfulAudits.Count / 2) } | 
                    Select-Object -ExpandProperty Name)
                
                $auditResults.ComparativeAnalysis.CommonFindings = $commonFindings
            }
            
        }
        catch {
            Write-Error "Error during multi-system audit: $_"
            throw
        }
    }
    
    end {
        # Display summary
        Write-Host "`n=== AUDIT COMPLETE ===" -ForegroundColor Green
        Write-Host "Total Systems: $($auditResults.TotalSystems)" -ForegroundColor Cyan
        Write-Host "Successfully Audited: $($auditResults.Summary.SystemsAudited)" -ForegroundColor Green
        Write-Host "Failed: $($auditResults.Summary.SystemsFailed)" -ForegroundColor Red
        
        Write-Host "`nComparative Analysis:" -ForegroundColor Cyan
        Write-Host " Average Security Score: $($auditResults.ComparativeAnalysis.AverageSecurityScore)/100" -ForegroundColor White
        Write-Host " Highest Score: $($auditResults.ComparativeAnalysis.HighestScore)/100 ($($auditResults.ComparativeAnalysis.MostSecureSystem))" -ForegroundColor Green
        Write-Host " Lowest Score: $($auditResults.ComparativeAnalysis.LowestScore)/100 ($($auditResults.ComparativeAnalysis.LeastSecureSystem))" -ForegroundColor Red
        
        if ($auditResults.ComparativeAnalysis.CriticalSystems.Count -gt 0) {
            Write-Host "`nCritical Systems (Score < 40):" -ForegroundColor Red
            $auditResults.ComparativeAnalysis.CriticalSystems | ForEach-Object {
                Write-Host " - $_" -ForegroundColor Red
            }
        }
        
        Write-Host "`nFindings Summary:" -ForegroundColor Cyan
        Write-Host " Total Findings: $($auditResults.Summary.TotalFindings)" -ForegroundColor White
        Write-Host " Critical: $($auditResults.Summary.CriticalFindings)" -ForegroundColor Red
        Write-Host " High: $($auditResults.Summary.HighFindings)" -ForegroundColor Yellow
        Write-Host " Medium: $($auditResults.Summary.MediumFindings)" -ForegroundColor Yellow
        Write-Host " Low: $($auditResults.Summary.LowFindings)" -ForegroundColor White
        
        if ($auditResults.ComparativeAnalysis.CommonFindings.Count -gt 0) {
            Write-Host "`nCommon Issues (affecting 50%+ of systems):" -ForegroundColor Yellow
            $auditResults.ComparativeAnalysis.CommonFindings | ForEach-Object {
                Write-Host " - $_" -ForegroundColor Yellow
            }
        }
        
        # Save results
        $auditResults | ConvertTo-Json -Depth 10 | Out-File (Join-Path $OutputPath "MultiSystemAudit.json") -Encoding UTF8
        
        # Export system summary
        $auditResults.SystemAudits | Select-Object ComputerName, Status, SecurityScore, SecurityLevel, @{N='FindingsCount';E={$_.Findings.Count}} |
            Export-Csv (Join-Path $OutputPath "SystemSummary.csv") -NoTypeInformation
        
        # Export all findings
        $allFindings = @($auditResults.SystemAudits | ForEach-Object {
            $computer = $_.ComputerName
            $_.Findings | Select-Object @{N='Computer';E={$computer}}, Severity, Finding
        })
        
        if ($allFindings.Count -gt 0) {
            $allFindings | Export-Csv (Join-Path $OutputPath "AllFindings.csv") -NoTypeInformation
        }
        
        # Generate HTML report if requested
        if ($GenerateReport) {
            Write-Host "`nGenerating HTML report..." -ForegroundColor Cyan
            
            $htmlReport = @"
<!DOCTYPE html>
<html>
<head>
    <title>Multi-System Security Audit</title>
    <style>
        body { font-family: Arial; margin: 20px; background: #f5f5f5; }
        .header { background: #2c3e50; color: white; padding: 20px; border-radius: 5px; }
        .summary { background: white; padding: 20px; margin: 20px 0; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
        table { width: 100%; border-collapse: collapse; background: white; }
        th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
        th { background: #34495e; color: white; }
        .critical { color: #e74c3c; font-weight: bold; }
        .high { color: #e67e22; font-weight: bold; }
        .medium { color: #f39c12; }
        .low { color: #3498db; }
    </style>
</head>
<body>
    <div class="header">
        <h1>Multi-System Security Audit Report</h1>
        <p>Date: $($auditResults.AuditDate)</p>
        <p>Systems Audited: $($auditResults.Summary.SystemsAudited) / $($auditResults.TotalSystems)</p>
    </div>
     
    <div class="summary">
        <h2>Comparative Analysis</h2>
        <p>Average Security Score: $($auditResults.ComparativeAnalysis.AverageSecurityScore)/100</p>
        <p>Most Secure: $($auditResults.ComparativeAnalysis.MostSecureSystem) ($($auditResults.ComparativeAnalysis.HighestScore)/100)</p>
        <p>Least Secure: $($auditResults.ComparativeAnalysis.LeastSecureSystem) ($($auditResults.ComparativeAnalysis.LowestScore)/100)</p>
    </div>
     
    <div class="summary">
        <h2>System Audit Results</h2>
        <table>
            <tr>
                <th>Computer</th>
                <th>Status</th>
                <th>Security Score</th>
                <th>Security Level</th>
                <th>Findings</th>
            </tr>
            $($auditResults.SystemAudits | ForEach-Object {
                "<tr>
                    <td>$($_.ComputerName)</td>
                    <td>$($_.Status)</td>
                    <td>$($_.SecurityScore)/100</td>
                    <td class='$($_.SecurityLevel.ToLower())'>$($_.SecurityLevel)</td>
                    <td>$($_.Findings.Count)</td>
                </tr>"
            } | Out-String)
        </table>
    </div>
     
    <div class="summary">
        <h2>Findings Summary</h2>
        <p class="critical">Critical: $($auditResults.Summary.CriticalFindings)</p>
        <p class="high">High: $($auditResults.Summary.HighFindings)</p>
        <p class="medium">Medium: $($auditResults.Summary.MediumFindings)</p>
    </div>
</body>
</html>
"@

            
            $htmlPath = Join-Path $OutputPath "MultiSystemAudit.html"
            $htmlReport | Out-File $htmlPath -Encoding UTF8
            Write-Host "HTML report generated: $htmlPath" -ForegroundColor Green
            Start-Process $htmlPath
        }
        
        Write-Host "`nResults saved to: $OutputPath" -ForegroundColor Cyan
        
        return $auditResults
    }
}