[CmdletBinding()] param( [string]$OutputPath ) $ErrorActionPreference = 'Stop' if ([string]::IsNullOrWhiteSpace($OutputPath)) { $desktop = [Environment]::GetFolderPath('Desktop') if ([string]::IsNullOrWhiteSpace($desktop)) { $desktop = $PSScriptRoot } $OutputPath = Join-Path $desktop ("SystemInformation_{0}.txt" -f (Get-Date -Format 'yyyyMMdd_HHmmss')) } $report = New-Object System.Collections.Generic.List[string] $steps = @( 'General system information' 'Windows version and uptime' 'CPU information' 'Memory information' 'GPU information' 'Network adapters and IP addresses' 'Windows product key' 'Windows update history' 'Writing report' 'Opening report in Notepad' ) $stepNumber = 0 function Add-Line { param([AllowEmptyString()][string]$Text = '') $script:report.Add($Text) } function Add-Section { param([Parameter(Mandatory = $true)][string]$Title) Add-Line Add-Line ('=' * 78) Add-Line $Title.ToUpperInvariant() Add-Line ('=' * 78) } function Start-Step { param([Parameter(Mandatory = $true)][string]$Name) $script:stepNumber++ $percent = [int](($script:stepNumber / $script:steps.Count) * 100) Write-Host ("[{0}/{1}] {2}..." -f $script:stepNumber, $script:steps.Count, $Name) -ForegroundColor Cyan Write-Progress -Activity 'Collecting system information' -Status $Name -PercentComplete $percent } function Add-PropertyLines { param( [Parameter(Mandatory = $true)]$InputObject, [Parameter(Mandatory = $true)][string[]]$Properties ) foreach ($property in $Properties) { $value = $InputObject.$property if ($null -eq $value -or "$value" -eq '') { $value = 'Not available' } Add-Line ("{0,-28}: {1}" -f $property, $value) } } function Invoke-ReportStep { param( [Parameter(Mandatory = $true)][string]$Name, [Parameter(Mandatory = $true)][scriptblock]$Action ) Start-Step $Name try { & $Action Write-Host ' Completed.' -ForegroundColor Green } catch { Add-Line ("Unable to collect this section: {0}" -f $_.Exception.Message) Write-Warning (" {0}" -f $_.Exception.Message) } } function ConvertFrom-DigitalProductId { param([byte[]]$DigitalProductId) if ($null -eq $DigitalProductId -or $DigitalProductId.Length -lt 67) { return $null } $keyCharacters = 'BCDFGHJKMPQRTVWXY2346789' $keyOffset = 52 $isWindows8OrLater = [int](($DigitalProductId[66] / 6) -band 1) $DigitalProductId[66] = [byte](($DigitalProductId[66] -band 0xF7) -bor (($isWindows8OrLater -band 2) * 4)) $decoded = '' $last = 0 for ($i = 24; $i -ge 0; $i--) { $current = 0 for ($j = 14; $j -ge 0; $j--) { $current = ($current * 256) -bxor $DigitalProductId[$j + $keyOffset] $DigitalProductId[$j + $keyOffset] = [byte][math]::Floor($current / 24) $current = $current % 24 } $decoded = $keyCharacters[$current] + $decoded $last = $current } if ($isWindows8OrLater -eq 1) { $decoded = $decoded.Insert($last, 'N') if ($decoded.Length -gt 25) { $decoded = $decoded.Substring(0, 25) } } for ($i = 5; $i -lt $decoded.Length; $i += 6) { $decoded = $decoded.Insert($i, '-') } return $decoded } Add-Line 'SYSTEM INFORMATION REPORT' Add-Line ("Generated : {0}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss K')) Invoke-ReportStep 'General system information' { Add-Section 'General System Information' $computer = Get-CimInstance Win32_ComputerSystem $bios = Get-CimInstance Win32_BIOS Add-Line ("Hostname : {0}" -f $env:COMPUTERNAME) Add-Line ("Logged-in user : {0}" -f $(if ($computer.UserName) { $computer.UserName } else { "$env:USERDOMAIN\$env:USERNAME" })) Add-Line ("User domain : {0}" -f $env:USERDOMAIN) Add-Line ("Computer domain/workgroup : {0}" -f $computer.Domain) Add-Line ("Domain joined : {0}" -f $computer.PartOfDomain) Add-Line ("Manufacturer : {0}" -f $computer.Manufacturer) Add-Line ("Model : {0}" -f $computer.Model) Add-Line ("System type : {0}" -f $computer.SystemType) Add-Line ("Serial number : {0}" -f $bios.SerialNumber) Add-Line ("BIOS version : {0}" -f ($bios.SMBIOSBIOSVersion -join ', ')) } Invoke-ReportStep 'Windows version and uptime' { Add-Section 'Windows Version and Uptime' $os = Get-CimInstance Win32_OperatingSystem $windowsKey = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' $now = Get-Date $lastBoot = $os.LastBootUpTime $uptime = $now - $lastBoot Add-Line ("Windows product name : {0}" -f $windowsKey.ProductName) Add-Line ("Edition ID : {0}" -f $windowsKey.EditionID) Add-Line ("Display version : {0}" -f $windowsKey.DisplayVersion) Add-Line ("Version : {0}" -f $os.Version) Add-Line ("Build number : {0}" -f $os.BuildNumber) Add-Line ("Update build revision (UBR) : {0}" -f $windowsKey.UBR) Add-Line ("Full build : {0}.{1}" -f $os.BuildNumber, $windowsKey.UBR) Add-Line ("Architecture : {0}" -f $os.OSArchitecture) Add-Line ("Install date : {0}" -f $os.InstallDate) Add-Line ("Last boot : {0}" -f $lastBoot) Add-Line ("Uptime : {0} days, {1} hours, {2} minutes, {3} seconds" -f [math]::Floor($uptime.TotalDays), $uptime.Hours, $uptime.Minutes, $uptime.Seconds) } Invoke-ReportStep 'CPU information' { Add-Section 'CPU Information' $processors = Get-CimInstance Win32_Processor $index = 0 foreach ($processor in $processors) { $index++ Add-Line ("Processor {0}" -f $index) Add-Line (" Name : {0}" -f $processor.Name.Trim()) Add-Line (" Manufacturer : {0}" -f $processor.Manufacturer) Add-Line (" Physical cores : {0}" -f $processor.NumberOfCores) Add-Line (" Logical processors : {0}" -f $processor.NumberOfLogicalProcessors) Add-Line (" Current clock speed : {0} MHz" -f $processor.CurrentClockSpeed) Add-Line (" Maximum clock speed : {0} MHz" -f $processor.MaxClockSpeed) Add-Line (" Socket : {0}" -f $processor.SocketDesignation) Add-Line (" Processor ID : {0}" -f $processor.ProcessorId) } } Invoke-ReportStep 'Memory information' { Add-Section 'Memory Information' $computer = Get-CimInstance Win32_ComputerSystem $os = Get-CimInstance Win32_OperatingSystem $modules = Get-CimInstance Win32_PhysicalMemory $totalGB = [math]::Round($computer.TotalPhysicalMemory / 1GB, 2) $freeGB = [math]::Round($os.FreePhysicalMemory * 1KB / 1GB, 2) Add-Line ("Installed RAM : {0} GB" -f $totalGB) Add-Line ("Available RAM : {0} GB" -f $freeGB) Add-Line ("Used RAM : {0} GB" -f ([math]::Round($totalGB - $freeGB, 2))) Add-Line Add-Line 'Physical memory modules:' $slot = 0 foreach ($module in $modules) { $slot++ Add-Line (" Module {0}" -f $slot) Add-Line (" Bank/slot : {0} / {1}" -f $module.BankLabel, $module.DeviceLocator) Add-Line (" Capacity : {0} GB" -f ([math]::Round($module.Capacity / 1GB, 2))) Add-Line (" Speed : {0} MT/s" -f $module.ConfiguredClockSpeed) Add-Line (" Manufacturer : {0}" -f $module.Manufacturer) Add-Line (" Part number : {0}" -f $module.PartNumber.Trim()) Add-Line (" Serial number : {0}" -f $module.SerialNumber) } } Invoke-ReportStep 'GPU information' { Add-Section 'GPU Information' $gpus = Get-CimInstance Win32_VideoController $index = 0 foreach ($gpu in $gpus) { $index++ Add-Line ("GPU {0}" -f $index) Add-Line (" Name : {0}" -f $gpu.Name) Add-Line (" Adapter RAM : {0} GB" -f $(if ($gpu.AdapterRAM) { [math]::Round($gpu.AdapterRAM / 1GB, 2) } else { 'Not available' })) Add-Line (" Driver version : {0}" -f $gpu.DriverVersion) Add-Line (" Driver date : {0}" -f $gpu.DriverDate) Add-Line (" Video processor : {0}" -f $gpu.VideoProcessor) Add-Line (" Current resolution : {0} x {1} @ {2} Hz" -f $gpu.CurrentHorizontalResolution, $gpu.CurrentVerticalResolution, $gpu.CurrentRefreshRate) Add-Line (" Status : {0}" -f $gpu.Status) } } Invoke-ReportStep 'Network adapters and IP addresses' { Add-Section 'Network Adapters and IP Addresses' $adapters = Get-CimInstance Win32_NetworkAdapterConfiguration | Where-Object { $_.IPEnabled -or $_.MACAddress } | Sort-Object -Property IPEnabled -Descending $index = 0 foreach ($adapter in $adapters) { $index++ Add-Line ("Adapter {0}" -f $index) Add-Line (" Description : {0}" -f $adapter.Description) Add-Line (" Enabled : {0}" -f $adapter.IPEnabled) Add-Line (" MAC address : {0}" -f $adapter.MACAddress) Add-Line (" DHCP enabled : {0}" -f $adapter.DHCPEnabled) Add-Line (" DHCP server : {0}" -f $adapter.DHCPServer) Add-Line (" IP addresses : {0}" -f $(if ($adapter.IPAddress) { $adapter.IPAddress -join ', ' } else { 'None' })) Add-Line (" Subnets : {0}" -f $(if ($adapter.IPSubnet) { $adapter.IPSubnet -join ', ' } else { 'None' })) Add-Line (" Default gateways : {0}" -f $(if ($adapter.DefaultIPGateway) { $adapter.DefaultIPGateway -join ', ' } else { 'None' })) Add-Line (" DNS servers : {0}" -f $(if ($adapter.DNSServerSearchOrder) { $adapter.DNSServerSearchOrder -join ', ' } else { 'None' })) Add-Line } } Invoke-ReportStep 'Windows product key' { Add-Section 'Windows Product Key' $licensing = Get-CimInstance SoftwareLicensingService $firmwareKey = $licensing.OA3xOriginalProductKey $registry = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' $decodedKey = ConvertFrom-DigitalProductId -DigitalProductId ([byte[]]$registry.DigitalProductId.Clone()) Add-Line ("Firmware/OEM key : {0}" -f $(if ($firmwareKey) { $firmwareKey } else { 'Not embedded or not available' })) Add-Line ("Registry-decoded key : {0}" -f $(if ($decodedKey) { $decodedKey } else { 'Not available' })) Add-Line 'Note: Digital licenses and volume licensing may not expose the currently active key.' } Invoke-ReportStep 'Windows update history' { Add-Section 'Windows Update History' $hotFixes = Get-HotFix | Where-Object { $null -ne $_.InstalledOn } | Sort-Object InstalledOn -Descending if ($hotFixes.Count -gt 0) { Add-Line ("Most recent update installed: {0}" -f $hotFixes[0].InstalledOn) Add-Line Add-Line 'Installed updates (newest first):' foreach ($hotFix in $hotFixes) { Add-Line (" {0,-12} {1,-14} {2}" -f $hotFix.HotFixID, $hotFix.InstalledOn.ToString('yyyy-MM-dd'), $hotFix.Description) } } else { Add-Line 'No update installation dates were returned by Get-HotFix.' } } Start-Step 'Writing report' try { $outputDirectory = Split-Path -Parent $OutputPath if ($outputDirectory -and -not (Test-Path -LiteralPath $outputDirectory)) { New-Item -ItemType Directory -Path $outputDirectory -Force | Out-Null } $report | Out-File -LiteralPath $OutputPath -Encoding utf8 -Force Write-Host (" Report saved to: {0}" -f $OutputPath) -ForegroundColor Green } catch { Write-Progress -Activity 'Collecting system information' -Completed throw "Could not write the report to '$OutputPath': $($_.Exception.Message)" } Start-Step 'Opening report in Notepad' try { Start-Process -FilePath 'notepad.exe' -ArgumentList "`"$OutputPath`"" Write-Host ' Report opened in Notepad.' -ForegroundColor Green } catch { Write-Warning ("Could not open Notepad: {0}" -f $_.Exception.Message) } Write-Progress -Activity 'Collecting system information' -Completed Write-Host 'System information collection complete.' -ForegroundColor Green