Add server lifecycle audit PowerShell script#47
Add server lifecycle audit PowerShell script#47AlrightLad wants to merge 1 commit intoDTC-Inc:mainfrom
Conversation
This script performs a comprehensive audit of server lifecycle aspects including OS details, domain roles, CPU, memory, disk layout, server roles, SQL instances, installed software, running services, file shares, printers, network configuration, DNS settings, DHCP scopes, RDS licensing, scheduled tasks, and Hyper-V VMs.
📝 WalkthroughWalkthroughA new PowerShell audit script has been added that inventories and reports system and infrastructure details including host configuration, domain roles, hardware metrics, installed roles and software, network configuration, and virtualization state via structured queries. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
msft-windows/msft-windows-vm-lifecycle-audit (1)
1-5: Add transcript logging and an explicit RMM simulation path.The script currently has no transcript lifecycle, which makes audit traceability harder during field execution and validation.
Proposed logging/testability addition
+# Transcript path under %WINDIR%\logs +$logDir = Join-Path $env:WINDIR "logs" +$logFile = Join-Path $logDir ("vm-lifecycle-audit-{0:yyyyMMdd-HHmmss}.log" -f (Get-Date)) +if (-not (Test-Path $logDir)) { New-Item -Path $logDir -ItemType Directory -Force | Out-Null } +Start-Transcript -Path $logFile -Force + +# Optional RMM simulation switch +$RMM = 0 +# Set $RMM = 1 and predefine required variables during testing + try { # existing audit logic... } finally { + Stop-Transcript | Out-Null }Based on learnings: “Verify transcripts/logs in %WINDIR%\logs after execution; support RMM simulation by setting $RMM=1 and predefining required variables during testing”.
Also applies to: 65-65
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@msft-windows/msft-windows-vm-lifecycle-audit` around lines 1 - 5, Add PowerShell transcript start/stop around the main script execution by invoking Start-Transcript and Stop-Transcript and write the transcript to $env:windir\Logs with a timestamped filename so audit traces land in %WINDIR%\Logs; also add explicit test-mode/RMM simulation support by declaring and checking a $RMM variable (e.g., default $RMM = 0) and, when $RMM -eq 1, predefine or mock required input variables and skip any destructive operations to allow running under RMM testing; ensure Start-Transcript is invoked early (before major actions) and Stop-Transcript in a finally/cleanup path so transcripts are always closed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@msft-windows/msft-windows-vm-lifecycle-audit`:
- Around line 1-65: The script lacks the required three-part PowerShell template
(RMM variable declaration, input handling, script logic) so preserve backward
compatibility by adding the RMM variable declaration block and input parsing
before any immediate execution (before the first Write-Host), implement the
input handling/parameter mapping that mirrors script-template-powershell.ps1
(declare expected RMM variables and parse parameters/env), and then move the
existing audit commands into a clearly delimited "script logic" section;
reference the template name script-template-powershell.ps1 and the existing
top-level execution points (the initial Write-Host and subsequent audit command
blocks) to locate where to insert the RMM variables and input handling.
- Around line 12-14: The netdom call in the FSMO block (netdom query fsmo)
currently relies on try/catch which won't catch non-zero exit codes from the
native executable; update the block that calls netdom to capture its output,
examine $LASTEXITCODE immediately after the call, and handle non-zero values by
logging a clear message via Write-Host (including the captured output/error)
instead of silently relying on the catch. Ensure the modified logic still
handles the non-DC case and unavailability of netdom by checking for
$LASTEXITCODE and/or specific error text and emitting a concise failure message.
---
Nitpick comments:
In `@msft-windows/msft-windows-vm-lifecycle-audit`:
- Around line 1-5: Add PowerShell transcript start/stop around the main script
execution by invoking Start-Transcript and Stop-Transcript and write the
transcript to $env:windir\Logs with a timestamped filename so audit traces land
in %WINDIR%\Logs; also add explicit test-mode/RMM simulation support by
declaring and checking a $RMM variable (e.g., default $RMM = 0) and, when $RMM
-eq 1, predefine or mock required input variables and skip any destructive
operations to allow running under RMM testing; ensure Start-Transcript is
invoked early (before major actions) and Stop-Transcript in a finally/cleanup
path so transcripts are always closed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: aec60540-d5ba-453e-a146-58ab3c2fd743
📒 Files selected for processing (1)
msft-windows/msft-windows-vm-lifecycle-audit
| # ===== SERVER REPLACEMENT AUDIT ===== | ||
| # Server Lifecycle - Quote Validation Audit SOP (BookStack Page 1787) | ||
| # Run on EACH server/VM being evaluated for replacement. | ||
| # Paste into elevated PowerShell session or push via NinjaOne. | ||
|
|
||
| Write-Host "===== HOSTNAME & OS =====" -ForegroundColor Cyan | ||
| Get-CimInstance Win32_OperatingSystem | Select-Object CSName, Caption, Version, InstallDate | Format-List | ||
|
|
||
| Write-Host "===== DOMAIN ROLE =====" -ForegroundColor Cyan | ||
| Get-CimInstance Win32_ComputerSystem | Select-Object Name, Domain, DomainRole, PartOfDomain | Format-List | ||
|
|
||
| Write-Host "===== AD FSMO ROLES (if DC) =====" -ForegroundColor Cyan | ||
| try { netdom query fsmo 2>&1 } catch { Write-Host "Not a DC or netdom unavailable" } | ||
|
|
||
| Write-Host "===== CPU =====" -ForegroundColor Cyan | ||
| Get-CimInstance Win32_Processor | Select-Object Name, NumberOfCores, NumberOfLogicalProcessors, MaxClockSpeed | Format-List | ||
|
|
||
| Write-Host "===== MEMORY =====" -ForegroundColor Cyan | ||
| $os = Get-CimInstance Win32_OperatingSystem | ||
| [PSCustomObject]@{TotalGB=[math]::Round($os.TotalVisibleMemorySize/1MB,1);FreeGB=[math]::Round($os.FreePhysicalMemory/1MB,1);UsedGB=[math]::Round(($os.TotalVisibleMemorySize-$os.FreePhysicalMemory)/1MB,1);CommittedGB=[math]::Round(($os.TotalVirtualMemorySize-$os.FreeVirtualMemory)/1MB,1)} | Format-List | ||
|
|
||
| Write-Host "===== DISK LAYOUT =====" -ForegroundColor Cyan | ||
| Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | Select-Object DeviceID, @{N='SizeGB';E={[math]::Round($_.Size/1GB,1)}}, @{N='FreeGB';E={[math]::Round($_.FreeSpace/1GB,1)}}, @{N='UsedGB';E={[math]::Round(($_.Size-$_.FreeSpace)/1GB,1)}} | Format-Table -AutoSize | ||
|
|
||
| Write-Host "===== PHYSICAL DISK CONTROLLER =====" -ForegroundColor Cyan | ||
| Get-CimInstance Win32_SCSIController | Select-Object Name, DriverName | Format-Table -AutoSize | ||
|
|
||
| Write-Host "===== SERVER ROLES =====" -ForegroundColor Cyan | ||
| try { Get-WindowsFeature | Where-Object { $_.Installed -eq $true -and $_.FeatureType -eq 'Role' } | Select-Object Name, DisplayName | Format-Table -AutoSize } catch { Write-Host "Get-WindowsFeature not available" } | ||
|
|
||
| Write-Host "===== SQL SERVER INSTANCES =====" -ForegroundColor Cyan | ||
| Get-Service | Where-Object { $_.Name -match 'MSSQL' -or $_.Name -match 'SQL' } | Select-Object Name, DisplayName, Status, StartType | Format-Table -AutoSize | ||
|
|
||
| Write-Host "===== INSTALLED SOFTWARE (filtered) =====" -ForegroundColor Cyan | ||
| Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*, HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -and $_.DisplayName -notmatch 'Update|Hotfix|KB\d|Visual C\+\+|\.NET' } | Select-Object DisplayName, DisplayVersion, Publisher | Sort-Object DisplayName | Format-Table -AutoSize | ||
|
|
||
| Write-Host "===== RUNNING SERVICES (non-Microsoft, Auto-start) =====" -ForegroundColor Cyan | ||
| Get-CimInstance Win32_Service | Where-Object { $_.StartMode -eq 'Auto' -and $_.PathName -notmatch 'Windows|Microsoft|svchost' } | Select-Object Name, DisplayName, State, PathName | Format-Table -AutoSize | ||
|
|
||
| Write-Host "===== FILE SHARES =====" -ForegroundColor Cyan | ||
| Get-SmbShare | Where-Object { $_.Name -notmatch '^\$|^ADMIN\$|^IPC\$|^print\$' } | Select-Object Name, Path, Description | Format-Table -AutoSize | ||
|
|
||
| Write-Host "===== SHARED PRINTERS =====" -ForegroundColor Cyan | ||
| Get-Printer -ErrorAction SilentlyContinue | Where-Object { $_.Shared -eq $true } | Select-Object Name, DriverName, PortName, Shared | Format-Table -AutoSize | ||
|
|
||
| Write-Host "===== NETWORK CONFIG =====" -ForegroundColor Cyan | ||
| Get-NetAdapter | Select-Object Name, InterfaceDescription, Status, LinkSpeed | Format-Table -AutoSize | ||
| Get-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.IPAddress -notmatch '^127' } | Select-Object InterfaceAlias, IPAddress, PrefixLength | Format-Table -AutoSize | ||
|
|
||
| Write-Host "===== DNS SERVER SETTINGS (if DC) =====" -ForegroundColor Cyan | ||
| try { Get-DnsServerZone -ErrorAction Stop | Select-Object ZoneName, ZoneType, IsReverseLookupZone | Format-Table -AutoSize } catch { Write-Host "DNS Server role not installed" } | ||
|
|
||
| Write-Host "===== DHCP SCOPES (if DHCP server) =====" -ForegroundColor Cyan | ||
| try { Get-DhcpServerv4Scope -ErrorAction Stop | Select-Object ScopeId, Name, StartRange, EndRange, SubnetMask, State | Format-Table -AutoSize } catch { Write-Host "DHCP Server role not installed" } | ||
|
|
||
| Write-Host "===== RDS LICENSING =====" -ForegroundColor Cyan | ||
| try { Get-RDLicenseConfiguration -ErrorAction Stop | Format-List } catch { Write-Host "RDS Licensing not configured" } | ||
|
|
||
| Write-Host "===== SCHEDULED TASKS (non-Microsoft) =====" -ForegroundColor Cyan | ||
| Get-ScheduledTask | Where-Object { $_.TaskPath -notmatch '\\Microsoft\\' -and $_.State -ne 'Disabled' } | Select-Object TaskName, TaskPath, State | Format-Table -AutoSize | ||
|
|
||
| Write-Host "===== HYPER-V VMs (if any) =====" -ForegroundColor Cyan | ||
| try { Get-VM -ErrorAction Stop | Select-Object Name, State, MemoryAssigned, ProcessorCount | Format-Table -AutoSize } catch { Write-Host "Hyper-V not installed" } | ||
|
|
||
| Write-Host "===== AUDIT COMPLETE =====" -ForegroundColor Green |
There was a problem hiding this comment.
Adopt the required 3-part PowerShell script template before merge.
This new script goes straight into execution logic and omits the required sections for RMM variable declaration and input handling, which risks incompatibility with existing automation paths.
Proposed structure scaffold
+# =========================
+# 1) RMM variable declaration
+# =========================
+# Example: preserve existing RMM variable names used by the template/repo
+$RMM = $env:RMM
+
+# =========================
+# 2) Input handling
+# =========================
+param(
+ [switch]$RunAsRmm
+)
+
+# =========================
+# 3) Script logic
+# =========================
Write-Host "===== HOSTNAME & OS =====" -ForegroundColor Cyan
...
Write-Host "===== AUDIT COMPLETE =====" -ForegroundColor GreenBased on learnings: “All PowerShell scripts must follow the three-part structure from script-template-powershell.ps1 (RMM variable declaration, input handling, script logic)” and “When modifying scripts, preserve the three-section structure and maintain backward compatibility with existing RMM variable names”.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@msft-windows/msft-windows-vm-lifecycle-audit` around lines 1 - 65, The script
lacks the required three-part PowerShell template (RMM variable declaration,
input handling, script logic) so preserve backward compatibility by adding the
RMM variable declaration block and input parsing before any immediate execution
(before the first Write-Host), implement the input handling/parameter mapping
that mirrors script-template-powershell.ps1 (declare expected RMM variables and
parse parameters/env), and then move the existing audit commands into a clearly
delimited "script logic" section; reference the template name
script-template-powershell.ps1 and the existing top-level execution points (the
initial Write-Host and subsequent audit command blocks) to locate where to
insert the RMM variables and input handling.
| Write-Host "===== AD FSMO ROLES (if DC) =====" -ForegroundColor Cyan | ||
| try { netdom query fsmo 2>&1 } catch { Write-Host "Not a DC or netdom unavailable" } | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In PowerShell 5.1 and 7.x, does try/catch catch non-zero exit codes from native executables (like netdom), or only terminating PowerShell errors? Please cite official docs.
💡 Result:
No, in PowerShell 5.1 and 7.x (up to 7.5 as of 2026-03-24), try/catch does not catch non-zero exit codes from native executables like netdom by default. These exit codes do not generate terminating PowerShell errors that try/catch can handle. Instead, check the $LASTEXITCODE automatic variable after running the executable. To handle native command errors in try/catch in PowerShell 7.3+, opt-in by setting $PSNativeCommandUseErrorActionPreference = $true (defaults to $false) and $ErrorActionPreference = 'Stop'. Then non-zero exit codes produce catchable errors (NativeCommandExitException). This is a mainstream feature since 7.4 but requires explicit enabling. Official docs confirm try/catch handles terminating errors/exceptions (about_Try_Catch_Finally), not native exit codes directly. about_Preference_Variables documents $PSNativeCommandUseErrorActionPreference.
Citations:
- 1: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_try_catch_finally?view=powershell-7.5
- 2: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_try_catch_finally?view=powershell-7.4
- 3: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_try_catch_finally?view=powershell-5.1
- 4: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables?view=powershell-7.5
- 5: https://stackoverflow.com/questions/67743279/how-to-get-exit-code-from-powershell-script
- 6: https://stackoverflow.com/questions/12359427/try-catch-on-executable-exe-in-powershell
- 7: https://www.codegenes.net/blog/try-catch-on-executable-exe-in-powershell/
🏁 Script executed:
#!/bin/bash
# Check if the file exists and read lines around 12-14
find . -type f -name "*msft-windows-vm-lifecycle-audit*" 2>/dev/nullRepository: DTC-Inc/msp-script-library
Length of output: 116
🏁 Script executed:
#!/bin/bash
# Read the file to check content around lines 12-14
head -20 ./msft-windows/msft-windows-vm-lifecycle-auditRepository: DTC-Inc/msp-script-library
Length of output: 1366
Harden netdom error handling in the FSMO block.
Line 13 uses try/catch around a native executable call; this can miss failure states. In PowerShell 5.1 and 7.x, try/catch does not catch non-zero exit codes from native executables—you must explicitly check $LASTEXITCODE after the call.
Proposed robust FSMO query block
Write-Host "===== AD FSMO ROLES (if DC) =====" -ForegroundColor Cyan
-try { netdom query fsmo 2>&1 } catch { Write-Host "Not a DC or netdom unavailable" }
+if (Get-Command netdom -ErrorAction SilentlyContinue) {
+ $fsmo = & netdom query fsmo 2>&1
+ if ($LASTEXITCODE -eq 0) {
+ $fsmo
+ } else {
+ Write-Host "Not a DC or netdom unavailable"
+ }
+} else {
+ Write-Host "netdom unavailable"
+}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@msft-windows/msft-windows-vm-lifecycle-audit` around lines 12 - 14, The
netdom call in the FSMO block (netdom query fsmo) currently relies on try/catch
which won't catch non-zero exit codes from the native executable; update the
block that calls netdom to capture its output, examine $LASTEXITCODE immediately
after the call, and handle non-zero values by logging a clear message via
Write-Host (including the captured output/error) instead of silently relying on
the catch. Ensure the modified logic still handles the non-DC case and
unavailability of netdom by checking for $LASTEXITCODE and/or specific error
text and emitting a concise failure message.
This script performs a comprehensive audit of server lifecycle aspects including OS details, domain roles, CPU, memory, disk layout, server roles, SQL instances, installed software, running services, file shares, printers, network configuration, DNS settings, DHCP scopes, RDS licensing, scheduled tasks, and Hyper-V VMs.
Summary by CodeRabbit
Release Notes