Private/Get-WUSystemInfo.ps1
|
function Get-WUSystemInfo { <# .SYNOPSIS Gathers comprehensive system information for Windows Update diagnostics. .DESCRIPTION Collects system information relevant to Windows Update operations including OS version, hardware details, disk space, and configuration details. .PARAMETER LogPath Path to the log file for detailed logging. .EXAMPLE $sysInfo = Get-WUSystemInfo -LogPath "C:\Logs\wu.log" .NOTES This is a private function used internally by the WindowsUpdateTools module. Returns a PSCustomObject with comprehensive system information. #> [CmdletBinding()] param( [string]$LogPath ) Write-WULog -Message "Collecting comprehensive system information" -LogPath $LogPath try { # Get basic OS information $os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop $computer = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop $bios = Get-CimInstance -ClassName Win32_BIOS -ErrorAction Stop # Get disk information for system drive $systemDrive = Get-CimInstance -ClassName Win32_LogicalDisk | Where-Object { $_.DeviceID -eq $env:SystemDrive } # Calculate disk space metrics $totalSpaceGB = [math]::Round($systemDrive.Size / 1GB, 2) $freeSpaceGB = [math]::Round($systemDrive.FreeSpace / 1GB, 2) $usedSpaceGB = $totalSpaceGB - $freeSpaceGB $freeSpacePercent = [math]::Round(($freeSpaceGB / $totalSpaceGB) * 100, 1) # Get network adapter information (for connectivity analysis) $networkAdapters = Get-CimInstance -ClassName Win32_NetworkAdapter | Where-Object { $_.NetConnectionStatus -eq 2 } | # Connected adapters only Select-Object Name, MACAddress, Speed # Get Windows Update related registry information $osVersion = [System.Environment]::OSVersion.Version $buildNumber = $os.BuildNumber $releaseId = $null $ubr = $null # Update Build Revision $displayVersion = $null try { # Get release ID, UBR, and accurate build from registry (more reliable than WMI after upgrades) $versionInfo = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -ErrorAction SilentlyContinue if ($versionInfo) { $releaseId = $versionInfo.ReleaseId $ubr = $versionInfo.UBR $displayVersion = $versionInfo.DisplayVersion # Prefer registry CurrentBuild over WMI BuildNumber (WMI can be stale after feature updates) if ($versionInfo.CurrentBuild) { $buildNumber = $versionInfo.CurrentBuild } } } catch { Write-WULog -Message "Could not retrieve version details from registry" -Level Warning -LogPath $LogPath } # Check for Windows Update for Business settings $wufbConfigured = $false try { $wufbKeys = @( "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate", "HKLM:\SOFTWARE\Microsoft\PolicyManager\current\device\Update" ) foreach ($key in $wufbKeys) { if (Test-Path $key) { $wufbConfigured = $true break } } } catch { Write-WULog -Message "Could not check Windows Update for Business configuration" -Level Warning -LogPath $LogPath } # Check for domain membership $domainInfo = "Workgroup" try { if ($computer.PartOfDomain) { $domainInfo = $computer.Domain } } catch { Write-WULog -Message "Could not determine domain membership" -Level Warning -LogPath $LogPath } # Check system uptime $uptime = (Get-Date) - $os.LastBootUpTime # Check for pending reboot $pendingReboot = Test-WUPendingReboot # Determine Windows edition and licensing status $windowsEdition = $os.Caption $licensingStatus = "Unknown" try { $licensingInfo = Get-CimInstance -ClassName SoftwareLicensingProduct | Where-Object { $_.PartialProductKey -and $_.Name -like "*Windows*" } | Select-Object -First 1 if ($licensingInfo) { $licensingStatus = switch ($licensingInfo.LicenseStatus) { 0 { "Unlicensed" } 1 { "Licensed" } 2 { "OOBGrace" } 3 { "OOTGrace" } 4 { "NonGenuineGrace" } 5 { "Notification" } 6 { "ExtendedGrace" } default { "Unknown" } } } } catch { Write-WULog -Message "Could not determine licensing status" -Level Warning -LogPath $LogPath } # Create comprehensive system info object # Build full version string (e.g., "10.0.26200.7171") $fullBuildVersion = if ($ubr) { "$buildNumber.$ubr" } else { $buildNumber } $systemInfo = [PSCustomObject]@{ # Basic system information ComputerName = $computer.Name Manufacturer = $computer.Manufacturer Model = $computer.Model SerialNumber = $bios.SerialNumber BiosVersion = $bios.SMBIOSBIOSVersion # Operating system details OperatingSystem = $windowsEdition OSVersion = $osVersion.ToString() BuildNumber = $buildNumber FullBuildVersion = $fullBuildVersion DisplayVersion = $displayVersion ReleaseId = $releaseId UpdateBuildRevision = $ubr Architecture = $os.OSArchitecture InstallDate = $os.InstallDate LicensingStatus = $licensingStatus # Hardware information TotalRAM_GB = [math]::Round($computer.TotalPhysicalMemory / 1GB, 2) ProcessorCount = $computer.NumberOfLogicalProcessors ProcessorArchitecture = $computer.SystemType # Disk space information SystemDrive = $env:SystemDrive TotalSpace_GB = $totalSpaceGB FreeSpace_GB = $freeSpaceGB UsedSpace_GB = $usedSpaceGB FreeSpacePercent = $freeSpacePercent LowDiskSpace = $freeSpaceGB -lt 10 # Flag if less than 10GB free CriticalDiskSpace = $freeSpaceGB -lt 5 # Flag if less than 5GB free # Network and connectivity NetworkAdapters = $networkAdapters ConnectedAdapterCount = $networkAdapters.Count # System state LastBootTime = $os.LastBootUpTime Uptime_Hours = [math]::Round($uptime.TotalHours, 1) PendingReboot = $pendingReboot # Domain and management DomainMembership = $domainInfo PartOfDomain = $computer.PartOfDomain WUfBConfigured = $wufbConfigured # Time and locale TimeZone = (Get-TimeZone).Id CurrentTime = Get-Date # Additional system flags IsVirtualMachine = Test-WUVirtualMachine IsServerOS = $os.ProductType -ne 1 # 1 = Workstation, 2 = Domain Controller, 3 = Server # Windows 10 EOL detection (End of Support: October 14, 2025) IsWindows10 = $buildNumber -lt 22000 # Windows 11 starts at build 22000 IsWindows11 = $buildNumber -ge 22000 Windows10EOLDate = [DateTime]"2025-10-14" IsWindows10EOL = ($buildNumber -lt 22000) -and ((Get-Date) -gt [DateTime]"2025-10-14") ESUEnrolled = $false # Will be updated below # Windows 11 version lifecycle detection (Home/Pro editions) # Build numbers: 22000=21H2, 22621=22H2, 22631=23H2, 26100=24H2 Windows11Version = $null # Will be set below Windows11VersionEOL = $false # Will be set below Windows11VersionEOLDate = $null # Will be set below # .NET Framework version (important for SetupDiag) DotNetVersion = Get-WUDotNetVersion } # Detect Windows 11 version and check if it's end of servicing if ($systemInfo.IsWindows11) { # Windows 11 version lifecycle dates (Home/Pro - Enterprise/Education get longer support) # https://learn.microsoft.com/en-us/lifecycle/products/windows-11-home-and-pro $win11Lifecycle = @{ '21H2' = @{ Build = 22000; EOL = [DateTime]"2023-10-10" } '22H2' = @{ Build = 22621; EOL = [DateTime]"2024-10-08" } '23H2' = @{ Build = 22631; EOL = [DateTime]"2025-11-11" } '24H2' = @{ Build = 26100; EOL = [DateTime]"2026-10-13" } } # Determine version from DisplayVersion or build number $detectedVersion = $null if ($displayVersion -and $win11Lifecycle.ContainsKey($displayVersion)) { $detectedVersion = $displayVersion } else { # Fall back to build number detection foreach ($ver in $win11Lifecycle.GetEnumerator() | Sort-Object { $_.Value.Build } -Descending) { if ([int]$buildNumber -ge $ver.Value.Build) { $detectedVersion = $ver.Key break } } } if ($detectedVersion) { $systemInfo.Windows11Version = $detectedVersion $systemInfo.Windows11VersionEOLDate = $win11Lifecycle[$detectedVersion].EOL $systemInfo.Windows11VersionEOL = (Get-Date) -gt $win11Lifecycle[$detectedVersion].EOL if ($systemInfo.Windows11VersionEOL) { Write-WULog -Message "Windows 11 $detectedVersion is END OF SERVICING (ended $($win11Lifecycle[$detectedVersion].EOL.ToString('yyyy-MM-dd')))" -Level Warning -LogPath $LogPath Write-WULog -Message "This system will NOT receive security updates - upgrade to a supported version required" -Level Warning -LogPath $LogPath } } } # Check for Windows 10 ESU (Extended Security Updates) enrollment if ($systemInfo.IsWindows10EOL) { try { # ESU is delivered via activation - check for ESU product key $esuProducts = Get-CimInstance -ClassName SoftwareLicensingProduct -ErrorAction SilentlyContinue | Where-Object { $_.Name -like "*ESU*" -and $_.LicenseStatus -eq 1 } if ($esuProducts) { $systemInfo.ESUEnrolled = $true Write-WULog -Message "Windows 10 ESU (Extended Security Updates) is active" -LogPath $LogPath } else { # Also check for ESU via registry (Volume License ESU) $esuRegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ESU" if (Test-Path $esuRegPath) { $esuInfo = Get-ItemProperty -Path $esuRegPath -ErrorAction SilentlyContinue if ($esuInfo.ESU_Activated -eq 1) { $systemInfo.ESUEnrolled = $true Write-WULog -Message "Windows 10 ESU (Extended Security Updates) detected via registry" -LogPath $LogPath } } } if (-not $systemInfo.ESUEnrolled) { Write-WULog -Message "Windows 10 is END OF LIFE (Oct 14, 2025) - No ESU subscription detected" -Level Warning -LogPath $LogPath Write-WULog -Message "This system will NOT receive security updates without ESU or upgrade to Windows 11" -Level Warning -LogPath $LogPath } } catch { Write-WULog -Message "Could not determine ESU enrollment status: $($_.Exception.Message)" -Level Warning -LogPath $LogPath } } # Log key information Write-WULog -Message "System: $($systemInfo.Manufacturer) $($systemInfo.Model)" -LogPath $LogPath $osDisplayVersion = if ($systemInfo.DisplayVersion) { " ($($systemInfo.DisplayVersion))" } else { "" } Write-WULog -Message "OS: $($systemInfo.OperatingSystem) Build $($systemInfo.FullBuildVersion)$osDisplayVersion" -LogPath $LogPath Write-WULog -Message "RAM: $($systemInfo.TotalRAM_GB) GB, Disk Free: $($systemInfo.FreeSpace_GB) GB ($($systemInfo.FreeSpacePercent)%)" -LogPath $LogPath Write-WULog -Message "Uptime: $($systemInfo.Uptime_Hours) hours, Domain: $($systemInfo.DomainMembership)" -LogPath $LogPath if ($systemInfo.LowDiskSpace) { Write-WULog -Message "Low disk space detected ($($systemInfo.FreeSpace_GB) GB free)" -Level Warning -LogPath $LogPath } if ($systemInfo.PendingReboot) { Write-WULog -Message "Pending reboot detected" -Level Warning -LogPath $LogPath } return $systemInfo } catch { Write-WULog -Message "Error collecting system information: $($_.Exception.Message)" -Level Error -LogPath $LogPath # Return minimal system info object on error return [PSCustomObject]@{ ComputerName = $env:COMPUTERNAME OperatingSystem = "Unknown" OSVersion = "Unknown" BuildNumber = "Unknown" Architecture = "Unknown" TotalRAM_GB = 0 FreeSpace_GB = 0 LastBootTime = $null ErrorOccurred = $true ErrorMessage = $_.Exception.Message } } } function Test-WUPendingReboot { <# .SYNOPSIS Checks if the system has a pending reboot. .DESCRIPTION Checks various registry locations and system state to determine if a reboot is pending. #> try { # Check multiple indicators of pending reboot $pendingReboot = $false # Windows Update pending reboot if (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired") { $pendingReboot = $true } # Component Based Servicing pending reboot if (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending") { $pendingReboot = $true } # Pending file rename operations $pendingFileRenames = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name "PendingFileRenameOperations" -ErrorAction SilentlyContinue if ($pendingFileRenames) { $pendingReboot = $true } # System Center Configuration Manager pending reboot if (Test-Path "HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData") { $pendingReboot = $true } return $pendingReboot } catch { return $false } } function Test-WUVirtualMachine { <# .SYNOPSIS Detects if the system is running in a virtual machine. .DESCRIPTION Checks various system indicators to determine if running in a VM environment. #> try { # Check common VM indicators $computer = Get-CimInstance -ClassName Win32_ComputerSystem $bios = Get-CimInstance -ClassName Win32_BIOS # Common VM manufacturers and models $vmIndicators = @( "Microsoft Corporation", # Hyper-V "VMware", # VMware "VirtualBox", # VirtualBox "Xen", # Xen "QEMU", # QEMU "Parallels", # Parallels "Red Hat", # KVM/RHEV "oVirt" # oVirt ) foreach ($indicator in $vmIndicators) { if ($computer.Manufacturer -like "*$indicator*" -or $computer.Model -like "*$indicator*" -or $bios.Manufacturer -like "*$indicator*") { return $true } } # Check for VM-specific hardware if ($computer.Model -like "*Virtual*" -or $computer.Model -like "*VMware*" -or $computer.Model -like "*VirtualBox*") { return $true } return $false } catch { return $false } } function Get-WUDotNetVersion { <# .SYNOPSIS Gets the installed .NET Framework version. .DESCRIPTION Determines the highest .NET Framework version installed on the system. #> try { $netVersions = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP" -Recurse -ErrorAction SilentlyContinue | Get-ItemProperty -Name Version, Release -ErrorAction SilentlyContinue | Where-Object { $_.PSChildName -match '^(?!S)\d+' } | Sort-Object Version -Descending | Select-Object -First 1 if ($netVersions.Release -ge 528040) { return [Version]"4.8" } elseif ($netVersions.Release -ge 461808) { return [Version]"4.7.2" } elseif ($netVersions.Release -ge 460798) { return [Version]"4.7" } elseif ($netVersions.Release -ge 394802) { return [Version]"4.6.2" } elseif ($netVersions.Release -ge 394254) { return [Version]"4.6.1" } elseif ($netVersions.Release -ge 393295) { return [Version]"4.6" } else { return [Version]$netVersions.Version } } catch { return [Version]"0.0" } } |