-
Notifications
You must be signed in to change notification settings - Fork 0
Output Reference
Comprehensive guide to understanding and using the audit report outputs.
- Output Formats Overview
- HTML Report
- JSON Report
- CSV Report
- XML Report
- Console Output
- Report Locations
- Parsing and Integration
- Report Retention
The Windows Security Audit Script generates reports in multiple formats, plus real-time console output:
| Format | Best For | File Extension | Typical Size |
|---|---|---|---|
| HTML | Human review, management reporting, interactive analysis | .html |
300-600 KB |
| JSON | Automation, remediation files, API integration | .json |
100-300 KB |
| CSV | Excel analysis, tracking, dashboards | .csv |
50-150 KB |
| XML | SIEM integration, compliance systems | .xml |
150-400 KB |
| JSON (auto-generated) | Machine-readable companion file | .json |
200-500 KB |
| Console | Real-time monitoring, quick checks | N/A | N/A |
Security-Audit-Report-YYYYMMDD-HHMMSS.{html|json|csv|xml}
Examples:
Security-Audit-Report-20241230-143022.html
Security-Audit-Report-20241230-143022.json
Security-Audit-Report-20241230-143022.csv
Security-Audit-Report-20241230-143022.xml
Naming Pattern:
-
YYYYMMDD- Date (Year, Month, Day) -
HHMMSS- Time (Hour, Minute, Second in 24-hour format) - Same timestamp for all formats from single execution
The HTML report is a self-contained, interactive web application designed for human consumption. It requires no external resources and opens in any modern web browser with advanced features for analysis and export.
✨ Interactive Features:
- 🌙 Dark mode toggle with persistent preference
- 🔍 Per-column filtering (real-time search)
↕️ Column sorting (click headers)- ☑️ Row selection for targeted exports
- 📊 Collapsible module sections
- 📤 Built-in export functionality (5 formats)
- 🎨 Color-coded status indicators
Method 1: Automatic (Default)
- Script automatically opens report in default browser after completion
- Skip with:
.\Windows-Security-Audit.ps1 -OutputFormat JSON(or other non-HTML format)
Method 2: Manual
# Double-click the .html file in file explorer
# Or open via PowerShell
Start-Process ".\Security-Audit-Report-*.html"
# Specify browser
Start-Process "chrome.exe" ".\Security-Audit-Report-*.html"
Start-Process "msedge.exe" ".\Security-Audit-Report-*.html"
Start-Process "firefox.exe" ".\Security-Audit-Report-*.html"Banner:
========================================================================================================
Windows Security Audit Report
Comprehensive Multi-Framework Security Assessment
========================================================================================================
Dark Mode Toggle:
- Located in top-right corner
- Click to switch between light and dark themes
- Preference saved in browser's local storage
┌────────────────────────────────────────────────────────────────────────────┐
│ Computer Name │ WORKSTATION01 │
│ Operating System │ Microsoft Windows 11 Pro (Build 22631) │
│ Scan Date │ 2024-12-30 14:30:22 │
│ Duration │ 00:05:34 │
│ Modules Executed │ Core, STIG, NIST, CIS, NSA, CISA, MS │
└────────────────────────────────────────────────────────────────────────────┘
Visual Status Cards:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 3199 │ │ 487 │ │ 34 │ │ 18 │
│ Total Checks │ │ Passed │ │ Failed │ │ Warnings │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
┌──────────────┐ ┌──────────────┐
│ 9 │ │ 2 │
│ Info │ │ Errors │
└──────────────┘ └──────────────┘
Color Coding:
- Blue - Total Checks
- Green - Passed (success)
- Red - Failed (critical)
- Yellow/Orange - Warnings
- Cyan - Informational
- Purple - Errors
Located below the summary dashboard:
┌────────────────────────────────────────────────────────────────┐
│ Global Export Options │
│ [Export All] [Export Selected] │
└────────────────────────────────────────────────────────────────┘
Automatic JSON Companion - Every audit automatically generates a structured JSON file alongside the HTML report, regardless of the -OutputFormat setting.
Export All - Exports all findings across all modules Export Selected - Exports only rows with checked checkboxes from all modules
- Severity Cards — Row of 5 cards below status summary showing Critical/High/Medium/Low/Informational distribution; click to filter
- Category Detail Tables — Each module section includes an expanded table showing every check category with pass/fail/warn/info/error counts and per-category compliance percentage
- Overall Compliance Cards — 4 color-coded cards: Weighted Score (color-coded by result), Overall Rating, Simple Score, Severity-Adjusted Score
- Remediation Priority Ranking (Top 50) — Collapsible section after all modules listing the 50 most critical findings ranked by severity
- Deep Navy Dark Theme — Theme toggle in top-right banner corner; dark mode uses #0b0e14, #111822, #1a2332 matching the Linux Security Audit report
- Table of Contents — Collapsible section below host info cards with links to all modules and Remediation Priority
Each module section includes:
Module Header (Collapsible):
┌────────────────────────────────────────────────────────────────┐
│ MODULE: STIG Pass: 78 | Fail: 8 | Warning: 4 | Info: 2 ▼│
└────────────────────────────────────────────────────────────────┘
- Click header to collapse/expand section
- Arrow indicator (▼/▶) shows current state
Results Table:
┌───┬────────┬─────────────────────────┬──────────────────────────────────┐
│ ☑ │ Status │ Category │ Finding │
├───┼────────┼─────────────────────────┼──────────────────────────────────┤
│ ☐ │ FAIL │ STIG - V-220929 (CAT I) │ Guest account is ENABLED │
│ ☐ │ PASS │ STIG - V-220718 (CAT II)│ Minimum password length: 14 chars│
│ ☐ │ WARN │ STIG - V-220930 (CAT II)│ Administrator account not renamed│
└───┴────────┴─────────────────────────┴──────────────────────────────────┘
Filter boxes under each column header:
┌───┬────────┬─────────────────────────┬──────────────────────────────────┐
│ │[Filter]│[Filter] │[Filter] │
└───┴────────┴─────────────────────────┴──────────────────────────────────┘
Expandable Details: Click any row to expand inline details:
┌────────────────────────────────────────────────────────────────────────┐
│ STIG - V-220929 (CAT I) │
│ Guest account is ENABLED │
├────────────────────────────────────────────────────────────────────────┤
│ Details: │
│ STIG CAT I: Guest account must be disabled to prevent anonymous access. │
│ This is a critical security control. │
│ │
│ REMEDIATION: │
│ Disable-LocalUser -Name Guest │
│ │
│ Timestamp: 2024-12-25 14:30:25 │
└────────────────────────────────────────────────────────────────────────┘
Module Export Buttons: Each module section has its own export options:
- [Export Module] - Export all findings from this module
- [Export Selected from Module] - Export checked rows from this module
Activation:
- Click "Toggle Dark Mode" button (top-right)
- Page theme switches instantly
- Preference saved automatically
Benefits:
- Easier on eyes during long review sessions
- Reduces eye strain
- Professional appearance for presentations
Real-Time Search:
- Type in filter box under any column header
- Results filter instantly as you type
- Case-insensitive matching
- Multiple filters work together (AND logic)
Example Use Cases:
Status filter: "fail" → Shows only failures
Category filter: "CAT I" → Shows only Category I findings
Finding filter: "password" → Shows all password-related findings
How to Sort:
- Click any column header
- First click: Ascending sort
- Second click: Descending sort
- Visual arrow indicator (↑/↓) shows sort direction
Recommended Sorting:
- Sort by Status to group Pass/Fail/Warning
- Sort by Category to group by compliance framework
- Sort by Finding to alphabetically organize results
Selecting Rows:
- ☐ Click checkbox to select individual row
- ☑ Click "Select All" checkbox in header to select all visible rows
- Works with filters (selects only filtered results)
Use Cases:
- Select specific findings to remediate
- Export subset of results
- Create targeted reports for specific teams
Workflow:
- Click any Export button (Global or Module-level)
- Modal dialog appears with format options
- Click desired format
- File downloads automatically
Available Export Formats:
| Format | Description | Best For |
|---|---|---|
| CSV | Comma-delimited spreadsheet | Excel analysis, pivot tables |
| Excel | Formatted HTML table (.xls) |
Presentations, formatted reports |
| JSON | Machine-readable data | Remediation files, automation |
| XML | Standards-compliant structure | SIEM ingestion, compliance systems |
| TXT | Fixed-width plain text | Documentation, email, tickets |
Steps:
- Open HTML report
- Click "Status" column header to sort
- Use Status filter box, type: "fail"
- Click "Export All" button
- Select "Excel" format
- Send to management
Result: Formatted Excel report with only failed checks
Steps:
- Open HTML report
- Review findings carefully
- Check boxes next to issues you want to fix
- Click "Export Selected" button
- Select "JSON" format
- Save as
remediation-plan.json
Use Remediation File:
.\Windows-Security-Audit.ps1 -AutoRemediate -RemediationFile "remediation-plan.json"Result: Only selected issues are remediated
Steps:
- Navigate to STIG module section
- Click "Export Module" button
- Select "TXT" format
- Include in compliance documentation
Result: Clean text report of all STIG findings
Steps:
- Filter STIG module to show "CAT I" findings
- Select all filtered results (checkboxes)
- Click "Export Selected from Module"
- Select "CSV"
- Import to project tracking tool
Result: CSV of only critical STIG findings
✅ Fully Supported:
- Google Chrome (v90+)
- Microsoft Edge (v90+)
- Mozilla Firefox (v88+)
- Safari (v14+)
- Internet Explorer (not recommended, lacks modern features)
- Older browsers (may lack dark mode, export features)
Ctrl+F (Cmd+F on Mac) - Browser's find function
Ctrl+P (Cmd+P on Mac) - Print dialog (save to PDF)
Ctrl+S (Cmd+S on Mac) - Save HTML file
Ctrl+Click - Open link in new tab (if external links present)
Browser Find (Ctrl+F) Use Cases:
Search: "CAT I" → Find all Category I STIG findings
Search: "Fail" → Jump through all failures
Search: "BitLocker" → Find encryption-related findings
Search: "V-220929" → Find specific STIG V-ID
Search: "password" → Find all password-related checks
High-Quality PDF Export:
1. Open HTML report
2. Press Ctrl+P (Cmd+P on Mac)
3. Destination: "Save as PDF"
4. Settings:
- Layout: Portrait
- Margins: Default
- Scale: 100%
- Background graphics: On (for colors)
5. Click "Save"
Result: Professional PDF with full formatting, colors, and tables
Best Practices:
✅ Do:
- Share complete HTML file (self-contained)
- Use secure file transfer methods
- Add date/context in email subject
- Include summary of key findings
- Encrypt if sending externally
❌ Don't:
- Email to untrusted recipients
- Post to public file shares
- Modify HTML manually (regenerate instead)
- Share without management approval
- Include in public repositories
Secure Sharing Example:
# Compress report with password
Compress-Archive -Path "Security-Audit-Report-*.html" -DestinationPath "Audit-Report.zip"
# Use 7-Zip or similar to add password protection
# Send via secure email
Send-MailMessage `
-To "security-team@company.com" `
-From "auditor@company.com" `
-Subject "Security Audit - $env:COMPUTERNAME - $(Get-Date -Format 'yyyy-MM-dd')" `
-Body "Please find attached security audit report. Password will be sent separately." `
-Attachments "Audit-Report.zip" `
-Priority HighScreen Reader Support:
- Semantic HTML structure
- ARIA labels on interactive elements
- Keyboard navigation support
- High contrast in dark mode
Keyboard Navigation:
- Tab through interactive elements
- Enter to activate buttons
- Space to toggle checkboxes
- Arrow keys in filter boxes
The JSON report contains structured, machine-readable data designed for automation, integration, and programmatic analysis. It can also be used as a remediation file with the -RemediationFile parameter.
{
"ExecutionInfo": {
"ComputerName": "WORKSTATION01",
"OSVersion": "Microsoft Windows 11 Pro",
"ScanDate": "2024-12-30 14:30:22",
"Duration": "00:05:34",
"ModulesRun": ["Core", "STIG", "NIST", "CIS", "NSA", "CISA", "MS"],
"TotalChecks": 3199,
"PassCount": 487,
"FailCount": 34,
"WarningCount": 18,
"InfoCount": 9,
"ErrorCount": 2
},
"Results": [
{
"Module": "STIG",
"Category": "STIG - V-220929 (CAT I)",
"Status": "Fail",
"Message": "Guest account is ENABLED",
"Details": "STIG CAT I: Guest account must be disabled to prevent anonymous access",
"Remediation": "Disable-LocalUser -Name Guest",
"Timestamp": "2024-12-30 14:30:25"
},
{
"Module": "STIG",
"Category": "STIG - V-220718 (CAT II)",
"Status": "Pass",
"Message": "Minimum password length: 14 characters",
"Details": "STIG: Passwords must be at least 14 characters to resist brute force",
"Remediation": "",
"Timestamp": "2024-12-30 14:30:25"
}
]
}When you export selected issues from the HTML report, the JSON structure is optimized for use with -RemediationFile:
{
"exportDate": "2024-12-30T14:30:22Z",
"modules": [
{
"moduleName": "STIG",
"results": [
{
"Status": "Fail",
"Category": "STIG - V-220929 (CAT I)",
"Finding": "Guest account is ENABLED"
},
{
"Status": "Fail",
"Category": "STIG - V-220968 (CAT II)",
"Finding": "SMBv1 protocol is ENABLED"
}
]
},
{
"moduleName": "CIS",
"results": [
{
"Status": "Fail",
"Category": "CIS - Account Policy",
"Finding": "Minimum password length is 8 characters"
}
]
}
]
}Usage:
# Export selected issues from HTML report to JSON
# Then use for targeted remediation:
.\Windows-Security-Audit.ps1 -AutoRemediate -RemediationFile "selected-issues.json"# Load JSON file
$report = Get-Content ".\Security-Audit-Report-*.json" -Raw | ConvertFrom-Json
# Access execution information
$report.ExecutionInfo.ComputerName
$report.ExecutionInfo.TotalChecks
$report.ExecutionInfo.FailCount
# Get all results
$report.Results
# Filter by status
$failures = $report.Results | Where-Object { $_.Status -eq "Fail" }
$warnings = $report.Results | Where-Object { $_.Status -eq "Warning" }
# Display failures
$failures | Format-Table Module, Category, Message -AutoSize# Get CAT I STIG findings
$catI = $report.Results | Where-Object { $_.Category -like "*CAT I*" }
# Get all password-related findings
$passwordIssues = $report.Results | Where-Object {
$_.Message -like "*password*" -or $_.Category -like "*password*"
}
# Filter by module
$stigResults = $report.Results | Where-Object { $_.Module -eq "STIG" }
$cisResults = $report.Results | Where-Object { $_.Module -eq "CIS" }
# Get findings with remediation available
$remediable = $report.Results | Where-Object {
$_.Status -eq "Fail" -and $_.Remediation -ne ""
}
# Count findings by module and status
$report.Results |
Group-Object Module, Status |
Select-Object Count, Name |
Sort-Object Name# Calculate compliance rate
$totalChecks = $report.ExecutionInfo.TotalChecks
$passCount = $report.ExecutionInfo.PassCount
$complianceRate = [math]::Round(($passCount / $totalChecks) * 100, 2)
Write-Host "Compliance Rate: $complianceRate%"
# Status distribution
$statusCounts = $report.Results | Group-Object Status
foreach ($status in $statusCounts) {
$percentage = [math]::Round(($status.Count / $totalChecks) * 100, 1)
Write-Host "$($status.Name): $($status.Count) ($percentage%)"
}
# Module performance
$moduleStats = $report.Results | Group-Object Module | ForEach-Object {
$moduleName = $_.Name
$total = $_.Count
$pass = ($_.Group | Where-Object Status -eq "Pass").Count
$fail = ($_.Group | Where-Object Status -eq "Fail").Count
$compliance = [math]::Round(($pass / $total) * 100, 1)
[PSCustomObject]@{
Module = $moduleName
Total = $total
Pass = $pass
Fail = $fail
Compliance = "$compliance%"
}
}
$moduleStats | Format-Table -AutoSize# Export failures to CSV for tracking
$failures |
Select-Object Module, Category, Message, Remediation |
Export-Csv "Failures-ToRemediate.csv" -NoTypeInformation
# Create remediation checklist
$failures | ForEach-Object {
[PSCustomObject]@{
Priority = if ($_.Category -like "*CAT I*") { "HIGH" }
elseif ($_.Category -like "*CAT II*") { "MEDIUM" }
else { "LOW" }
Module = $_.Module
Issue = $_.Message
Remediation = $_.Remediation
AssignedTo = ""
Status = "Open"
DueDate = (Get-Date).AddDays(7).ToString("yyyy-MM-dd")
}
} | Export-Csv "Remediation-Checklist.csv" -NoTypeInformation
# Generate executive summary
$summary = @"
Security Audit Summary - $($report.ExecutionInfo.ComputerName)
Date: $($report.ExecutionInfo.ScanDate)
Overall Compliance: $complianceRate%
Critical Issues (Fail): $($report.ExecutionInfo.FailCount)
Warnings: $($report.ExecutionInfo.WarningCount)
Top Issues:
$($failures | Select-Object -First 5 | ForEach-Object { "- $($_.Message)" } | Out-String)
Full report: Security-Audit-Report-$(Get-Date -Format 'yyyyMMdd-HHmmss').html
"@
$summary | Out-File "Executive-Summary.txt"import json
from datetime import datetime
# Load JSON
with open('Security-Audit-Report-20241230-143022.json', 'r') as f:
report = json.load(f)
# Access data
exec_info = report['ExecutionInfo']
print(f"Computer: {exec_info['ComputerName']}")
print(f"Total Checks: {exec_info['TotalChecks']}")
print(f"Failures: {exec_info['FailCount']}")
print(f"Compliance: {(exec_info['PassCount']/exec_info['TotalChecks']*100):.1f}%")
# Filter failures
failures = [r for r in report['Results'] if r['Status'] == 'Fail']
print(f"\nFailures ({len(failures)}):")
for fail in failures:
print(f" [{fail['Module']}] {fail['Message']}")
# Group by module
from collections import defaultdict
by_module = defaultdict(lambda: {'total': 0, 'pass': 0, 'fail': 0})
for result in report['Results']:
module = result['Module']
by_module[module]['total'] += 1
if result['Status'] == 'Pass':
by_module[module]['pass'] += 1
elif result['Status'] == 'Fail':
by_module[module]['fail'] += 1
print("\nModule Summary:")
for module, stats in by_module.items():
compliance = (stats['pass'] / stats['total'] * 100)
print(f" {module}: {compliance:.1f}% ({stats['pass']}/{stats['total']})")
# Create remediation list
remediation_list = [
{
'priority': 'HIGH' if 'CAT I' in r['Category'] else 'MEDIUM',
'module': r['Module'],
'issue': r['Message'],
'remediation': r['Remediation']
}
for r in failures if r['Remediation']
]
# Export to JSON
with open('remediation-plan.json', 'w') as f:
json.dump(remediation_list, f, indent=2)const fs = require('fs');
// Load JSON
const report = JSON.parse(
fs.readFileSync('Security-Audit-Report-20241230-143022.json', 'utf8')
);
// Access data
const { ExecutionInfo, Results } = report;
console.log(`Computer: ${ExecutionInfo.ComputerName}`);
console.log(`Total Checks: ${ExecutionInfo.TotalChecks}`);
console.log(`Failures: ${ExecutionInfo.FailCount}`);
// Calculate compliance
const compliance = ((ExecutionInfo.PassCount / ExecutionInfo.TotalChecks) * 100).toFixed(1);
console.log(`Compliance: ${compliance}%`);
// Filter and group
const failures = Results.filter(r => r.Status === 'Fail');
const byModule = Results.reduce((acc, r) => {
if (!acc[r.Module]) {
acc[r.Module] = { total: 0, pass: 0, fail: 0 };
}
acc[r.Module].total++;
if (r.Status === 'Pass') acc[r.Module].pass++;
if (r.Status === 'Fail') acc[r.Module].fail++;
return acc;
}, {});
console.log('\nModule Summary:');
Object.entries(byModule).forEach(([module, stats]) => {
const moduleCompliance = ((stats.pass / stats.total) * 100).toFixed(1);
console.log(` ${module}: ${moduleCompliance}% (${stats.pass}/${stats.total})`);
});
// Create issue tickets
const tickets = failures.map(f => ({
title: f.Message,
description: f.Details,
priority: f.Category.includes('CAT I') ? 'Critical' : 'High',
category: f.Category,
remediation: f.Remediation,
module: f.Module
}));
// Export
fs.writeFileSync('tickets.json', JSON.stringify(tickets, null, 2));The HTML report provides 6 browser-based export formats via the "Export All" / "Export Selected" modal:
| Format | Description | Use Case |
|---|---|---|
| CSV Workbook | Comma-separated values with all fields | Spreadsheet analysis, data import |
| Excel (XLS) | HTML-based Excel workbook format | Direct opening in Microsoft Excel |
| JSON Structured Data | Formatted JSON with metadata envelope | API integration, programmatic analysis |
| XML Workbook | XSL-styled self-rendering XML | Browser viewing, XML-based tools, data interchange |
| XML SIEM-Compatible | Event-based XML with severity/timestamp | SIEM ingestion (Splunk, QRadar, ArcSight) |
| TXT Plain Text | Human-readable plain text report | Email, documentation, log archival |
The XML Workbook format uses an embedded XSLT stylesheet that renders the XML as a styled HTML table when opened in any modern browser. The data remains valid XML within the urn:windows-security-audit namespace, parseable by XML tools.
The XML SIEM-Compatible format produces raw XML without styling, optimized for secondary processing by SIEM appliances and log aggregation platforms.
# Index JSON files
/opt/splunk/bin/splunk add oneshot /path/to/reports/*.json \
-sourcetype windows:security:audit \
-index securitySplunk Search Queries:
# Find all failures
sourcetype="windows:security:audit" ExecutionInfo.FailCount>0
| table ExecutionInfo.ComputerName, ExecutionInfo.FailCount
# Track compliance over time
sourcetype="windows:security:audit"
| eval compliance=(ExecutionInfo.PassCount/ExecutionInfo.TotalChecks)*100
| timechart avg(compliance) by ExecutionInfo.ComputerName
# Alert on critical failures
sourcetype="windows:security:audit" Results{}.Status="Fail" Results{}.Category="*CAT I*"
| table ExecutionInfo.ComputerName, Results{}.Message
// Create incidents from failures
const report = require('./Security-Audit-Report-20241230.json');
const failures = report.Results.filter(r => r.Status === 'Fail');
failures.forEach(fail => {
const priority = fail.Category.includes('CAT I') ? '1' :
fail.Category.includes('CAT II') ? '2' : '3';
createIncident({
short_description: `[${fail.Module}] ${fail.Message}`,
description: fail.Details,
work_notes: `Remediation: ${fail.Remediation}`,
priority: priority,
category: 'Security',
subcategory: fail.Module,
assigned_to: 'security-team',
configuration_item: report.ExecutionInfo.ComputerName
});
});from influxdb import InfluxDBClient
import json
client = InfluxDBClient(host='localhost', port=8086, database='security_metrics')
with open('Security-Audit-Report-20241230.json') as f:
report = json.load(f)
# Write metrics to InfluxDB
points = [{
"measurement": "security_audit",
"tags": {
"hostname": report['ExecutionInfo']['ComputerName'],
"os_version": report['ExecutionInfo']['OSVersion']
},
"time": report['ExecutionInfo']['ScanDate'],
"fields": {
"total_checks": report['ExecutionInfo']['TotalChecks'],
"pass_count": report['ExecutionInfo']['PassCount'],
"fail_count": report['ExecutionInfo']['FailCount'],
"warning_count": report['ExecutionInfo']['WarningCount'],
"compliance_rate": (report['ExecutionInfo']['PassCount'] /
report['ExecutionInfo']['TotalChecks'] * 100)
}
}]
# Add per-module metrics
for module in set(r['Module'] for r in report['Results']):
module_results = [r for r in report['Results'] if r['Module'] == module]
pass_count = sum(1 for r in module_results if r['Status'] == 'Pass')
points.append({
"measurement": "security_audit_module",
"tags": {
"hostname": report['ExecutionInfo']['ComputerName'],
"module": module
},
"time": report['ExecutionInfo']['ScanDate'],
"fields": {
"total": len(module_results),
"pass": pass_count,
"compliance": (pass_count / len(module_results) * 100)
}
})
client.write_points(points)$report = Get-Content ".\Security-Audit-Report-*.json" -Raw | ConvertFrom-Json
$failures = $report.Results | Where-Object { $_.Status -eq "Fail" }
# Alert on critical failures
$criticalFailures = $failures | Where-Object { $_.Category -like "*CAT I*" }
if ($criticalFailures.Count -gt 0) {
$body = @"
CRITICAL SECURITY ALERT
=======================
System: $($report.ExecutionInfo.ComputerName)
Date: $($report.ExecutionInfo.ScanDate)
Critical Failures ($($criticalFailures.Count)):
$(foreach ($fail in $criticalFailures) {
"• [$($fail.Module)] $($fail.Message)`n Remediation: $($fail.Remediation)`n"
})
Full Report: \\FileServer\Security\Audits\$($report.ExecutionInfo.ComputerName)\
"@
Send-MailMessage `
-To "security-team@company.com", "manager@company.com" `
-From "security-audit@company.com" `
-Subject "🚨 CRITICAL: Security Failures on $($report.ExecutionInfo.ComputerName)" `
-Body $body `
-Priority High `
-SmtpServer "smtp.company.com"
}The CSV report provides tabular data optimized for spreadsheet analysis, tracking remediation over time, and creating dashboards.
Module,Category,Status,Message,Details,Remediation,Timestamp
STIG,"STIG - V-220929 (CAT I)",Fail,Guest account is ENABLED,"STIG CAT I: Guest account must be disabled to prevent anonymous access",Disable-LocalUser -Name Guest,2024-12-30 14:30:25
STIG,"STIG - V-220718 (CAT II)",Pass,Minimum password length: 14 characters,"STIG: Passwords must be at least 14 characters to resist brute force",,2024-12-30 14:30:25
CIS,"CIS - Account Policy",Fail,Minimum password length is 8 characters,"CIS Benchmark: Set minimum password length to 14 or more characters",net accounts /minpwlen:14,2024-12-30 14:30:26Field Descriptions:
- Module - Framework name (Core, STIG, NIST, CIS, NSA, CISA, MS)
- Category - Specific control or requirement ID
- Status - Result status (Pass, Fail, Warning, Info, Error)
- Message - Brief description of the finding
- Details - Extended information about the check
- Remediation - PowerShell command or instructions to fix
- Timestamp - When the check was performed
- Navigate to CSV file
- Double-click to open in Excel
- Columns auto-detected
Excel → Data → From Text/CSV
→ Select file
→ Delimiter: Comma
→ Text qualifier: " (double quote)
→ Click "Load"
Excel → Data → Get Data → From File → From Text/CSV
→ Select file
→ Click "Transform Data" for advanced options
→ Data Types detected automatically
→ Click "Close & Load"
Show Only Failures:
1. Select data range (Ctrl+A)
2. Data → Filter
3. Click Status column dropdown
4. Uncheck all except "Fail"
5. Click OK
Sort by Priority:
1. Add helper column: Priority
2. Formula: =IF(ISNUMBER(SEARCH("CAT I",B2)),"Critical",IF(ISNUMBER(SEARCH("CAT II",B2)),"High","Medium"))
3. Sort by Priority column (Critical → High → Medium)
Create Status Summary by Module:
1. Select data range
2. Insert → PivotTable
3. Drag "Module" to Rows
4. Drag "Status" to Columns
5. Drag "Status" to Values (Count)
6. Result: Cross-tab showing Pass/Fail/Warning counts per module
Compliance Percentage Calculation:
1. In PivotTable
2. Add Calculated Field: Compliance
3. Formula: =Pass/(Pass+Fail+Warning)*100
4. Format as percentage
Color-Code Status Column:
1. Select Status column
2. Home → Conditional Formatting → New Rule
3. Format cells that contain:
- "Fail" → Red background, white text
- "Pass" → Green background, white text
- "Warning" → Yellow background, black text
- "Info" → Blue background, white text
4. Apply to range
Highlight Priority Issues:
1. Select Category column
2. Conditional Formatting → Highlight Cell Rules → Text that Contains
3. Enter "CAT I" → Red fill with dark red text
4. Repeat for "CAT II" → Orange fill
Compliance Dashboard Template:
A1: Security Audit Dashboard
A2: System: [=B2]
A3: Date: [=B3]
A5: Overall Statistics
A6: Total Checks: [=COUNTA(Status column)]
A7: Passed: [=COUNTIF(Status,"Pass")]
A8: Failed: [=COUNTIF(Status,"Fail")]
A9: Compliance Rate: [=A7/A6] (format as %)
A11: [Pie Chart: Status Distribution]
A20: [Bar Chart: Failures by Module]
A30: [Line Chart: Trend over time (if multiple audits)]
B5: Top 5 Critical Issues
B6: [Table filtered to CAT I failures, top 5]
Charts to Create:
- Pie Chart - Status distribution (Pass/Fail/Warning/Info/Error)
- Bar Chart - Count of failures by module
- Stacked Bar - Pass/Fail/Warning per module
- Line Chart - Compliance trend over time (requires multiple audit files)
Step 1: Run Baseline Audit
.\Windows-Security-Audit.ps1 -OutputFormat CSVStep 2: Create Tracking Spreadsheet
1. Open baseline CSV in Excel
2. Filter to show only "Fail" status
3. Copy filtered results to new sheet: "Remediation Tracking"
4. Add columns:
- Column H: Assigned To
- Column I: Priority (High/Medium/Low)
- Column J: Due Date
- Column K: Status (Open/In Progress/Complete)
- Column L: Completion Date
- Column M: Notes
5. Save as "Remediation-Tracker-2024-12.xlsx"
Week 1: Assign issues
- Assign critical (CAT I) to security team
- Set due dates (7 days for critical, 30 for others)
- Status: Open
Week 2: Begin remediation
- Update Status to "In Progress"
- Add notes on progress/blockers
Week 3: Verify fixes
- Run new audit
- Compare results
- Mark items "Complete" with completion date
Week 4: Management report
- Generate summary metrics
- Show improvement trend
- Identify remaining issues
Compare Current vs Baseline:
// In current audit sheet, add column:
=VLOOKUP(B2,Baseline!B:C,2,FALSE)
// Returns status from baseline for same finding
// Conditional format:
// If previous = "Fail" AND current = "Pass" → Green (Fixed)
// If previous = "Fail" AND current = "Fail" → Red (Not Fixed)
// Overall improvement
Total Issues (Baseline): [=COUNTIF(Baseline!C:C,"Fail")]
Resolved Issues: [=COUNTIF(Current!C:C,"Pass")-COUNTIF(Baseline!C:C,"Pass")]
Remaining Issues: [=COUNTIF(Current!C:C,"Fail")]
Resolution Rate: [=Resolved/Total*100]%
// By module
Module: STIG
Baseline Failures: [=COUNTIFS(Baseline!A:A,"STIG",Baseline!C:C,"Fail")]
Current Failures: [=COUNTIFS(Current!A:A,"STIG",Current!C:C,"Fail")]
Improvement: [=Baseline-Current]
# Import CSV
$audit = Import-Csv ".\Security-Audit-Report-*.csv"
# Basic statistics
$total = $audit.Count
$pass = ($audit | Where-Object Status -eq "Pass").Count
$fail = ($audit | Where-Object Status -eq "Fail").Count
$compliance = [math]::Round(($pass / $total) * 100, 1)
Write-Host "Compliance: $compliance% ($pass/$total)"
# Group by module
$byModule = $audit | Group-Object Module | ForEach-Object {
$moduleTotal = $_.Count
$modulePass = ($_.Group | Where-Object Status -eq "Pass").Count
$moduleCompliance = [math]::Round(($modulePass / $moduleTotal) * 100, 1)
[PSCustomObject]@{
Module = $_.Name
Total = $moduleTotal
Pass = $modulePass
Fail = ($_.Group | Where-Object Status -eq "Fail").Count
Compliance = "$moduleCompliance%"
}
}
$byModule | Format-Table -AutoSize
# Export failures for remediation
$failures = $audit | Where-Object { $_.Status -eq "Fail" }
$failures | Select-Object Module, Category, Message, Remediation |
Export-Csv "Remediation-List.csv" -NoTypeInformation
# Create summary report
$summary = @"
Security Audit Summary
Date: $(Get-Date -Format 'yyyy-MM-dd')
Overall: $compliance% compliant
- Total: $total
- Pass: $pass
- Fail: $fail
By Module:
$($byModule | Format-Table -AutoSize | Out-String)
"@
$summary | Out-File "Summary-Report.txt"-- Create table
CREATE TABLE audit_results (
id INT IDENTITY(1,1) PRIMARY KEY,
audit_date DATE,
hostname VARCHAR(50),
module VARCHAR(50),
category VARCHAR(100),
status VARCHAR(20),
message VARCHAR(500),
details TEXT,
remediation VARCHAR(500),
timestamp DATETIME
);
-- Bulk import CSV
BULK INSERT audit_results
FROM 'C:\Audits\Security-Audit-Report-20241230.csv'
WITH (
FORMAT = 'CSV',
FIRSTROW = 2,
FIELDTERMINATOR = ',',
ROWTERMINATOR = '\n'
);
-- Query compliance trend
SELECT
audit_date,
COUNT(*) as total_checks,
SUM(CASE WHEN status = 'Pass' THEN 1 ELSE 0 END) as passed,
SUM(CASE WHEN status = 'Fail' THEN 1 ELSE 0 END) as failed,
CAST(SUM(CASE WHEN status = 'Pass' THEN 1 ELSE 0 END) * 100.0 / COUNT(*) AS DECIMAL(5,2)) as compliance_rate
FROM audit_results
GROUP BY audit_date
ORDER BY audit_date;Setup:
1. Open PowerBI Desktop
2. Get Data → CSV
3. Select Security-Audit-Report-*.csv
4. Transform Data:
- Set data types (Module=Text, Status=Text, etc.)
- Add calculated column: Priority
= IF([Category] CONTAINS "CAT I", "Critical",
IF([Category] CONTAINS "CAT II", "High", "Medium"))
5. Load data
6. Create visualizations:
- Card: Total Checks, Pass Count, Fail Count, Compliance %
- Donut Chart: Status distribution
- Bar Chart: Failures by module
- Table: Top 10 critical failures
- Line Chart: Compliance trend (requires multiple imports over time)
DAX Measures:
Compliance Rate =
DIVIDE(
CALCULATE(COUNT('audit'[Status]), 'audit'[Status] = "Pass"),
COUNT('audit'[Status])
)
Critical Failures =
CALCULATE(
COUNT('audit'[Status]),
'audit'[Status] = "Fail",
'audit'[Priority] = "Critical"
)
# Create tracking database
$trackingData = Import-Csv ".\Security-Audit-Report-*.csv" |
Where-Object { $_.Status -eq "Fail" } |
Select-Object @{N='AuditDate';E={Get-Date -Format 'yyyy-MM-dd'}},
@{N='Hostname';E={$env:COMPUTERNAME}},
Module,
Category,
Message,
@{N='Priority';E={
if ($_.Category -like "*CAT I*") { "Critical" }
elseif ($_.Category -like "*CAT II*") { "High" }
else { "Medium" }
}},
@{N='AssignedTo';E={''}},
@{N='DueDate';E={(Get-Date).AddDays(7).ToString('yyyy-MM-dd')}},
@{N='Status';E={'Open'}},
@{N='CompletionDate';E={''}},
@{N='Notes';E={''}}
$trackingData | Export-Csv "Remediation-Tracking-$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformationThe XML report provides standards-compliant, hierarchical data specifically optimized for SIEM ingestion, compliance management systems, and enterprise security tools.
<?xml version="1.0" encoding="UTF-8"?>
<security_audit>
<metadata>
<export_date>2024-12-30T14:30:22Z</export_date>
<computer_name>WORKSTATION01</computer_name>
<operating_system>Microsoft Windows 11 Pro</operating_system>
<scan_date>2024-12-30 14:30:22</scan_date>
<duration>00:05:34</duration>
<total_checks>3199</total_checks>
<pass_count>487</pass_count>
<fail_count>34</fail_count>
<warning_count>18</warning_count>
<info_count>9</info_count>
<error_count>2</error_count>
</metadata>
<events>
<event>
<timestamp>2024-12-30T14:30:25Z</timestamp>
<module>STIG</module>
<status>Fail</status>
<category>STIG - V-220929 (CAT I)</category>
<message>Guest account is ENABLED</message>
<details>STIG CAT I: Guest account must be disabled to prevent anonymous access</details>
<remediation>Disable-LocalUser -Name Guest</remediation>
</event>
<event>
<timestamp>2024-12-30T14:30:25Z</timestamp>
<module>STIG</module>
<status>Pass</status>
<category>STIG - V-220718 (CAT II)</category>
<message>Minimum password length: 14 characters</message>
<details>STIG: Passwords must be at least 14 characters to resist brute force</details>
<remediation></remediation>
</event>
</events>
</security_audit>Benefits:
- ✅ Standards-compliant format
- ✅ Hierarchical structure for complex data
- ✅ Wide SIEM compatibility
- ✅ XPath query support
- ✅ Schema validation capable
- ✅ Direct ingestion by security tools
# Load XML
[xml]$report = Get-Content ".\Security-Audit-Report-*.xml"
# Access metadata
$report.security_audit.metadata.computer_name
$report.security_audit.metadata.total_checks
$report.security_audit.metadata.fail_count
# Get all events
$events = $report.security_audit.events.event
# Filter failures
$failures = $events | Where-Object { $_.status -eq "Fail" }
# Display failures
$failures | ForEach-Object {
Write-Host "[$($_.module)] $($_.message)" -ForegroundColor Red
Write-Host " Remediation: $($_.remediation)" -ForegroundColor Yellow
}
# Count by status
$events | Group-Object status | Format-Table Count, Name -AutoSize
# Get critical findings
$critical = $events | Where-Object {
$_.status -eq "Fail" -and $_.category -like "*CAT I*"
}
Write-Host "`nCritical Failures: $($critical.Count)" -ForegroundColor Red
$critical | ForEach-Object {
Write-Host "- $($_.message)" -ForegroundColor Red
}import xml.etree.ElementTree as ET
# Parse XML
tree = ET.parse('Security-Audit-Report-20241230-143022.xml')
root = tree.getroot()
# Access metadata
metadata = root.find('metadata')
computer = metadata.find('computer_name').text
total = int(metadata.find('total_checks').text)
failed = int(metadata.find('fail_count').text)
compliance = ((total - failed) / total * 100)
print(f"Computer: {computer}")
print(f"Total Checks: {total}")
print(f"Failed: {failed}")
print(f"Compliance: {compliance:.1f}%")
# Parse events
events = root.find('events')
failures = []
for event in events.findall('event'):
if event.find('status').text == 'Fail':
failures.append({
'module': event.find('module').text,
'category': event.find('category').text,
'message': event.find('message').text,
'remediation': event.find('remediation').text
})
print(f"\nFailures ({len(failures)}):")
for fail in failures:
print(f" [{fail['module']}] {fail['message']}")<!-- inputs.conf -->
<monitor>
<path>C:\SecurityAudits\Reports\*.xml</path>
<sourcetype>windows:security:audit:xml</sourcetype>
<index>security</index>
</monitor>Splunk Search Queries:
sourcetype="windows:security:audit:xml"
| xmlkv
| table computer_name, fail_count, compliance_rate
sourcetype="windows:security:audit:xml" status="Fail"
| stats count by module, category
<!-- Import XML reports directly into ArcSight -->
<!-- Configure XML FlexConnector to parse security_audit format --># Configure log source to parse XML
# Map fields: module → category, status → severityReal-time progress display during script execution with color-coded status indicators.
[*] Starting Windows Security Audit Script v6.0
[*] Output Directory: C:\SecurityAudit\Reports
[*] Running Modules: Core, STIG, NIST, CIS, NSA, CISA, MS
[Core] Starting Core security baseline checks...
[Core] Checking User Accounts...
[Core] Checking Account Policies...
[Core] Checking Windows Defender...
[Core] Module completed:
Total Checks: 42
Passed: 38
Failed: 2
Warnings: 1
Info: 1
Errors: 0
[STIG] Starting DISA STIG compliance checks...
[STIG] Checking Account Policies...
[STIG] Checking Audit Policy Configuration...
[STIG] Module completed:
Total Checks: 92
Passed: 78
Failed: 8
Warnings: 4
Info: 2
Errors: 0
...
[*] ========================================
[*] Audit Complete!
[*] ========================================
[*] Total Checks: 3199
[*] Passed: 487 (88.5%)
[*] Failed: 34 (6.2%)
[*] Warnings: 18 (3.3%)
[*] Info: 9 (1.6%)
[*] Errors: 2 (0.4%)
[*]
[*] Reports generated:
[*] HTML: C:\SecurityAudit\Reports\Security-Audit-Report-20241225-143022.html
[*] JSON: C:\SecurityAudit\Reports\Security-Audit-Report-20241225-143022.json
[*] CSV: C:\SecurityAudit\Reports\Security-Audit-Report-20241225-143022.csv
[*] XML: C:\SecurityAudit\Reports\Security-Audit-Report-20241225-143022.xml
- Cyan - Module names and section headers
- Yellow - Category/check group names
- Green - Pass status, success messages
- Red - Fail status, error messages
- Magenta - Module completion summaries
- White - Neutral information
# Redirect to file
.\Windows-Security-Audit.ps1 > audit_log.txt 2>&1
# Save transcript
Start-Transcript -Path "audit_transcript.txt"
.\Windows-Security-Audit.ps1
Stop-Transcript
# Run in background job
Start-Job -ScriptBlock {
C:\SecurityAudit\Windows-Security-Audit.ps1
}
# Check job status
Get-Job | Format-Table -AutoSize
# Retrieve job output
Receive-Job -Id 1.\
Current directory where script is executed
Example:
C:\SecurityAudit\Security-Audit-Report-20241230-143022.html
# Specify custom location
.\Windows-Security-Audit.ps1 -OutputPath "C:\AuditReports\Report.html"
# Network share
.\Windows-Security-Audit.ps1 -OutputPath "\\FileServer\Security\Audits\Report.html"
# Timestamped folders
$date = Get-Date -Format "yyyy-MM-dd"
.\Windows-Security-Audit.ps1 -OutputPath "C:\Audits\$date\Report.html"
# Per-computer folders
.\Windows-Security-Audit.ps1 -OutputPath "C:\Audits\$env:COMPUTERNAME\Report.html"# List recent HTML reports
Get-ChildItem ".\Security-Audit-Report-*.html" |
Sort-Object LastWriteTime -Descending |
Select-Object -First 5
# Find today's reports
$today = Get-Date -Format "yyyyMMdd"
Get-ChildItem ".\Security-Audit-Report-$today*.html"
# Find all reports
Get-ChildItem ".\Security-Audit-Report-*.html"
Get-ChildItem ".\Security-Audit-Report-*.json"
Get-ChildItem ".\Security-Audit-Report-*.csv"
Get-ChildItem ".\Security-Audit-Report-*.xml"# Load and analyze JSON
$report = Get-Content ".\Security-Audit-Report-*.json" -Raw | ConvertFrom-Json
# Extract key metrics
$metrics = @{
Hostname = $report.ExecutionInfo.ComputerName
TotalChecks = $report.ExecutionInfo.TotalChecks
Failures = $report.ExecutionInfo.FailCount
ComplianceRate = [math]::Round(($report.ExecutionInfo.PassCount / $report.ExecutionInfo.TotalChecks * 100), 1)
AuditDate = $report.ExecutionInfo.ScanDate
}
# Store in database
Invoke-SqlCmd -Query "
INSERT INTO AuditMetrics (Hostname, Date, Total, Failures, Compliance)
VALUES ('$($metrics.Hostname)', '$($metrics.AuditDate)',
$($metrics.TotalChecks), $($metrics.Failures), $($metrics.ComplianceRate))
"const fs = require('fs');
const express = require('express');
const app = express();
// Endpoint to retrieve audit results
app.get('/api/audit/:hostname', (req, res) => {
const reportPath = `./Reports/Security-Audit-Report-*.json`;
const report = JSON.parse(fs.readFileSync(reportPath, 'utf8'));
if (report.ExecutionInfo.ComputerName === req.params.hostname) {
res.json({
hostname: report.ExecutionInfo.ComputerName,
date: report.ExecutionInfo.ScanDate,
compliance: (report.ExecutionInfo.PassCount / report.ExecutionInfo.TotalChecks * 100).toFixed(1),
failures: report.Results.filter(r => r.Status === 'Fail')
});
} else {
res.status(404).send('Report not found');
}
});
app.listen(3000);from elasticsearch import Elasticsearch
import json
es = Elasticsearch(['http://localhost:9200'])
with open('Security-Audit-Report-20241230.json') as f:
report = json.load(f)
# Index document
es.index(
index='security-audits',
document={
'hostname': report['ExecutionInfo']['ComputerName'],
'audit_date': report['ExecutionInfo']['ScanDate'],
'results': report['Results'],
'summary': {
'total': report['ExecutionInfo']['TotalChecks'],
'pass': report['ExecutionInfo']['PassCount'],
'fail': report['ExecutionInfo']['FailCount'],
'compliance': (report['ExecutionInfo']['PassCount'] /
report['ExecutionInfo']['TotalChecks'] * 100)
}
}
)| Purpose | Retention | Reasoning |
|---|---|---|
| Active Analysis | 30 days | Recent trend analysis |
| Compliance Evidence | 1-3 years | Audit requirements |
| Historical Trending | 1 year monthly | Long-term metrics |
| Baseline Snapshots | Indefinite | Gold image validation |
# Compress old reports (>90 days)
$oldReports = Get-ChildItem ".\Security-Audit-Report-*" |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-90) }
$archiveName = "SecurityAudits_Archive_$(Get-Date -Format 'yyyyMM').zip"
Compress-Archive -Path $oldReports -DestinationPath ".\Archives\$archiveName"
# Delete originals after successful compression
if (Test-Path ".\Archives\$archiveName") {
$oldReports | Remove-Item
}Typical sizes per audit:
- HTML: 300-600 KB
- JSON: 100-300 KB
- CSV: 50-150 KB
- XML: 150-400 KB
- Total: ~600 KB - 1.6 MB
Annual storage (weekly audits):
- 52 weeks × ~1 MB = ~52 MB per system
- 100 systems × 52 MB = ~5 GB per year
✅ Do:
- Store reports in secured location with access controls
- Consider encrypting sensitive reports
- Implement proper file permissions
- Audit access to report directories
❌ Don't:
- Email reports unencrypted to large distribution lists
- Store reports in publicly accessible shares
- Commit reports to version control
- Share reports outside organization without review
Recommended folder structure:
Reports/
├── Active/ # Last 30 days
├── Monthly/ # Monthly baseline snapshots
├── Archives/ # Compressed older reports
└── Remediation/ # Tracking spreadsheets
# Automated weekly report generation and archival
$scriptPath = "C:\SecurityAudit\Windows-Security-Audit.ps1"
$outputPath = "\\FileServer\Security\Audits\$env:COMPUTERNAME"
# Run audit
& $scriptPath -OutputPath "$outputPath\Report.html"
# Copy to monthly snapshot if first week of month
if ((Get-Date).Day -le 7) {
$monthlyPath = "\\FileServer\Security\Monthly\$env:COMPUTERNAME"
New-Item -ItemType Directory -Force -Path $monthlyPath | Out-Null
Copy-Item "$outputPath\Security-Audit-Report-*.html" -Destination $monthlyPath
}
# Email summary to security team
$report = Get-Content "$outputPath\Security-Audit-Report-*.json" -Raw | ConvertFrom-Json
if ($report.ExecutionInfo.FailCount -gt 10) {
Send-MailMessage -To "security@company.com" `
-Subject "Security Audit Alert - $env:COMPUTERNAME" `
-Body "Audit found $($report.ExecutionInfo.FailCount) failures" `
-SmtpServer "smtp.company.com"
}Related Documentation:
- Usage Guide - How to generate reports
- Troubleshooting - Report generation issues
- Development Guide - Adding new output formats