Add 12 AI-generated PowerShell scripts with documentation
⚠️ IMPORTANT: These scripts are AI-GENERATED and UNTESTED Exchange Scripts (5): - Get-MailboxPermissions.ps1: Audit delegate access permissions - Get-InactiveMailboxes.ps1: Identify stale mailboxes - Compare-MailboxDatabases.ps1: Database health comparison - Export-DistributionGroups.ps1: Distribution group inventory - Get-MailflowStats.ps1: Transport log analysis Active Directory Scripts (3): - Get-ADUserLastLogon.ps1: True LastLogon across all DCs - Export-OUStructure.ps1: OU hierarchy with GPO links - Compare-ADGroupMemberships.ps1: Compare user group memberships System Maintenance Scripts (4): - Get-ServerInventory.ps1: Hardware/software inventory report - Monitor-DiskSpace.ps1: Disk space monitoring with alerts - Backup-ExchangeCertificates.ps1: Certificate backup to PFX - Test-ExchangeHealth.ps1: Aggregated Exchange health checks Documentation: - Updated CLAUDE.md with AI-generated scripts section - Added AI-GENERATED-SCRIPTS.md with warnings and testing guide All scripts include prominent warnings and follow established patterns from existing scripts. Require thorough testing before production use. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
238
Misc/Get-ServerInventory.ps1
Normal file
238
Misc/Get-ServerInventory.ps1
Normal file
@ -0,0 +1,238 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Comprehensive server hardware and software inventory report
|
||||
|
||||
.DESCRIPTION
|
||||
Collects system information including CPU, RAM, disk, OS version, installed roles,
|
||||
and key services. Generates Word document (with HTML fallback) similar to
|
||||
Exchange-Inventory.ps1 structure.
|
||||
|
||||
.PARAMETER OutputFolder
|
||||
Destination folder for reports. Default: .\ServerInventory-<date>
|
||||
|
||||
.PARAMETER IncludeInstalledSoftware
|
||||
Include installed software list (can be slow, default: $false)
|
||||
|
||||
.PARAMETER IncludeWindowsFeatures
|
||||
Include Windows Features/Roles (default: $true)
|
||||
|
||||
.NOTES
|
||||
⚠️ AI-GENERATED SCRIPT - UNTESTED
|
||||
This script was generated by Claude AI and has not been tested in production.
|
||||
Review and test thoroughly in a non-production environment before use.
|
||||
|
||||
- Run with Administrator privileges for complete information
|
||||
- Works on Windows Server 2012+
|
||||
- Report generation uses Word COM or HTML fallback
|
||||
|
||||
.EXAMPLE
|
||||
.\Get-ServerInventory.ps1
|
||||
|
||||
.EXAMPLE
|
||||
.\Get-ServerInventory.ps1 -IncludeInstalledSoftware $true -OutputFolder "D:\Inventory"
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$OutputFolder = (Join-Path -Path (Get-Location) -ChildPath ("ServerInventory-" + (Get-Date -Format "yyyyMMdd-HHmm"))),
|
||||
[bool]$IncludeInstalledSoftware = $false,
|
||||
[bool]$IncludeWindowsFeatures = $true
|
||||
)
|
||||
|
||||
function NowTag { (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") }
|
||||
|
||||
Write-Host "[$(NowTag)] ⚠️ AI-GENERATED SCRIPT - UNTESTED" -ForegroundColor Yellow
|
||||
Write-Host "[$(NowTag)] Starting server inventory..." -ForegroundColor Green
|
||||
|
||||
# Create output folder
|
||||
New-Item -ItemType Directory -Path $OutputFolder -Force | Out-Null
|
||||
|
||||
$ReportDocx = Join-Path $OutputFolder "Server-Inventory.docx"
|
||||
$ReportHtml = Join-Path $OutputFolder "Server-Inventory.html"
|
||||
|
||||
# Word COM or HTML fallback (reused pattern from Exchange-Inventory.ps1)
|
||||
$script:Word = $null
|
||||
$script:Doc = $null
|
||||
$script:UseHtml = $false
|
||||
$script:HtmlSb = New-Object System.Text.StringBuilder
|
||||
|
||||
function Start-Report {
|
||||
try {
|
||||
$script:Word = New-Object -ComObject Word.Application -ErrorAction Stop
|
||||
$script:Word.Visible = $false
|
||||
$script:Doc = $script:Word.Documents.Add()
|
||||
Add-Heading "Server Inventory Report" 1
|
||||
Add-Paragraph ("Server: $env:COMPUTERNAME")
|
||||
Add-Paragraph ("Generated: " + (Get-Date).ToString("yyyy-MM-dd HH:mm"))
|
||||
} catch {
|
||||
$script:UseHtml = $true
|
||||
[void]$script:HtmlSb.AppendLine("<html><head><meta charset='utf-8'><title>Server Inventory</title>")
|
||||
[void]$script:HtmlSb.AppendLine("<style>body{font-family:Segoe UI,Arial,sans-serif;margin:20px} h1{font-size:24px;color:#0066cc} h2{font-size:20px;margin-top:30px;color:#333} table{border-collapse:collapse;width:100%;margin:15px 0} th,td{border:1px solid #ccc;padding:8px;text-align:left} th{background:#f3f3f3;font-weight:bold}</style></head><body>")
|
||||
[void]$script:HtmlSb.AppendLine("<h1>Server Inventory Report</h1>")
|
||||
[void]$script:HtmlSb.AppendLine("<p><strong>Server:</strong> $env:COMPUTERNAME</p>")
|
||||
[void]$script:HtmlSb.AppendLine("<p><strong>Generated:</strong> " + (Get-Date).ToString("yyyy-MM-dd HH:mm") + "</p>")
|
||||
}
|
||||
}
|
||||
|
||||
function Add-Heading([string]$Text, [int]$Level = 2) {
|
||||
if ($script:UseHtml) {
|
||||
[void]$script:HtmlSb.AppendLine("<h$Level>$Text</h$Level>")
|
||||
} else {
|
||||
$range = $script:Doc.Range()
|
||||
$range.Collapse(0) | Out-Null
|
||||
$range.Text = "$Text`r"
|
||||
$style = switch ($Level) { 1 { 'Heading 1' } 2 { 'Heading 2' } default { 'Heading 2' } }
|
||||
$range.set_Style($style)
|
||||
$range.InsertParagraphAfter() | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
function Add-Paragraph([string]$Text) {
|
||||
if ($script:UseHtml) {
|
||||
$enc = [System.Web.HttpUtility]::HtmlEncode($Text)
|
||||
[void]$script:HtmlSb.AppendLine("<p>$enc</p>")
|
||||
} else {
|
||||
$range = $script:Doc.Range()
|
||||
$range.Collapse(0) | Out-Null
|
||||
$range.Text = $Text
|
||||
$range.InsertParagraphAfter() | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
function Add-Table([object]$Objects, [string[]]$PropOrder, [string]$Title) {
|
||||
if ($null -eq $Objects) {
|
||||
Add-Paragraph ("{0}: No data." -f $Title)
|
||||
return
|
||||
}
|
||||
$arr = @()
|
||||
if ($Objects -is [System.Collections.IEnumerable] -and -not ($Objects -is [string])) {
|
||||
foreach ($o in $Objects) { $arr += $o }
|
||||
} else { $arr = @($Objects) }
|
||||
if ($arr.Count -eq 0) {
|
||||
Add-Paragraph ("{0}: No data." -f $Title)
|
||||
return
|
||||
}
|
||||
Add-Heading $Title 3
|
||||
|
||||
if ($script:UseHtml) {
|
||||
[void]$script:HtmlSb.AppendLine("<table><thead><tr>")
|
||||
foreach ($p in $PropOrder) { [void]$script:HtmlSb.AppendLine("<th>$([System.Web.HttpUtility]::HtmlEncode($p))</th>") }
|
||||
[void]$script:HtmlSb.AppendLine("</tr></thead><tbody>")
|
||||
foreach ($o in $arr) {
|
||||
[void]$script:HtmlSb.AppendLine("<tr>")
|
||||
foreach ($p in $PropOrder) {
|
||||
$val = if ($o.$p) { [string]$o.$p } else { "" }
|
||||
[void]$script:HtmlSb.AppendLine("<td>$([System.Web.HttpUtility]::HtmlEncode($val))</td>")
|
||||
}
|
||||
[void]$script:HtmlSb.AppendLine("</tr>")
|
||||
}
|
||||
[void]$script:HtmlSb.AppendLine("</tbody></table>")
|
||||
} else {
|
||||
$rows = $arr.Count
|
||||
$cols = $PropOrder.Count
|
||||
$range = $script:Doc.Range()
|
||||
$table = $script:Doc.Tables.Add($range, [Math]::Max(1, $rows) + 1, $cols)
|
||||
for ($c = 1; $c -le $cols; $c++) { $table.Cell(1, $c).Range.Text = $PropOrder[$c - 1] }
|
||||
$table.Rows.Item(1).Range.Bold = $true
|
||||
for ($r = 0; $r -lt $rows; $r++) {
|
||||
for ($c = 0; $c -lt $cols; $c++) {
|
||||
$val = if ($arr[$r].($PropOrder[$c])) { [string]$arr[$r].($PropOrder[$c]) } else { "" }
|
||||
$table.Cell($r + 2, $c + 1).Range.Text = $val
|
||||
}
|
||||
}
|
||||
$table.AutoFitBehavior(2) | Out-Null
|
||||
$range.InsertParagraphAfter() | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
function End-Report {
|
||||
if ($script:UseHtml) {
|
||||
[void]$script:HtmlSb.AppendLine("</body></html>")
|
||||
$script:HtmlSb.ToString() | Out-File -LiteralPath $ReportHtml -Encoding UTF8
|
||||
Write-Host "[$(NowTag)] HTML report: $ReportHtml" -ForegroundColor Green
|
||||
} else {
|
||||
$wdFormatXMLDocument = 12
|
||||
$script:Doc.SaveAs([ref]$ReportDocx, [ref]$wdFormatXMLDocument)
|
||||
$script:Doc.Close(); $script:Word.Quit()
|
||||
Write-Host "[$(NowTag)] Word report: $ReportDocx" -ForegroundColor Green
|
||||
}
|
||||
}
|
||||
|
||||
Start-Report
|
||||
|
||||
# System Information
|
||||
Write-Host "[$(NowTag)] Collecting system information..."
|
||||
$cs = Get-CimInstance -ClassName Win32_ComputerSystem
|
||||
$os = Get-CimInstance -ClassName Win32_OperatingSystem
|
||||
$bios = Get-CimInstance -ClassName Win32_BIOS
|
||||
|
||||
$sysInfo = [PSCustomObject]@{
|
||||
ComputerName = $cs.Name
|
||||
Domain = $cs.Domain
|
||||
Manufacturer = $cs.Manufacturer
|
||||
Model = $cs.Model
|
||||
TotalPhysicalMemoryGB = [math]::Round($cs.TotalPhysicalMemory / 1GB, 2)
|
||||
NumberOfProcessors = $cs.NumberOfProcessors
|
||||
NumberOfLogicalProcessors = $cs.NumberOfLogicalProcessors
|
||||
OSName = $os.Caption
|
||||
OSVersion = $os.Version
|
||||
OSArchitecture = $os.OSArchitecture
|
||||
InstallDate = $os.InstallDate
|
||||
LastBootUpTime = $os.LastBootUpTime
|
||||
BIOSVersion = $bios.SMBIOSBIOSVersion
|
||||
SerialNumber = $bios.SerialNumber
|
||||
}
|
||||
|
||||
Add-Heading "System Information" 2
|
||||
Add-Table $sysInfo @("ComputerName", "Domain", "Manufacturer", "Model", "TotalPhysicalMemoryGB", "NumberOfProcessors", "NumberOfLogicalProcessors", "OSName", "OSVersion", "OSArchitecture", "InstallDate", "LastBootUpTime", "BIOSVersion", "SerialNumber") "System Details"
|
||||
|
||||
# CPU Information
|
||||
Write-Host "[$(NowTag)] Collecting CPU information..."
|
||||
$cpus = Get-CimInstance -ClassName Win32_Processor | Select-Object Name, NumberOfCores, NumberOfLogicalProcessors, MaxClockSpeed, CurrentClockSpeed
|
||||
Add-Table $cpus @("Name", "NumberOfCores", "NumberOfLogicalProcessors", "MaxClockSpeed", "CurrentClockSpeed") "Processors"
|
||||
|
||||
# Disk Information
|
||||
Write-Host "[$(NowTag)] Collecting disk information..."
|
||||
$disks = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3" | Select-Object @{N = "Drive"; E = { $_.DeviceID } }, @{N = "SizeGB"; E = { [math]::Round($_.Size / 1GB, 2) } }, @{N = "FreeGB"; E = { [math]::Round($_.FreeSpace / 1GB, 2) } }, @{N = "UsedGB"; E = { [math]::Round(($_.Size - $_.FreeSpace) / 1GB, 2) } }, @{N = "PercentFree"; E = { [math]::Round(($_.FreeSpace / $_.Size) * 100, 2) } }, VolumeName, FileSystem
|
||||
Add-Table $disks @("Drive", "SizeGB", "FreeGB", "UsedGB", "PercentFree", "VolumeName", "FileSystem") "Disk Drives"
|
||||
|
||||
# Network Adapters
|
||||
Write-Host "[$(NowTag)] Collecting network adapter information..."
|
||||
$nics = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled=True" | Select-Object Description, @{N = "IPAddress"; E = { $_.IPAddress -join ", " } }, @{N = "SubnetMask"; E = { $_.IPSubnet -join ", " } }, @{N = "DefaultGateway"; E = { $_.DefaultIPGateway -join ", " } }, @{N = "DNSServers"; E = { $_.DNSServerSearchOrder -join ", " } }, MACAddress, DHCPEnabled
|
||||
Add-Table $nics @("Description", "IPAddress", "SubnetMask", "DefaultGateway", "DNSServers", "MACAddress", "DHCPEnabled") "Network Adapters"
|
||||
|
||||
# Windows Features/Roles
|
||||
if ($IncludeWindowsFeatures) {
|
||||
Write-Host "[$(NowTag)] Collecting Windows features..."
|
||||
try {
|
||||
Import-Module ServerManager -ErrorAction Stop
|
||||
$features = Get-WindowsFeature | Where-Object Installed | Select-Object Name, DisplayName, FeatureType
|
||||
Add-Table $features @("Name", "DisplayName", "FeatureType") "Installed Windows Features/Roles"
|
||||
} catch {
|
||||
Add-Paragraph "Windows Features: Could not retrieve (ServerManager module not available)"
|
||||
}
|
||||
}
|
||||
|
||||
# Services
|
||||
Write-Host "[$(NowTag)] Collecting critical services..."
|
||||
$services = Get-Service | Where-Object { $_.StartType -eq "Automatic" -and $_.Status -ne "Running" } | Select-Object Name, DisplayName, Status, StartType
|
||||
if ($services) {
|
||||
Add-Table $services @("Name", "DisplayName", "Status", "StartType") "Automatic Services Not Running (Potential Issues)"
|
||||
} else {
|
||||
Add-Paragraph "All automatic services are running"
|
||||
}
|
||||
|
||||
# Installed Software (optional, can be slow)
|
||||
if ($IncludeInstalledSoftware) {
|
||||
Write-Host "[$(NowTag)] Collecting installed software (this may take a while)..."
|
||||
$software = Get-CimInstance -ClassName Win32_Product | Select-Object Name, Version, Vendor, InstallDate | Sort-Object Name
|
||||
Add-Table $software @("Name", "Version", "Vendor", "InstallDate") "Installed Software"
|
||||
}
|
||||
|
||||
# Hotfixes
|
||||
Write-Host "[$(NowTag)] Collecting installed hotfixes..."
|
||||
$hotfixes = Get-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 20 | Select-Object HotFixID, Description, InstalledBy, InstalledOn
|
||||
Add-Table $hotfixes @("HotFixID", "Description", "InstalledBy", "InstalledOn") "Recent Hotfixes (Last 20)"
|
||||
|
||||
End-Report
|
||||
Write-Host "[$(NowTag)] Inventory complete! Output folder: $OutputFolder"
|
||||
Reference in New Issue
Block a user