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

        try {
            # Get release ID and UBR from registry
            $versionInfo = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -ErrorAction SilentlyContinue
            if ($versionInfo) {
                $releaseId = $versionInfo.ReleaseId
                $ubr = $versionInfo.UBR
            }
        }
        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
        $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
            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
            
            # .NET Framework version (important for SetupDiag)
            DotNetVersion = Get-WUDotNetVersion
        }

        # Log key information
        Write-WULog -Message "System: $($systemInfo.Manufacturer) $($systemInfo.Model)" -LogPath $LogPath
        Write-WULog -Message "OS: $($systemInfo.OperatingSystem) Build $($systemInfo.BuildNumber)" -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"
    }
}