After publishing a project Create an interactive HTML report with PowerShell data, few people gave me feedback, They implemented this and found it very useful that it shows the serial number of server/desktop hardware parts without opening the system and doesn't require downtime, All happens remotely.
There were few suggestions, Accordingly I updated my scripts. But there was one good suggestion, one person suggested creating a GUI tool for the same utility, and if some of the server/laptop/desktop is not connected to the company network or in DMZ, this GUI tool will help to collect information offline and the data can be feed into master HTML report manually.
Here is the GUI screenshot after running PowerShell script. It uses Get-CIMInstance cmdlet to collect various WMI/CIM hardware information from remote systems. Once information is collected I can save it to a JSON file for later use.
Below screenshot is from the earlier written article Create an interactive HTML report with PowerShell data.
Download DeskHWinfo.ps1 here or it is also available github.com/janviudapi.
Below is WPF xaml file, This is the base of graphical user interface.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
<Window x:Class="hardware_info.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:hardware_info" mc:Ignorable="d" Title="Hardware Information v1 - http://vcloud-lab.com" Height="530" Width="800" ResizeMode="NoResize"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="10"/> <ColumnDefinition Width="120"/> <ColumnDefinition Width="120"/> <ColumnDefinition Width="120"/> <ColumnDefinition Width="120"/> <ColumnDefinition Width="120"/> <ColumnDefinition Width="120"/> <ColumnDefinition Width="120"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="43"/> <RowDefinition Height="43"/> <RowDefinition Height="43"/> <RowDefinition Height="33"/> <RowDefinition Height="33"/> <RowDefinition Height="33"/> <RowDefinition Height="33"/> <RowDefinition Height="50"/> <RowDefinition Height="33"/> <RowDefinition Height="33"/> <RowDefinition Height="50"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Image x:Name="iconImage" Margin="10,10,10,10" Grid.Column="1" Grid.RowSpan="3" Width="100" /> <Label Content="Hardware Info v1" Margin="10,10,10,10" Grid.RowSpan="2" VerticalContentAlignment="Center" FontSize="28" Grid.Column="2" Grid.ColumnSpan="2"/> <Button x:Name="exportButton" Content="Save to .JSON" Grid.Column="6" Margin="10,10,10,0" Grid.Row="1" VerticalAlignment="Top" Visibility="Hidden" /> <Label Content="ServerName:" Grid.Row="2" Grid.Column="2" Margin="10,10,10,10" VerticalContentAlignment="Center" HorizontalContentAlignment="Right" FontWeight="Bold"/> <TextBox x:Name="serverNameTextBox" Grid.Column="3" Margin="10,10,10,10" Grid.Row="2" Text="localhost" TextWrapping="Wrap" VerticalContentAlignment="Center" Grid.ColumnSpan="3"/> <Button x:Name="getHwInfobutton" Content="Get HW Info" Grid.Column="6" HorizontalAlignment="Center" Grid.Row="2" VerticalAlignment="Center" Width="100" Height="23"/> <TextBlock HorizontalAlignment="Left" Text="Operating System:" Margin="5,5,5,5" TextWrapping="Wrap" VerticalAlignment="Top" Grid.Row="3" Grid.Column="1" FontWeight="Bold"/> <TextBlock HorizontalAlignment="Left" Text="System Hardware:" Margin="5,5,5,5" TextWrapping="Wrap" VerticalAlignment="Top" Grid.Row="4" Grid.Column="1" FontWeight="Bold"/> <TextBlock HorizontalAlignment="Left" Text="MotherBoard:" Margin="5,5,5,5" TextWrapping="Wrap" VerticalAlignment="Top" Grid.Row="5" Grid.Column="1" FontWeight="Bold"/> <TextBlock HorizontalAlignment="Left" Text="CPU/Processor:" Margin="5,5,5,5" TextWrapping="Wrap" VerticalAlignment="Top" Grid.Row="6" Grid.Column="1" FontWeight="Bold"/> <TextBlock HorizontalAlignment="Left" Text="Memory/RAM:" Margin="5,5,5,5" TextWrapping="Wrap" VerticalAlignment="Top" Grid.Row="7" Grid.Column="1" FontWeight="Bold"/> <TextBlock HorizontalAlignment="Left" Text="Graphic Card:" Margin="5,5,5,5" TextWrapping="Wrap" VerticalAlignment="Top" Grid.Row="8" Grid.Column="1" FontWeight="Bold"/> <TextBlock HorizontalAlignment="Left" Text="Monitor:" Margin="5,5,5,5" TextWrapping="Wrap" VerticalAlignment="Top" Grid.Row="9" Grid.Column="1" FontWeight="Bold"/> <TextBlock HorizontalAlignment="Left" Text="Hard Disk:" Margin="5,5,5,5" TextWrapping="Wrap" VerticalAlignment="Top" Grid.Row="10" Grid.Column="1" FontWeight="Bold"/> <TextBlock HorizontalAlignment="Left" Text="Network card:" Margin="5,5,5,5" TextWrapping="Wrap" VerticalAlignment="Top" Grid.Row="11" Grid.Column="1" FontWeight="Bold"/> <TextBox x:Name="textboxUserName" Margin="10,10,10,10" Grid.Column="4" TextWrapping="Wrap" VerticalAlignment="Center" Grid.ColumnSpan="2" Text="Type Remote System UserName" Foreground="DarkGray" Visibility="Hidden"/> <PasswordBox x:Name="passwordboxPassword" Margin="10,10,10,10" Grid.Column="4" VerticalAlignment="Center" Grid.ColumnSpan="2" Grid.Row="1" Foreground="DarkGray" Visibility="Hidden"/> <CheckBox x:Name="checkBoxNoCreds" Content="No Credentials" Grid.Column="6" HorizontalAlignment="Left" Margin="10,10,10,10" VerticalAlignment="Top" IsChecked="True"/> <TextBlock x:Name="textBlockOS" Grid.Column="2" Margin="5,5,5,5" Grid.Row="3" Text="Operating System" TextWrapping="Wrap" VerticalAlignment="Top" Grid.ColumnSpan="5" Height="25"/> <TextBlock x:Name="textBlockSystemHW" Grid.Column="2" Margin="5,5,5,5" Grid.Row="4" Text="System Hardware" TextWrapping="Wrap" VerticalAlignment="Top" Grid.ColumnSpan="5" Height="25"/> <TextBlock x:Name="textBlockMotherBoard" Grid.Column="2" Margin="5,5,5,5" Grid.Row="5" Text="MotherBoard/MainBoard" TextWrapping="Wrap" VerticalAlignment="Top" Grid.ColumnSpan="5" Height="25"/> <TextBlock x:Name="textBlockCpu" Grid.Column="2" Margin="5,5,5,5" Grid.Row="6" Text="CPU/Processor" TextWrapping="Wrap" VerticalAlignment="Top" Grid.ColumnSpan="5" Height="25"/> <TextBlock x:Name="textBlockRam" Grid.Column="2" Margin="5,5,5,5" Grid.Row="7" Text="Memory/RAM" TextWrapping="Wrap" VerticalAlignment="Top" Grid.ColumnSpan="5" Height="35"/> <TextBlock x:Name="textBlockGraphic" Grid.Column="2" Margin="5,5,5,5" Grid.Row="8" Text="Graphics/Video/GPU Card/Adapter" TextWrapping="Wrap" VerticalAlignment="Top" Grid.ColumnSpan="5" Height="25"/> <TextBlock x:Name="textBlockMonitor" Grid.Column="2" Margin="5,5,5,5" Grid.Row="9" Text="Monitors" TextWrapping="Wrap" VerticalAlignment="Top" Grid.ColumnSpan="5" Height="25"/> <TextBlock x:Name="textBlockHDD" Grid.Column="2" Margin="5,5,5,5" Grid.Row="10" Text="Hard Disk Drive/HDD" TextWrapping="Wrap" VerticalAlignment="Top" Grid.ColumnSpan="5"/> <TextBlock x:Name="textBlockNetwork" Grid.Column="2" Margin="5,5,5,5" Grid.Row="11" Text="Network/Ethernet Card/Adapter" TextWrapping="Wrap" VerticalAlignment="Top" Grid.ColumnSpan="5"/> </Grid> </Window> |
Below PowerShell script will use xaml wpf form as GUI and with the logic code used in the script will fetch WMI/CIM hardware report.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
#Load required libraries Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase, System.Windows.Forms, System.Drawing #Website: http://vcloud-lab.com #Written By: vJanvi #Date: 20 May 2021 #Tested Environment: #Microsoft Windows 11 #PowerShell Version 5.1, 7 #Read xaml file $xamlFile = "$PSScriptRoot\DeskInfo.xaml" $xamlContent = Get-Content -Path $xamlFile -ErrorAction Stop #[xml]$xaml = $xamlContent -replace 'mc:Ignorable="d"', '' -replace "x:N", 'N' -replace 'x:Class=".*?"', '' -replace 'd:DesignHeight="\d*?"', '' -replace 'd:DesignWidth="\d*?"', '' [xml]$xaml = $xamlContent -replace 'x:Class=".*?"', '' -replace 'xmlns:d="http://schemas.microsoft.com/expression/blend/2008"', '' -replace 'mc:Ignorable="d"', '' #Read the forms in xaml $reader = (New-Object System.Xml.XmlNodeReader $xaml) $form = [Windows.Markup.XamlReader]::Load($reader) #AutoFind all controls $xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach-Object { New-Variable -Name $_.Name -Value $form.FindName($_.Name) -Force } $iconImage.Source = "$PSScriptRoot\info.png" #$hardwareInfo.Content = "Hardware Info V1" $textboxUserName.Add_GotFocus({ if ($textboxUserName.Text -eq 'Type Remote System UserName') { $textboxUserName.Foreground = 'Black' $textboxUserName.Text = '' } }) $textboxUserName.Add_LostFocus({ if ($textboxUserName.Text -eq '') { $textboxUserName.Text = 'Type Remote System UserName' $textboxUserName.Foreground = 'Darkgray' } }) $passwordboxPassword.Add_GotFocus({ if ($passwordboxPassword.Text -eq 'Type Remote System Password') { $passwordboxPassword.Foreground = 'Black' $passwordboxPassword.Text = '' } }) $passwordboxPassword.Add_LostFocus({ if ($passwordboxPassword.Text -eq '') { $passwordboxPassword.Text = 'Type Remote System Password' $passwordboxPassword.Foreground = 'Darkgray' } }) $checkBoxNoCreds.Add_Checked({ $textboxUserName.Visibility = 'Hidden' $passwordboxPassword.Visibility = 'Hidden' }) $checkBoxNoCreds.Add_Unchecked({ $textboxUserName.Visibility = 'Visible' $passwordboxPassword.Visibility = 'Visible' }) $serverNameTextBox.Text = $env:COMPUTERNAME $getHwInfobutton.Add_Click({ function Show-MessageBox { param ( [string]$Message = "Show user friendly Text Message", [string]$Title = 'Title here', [ValidateRange(0,5)] [Int]$Button = 0, [ValidateSet('None','Hand','Error','Stop','Question','Exclamation','Warning','Asterisk','Information')] [string]$Icon = 'Error' ) #Note: $Button is equl to [System.Enum]::GetNames([System.Windows.Forms.MessageBoxButtons]) #Note: $Icon is equl to [System.Enum]::GetNames([System.Windows.Forms.MessageBoxIcon]) $MessageIcon = [System.Windows.Forms.MessageBoxIcon]::$Icon [System.Windows.Forms.MessageBox]::Show($Message,$Title,$Button,$MessageIcon) } function Get-HardwareInfo { [CmdletBinding()] param ( [Parameter()] [System.String] $ServerName = $serverNameTextBox.Text ) $cimSessionOption = New-CimSessionOption -Protocol Default if (!$checkBoxNoCreds.IsChecked) { $secureStringPassword = ConvertTo-SecureString $passwordboxPassword.Password -AsPlainText -Force $encryptedPassword = ConvertFrom-SecureString -SecureString $secureStringPassword $credential = New-Object System.Management.Automation.PsCredential($textboxUserName.Text, ($encryptedPassword | ConvertTo-SecureString)) try { $cimsession = New-CimSession -ComputerName $ServerName -SessionOption $cimSessionOption -Credential $credential -Authentication Kerberos -ErrorAction Stop } catch { try { $cimsession = New-CimSession -ComputerName $ServerName -SessionOption $cimSessionOption -Credential $credential -ErrorAction Stop } catch { } Show-MessageBox -Message "Cannot connect remote system over CIM Protocol, `nTry script locally" -Title 'Connection Error' } } #if (!$checkBoxNoCreds.IsChecked) else { try { $cimsession = New-CimSession -ComputerName $ServerName -SessionOption $cimSessionOption -ErrorAction Stop } catch { Show-MessageBox -Message "Cannot connect remote system over CIM Protocol, `nTry script locally" -Title 'Connection Error' } } #else if (!$checkBoxNoCreds.IsChecked) if ($null -ne $cimsession) { $computerSystem = Get-CimInstance -Cimsession $cimsession -ClassName CIM_ComputerSystem | Select-Object Manufacturer, Model $bios = Get-CimInstance -Cimsession $cimsession -ClassName Win32_BIOS | Select-Object SerialNumber, SMBIOSBIOSVersion $baseBoard = Get-CimInstance -Cimsession $cimsession -ClassName win32_baseboard | Select-Object Manufacturer, Product, SerialNumber, Version $operatingSystem = Get-CimInstance -Cimsession $cimsession -ClassName CIM_OperatingSystem | select-Object Caption, OSArchitecture $processor = Get-CimInstance -Cimsession $cimsession -ClassName CIM_Processor | select-Object Name, OSArchitecture, NumberOfCores, NumberOfEnabledCore, NumberOfLogicalProcessors, ProcessorId, PartNumber $videoController = Get-CimInstance -Cimsession $cimsession -ClassName win32_VideoController | Select-Object Name, VideoProcessor $diskDrive = Get-CimInstance -Cimsession $cimsession -ClassName Win32_DiskDrive | Select-Object Model, SerialNumber, Size, FirmwareRevision, InterfaceType, Index $networkAdapter = Get-CimInstance -Cimsession $cimsession -ClassName Win32_NetworkAdapter -Filter "PhysicalAdapter = 'true'" | Select-Object Name, ProductName, DeviceID, Speed, AdapterType, InterfaceIndex, MACAddress $physicalMemory = Get-CimInstance -Cimsession $cimsession -ClassName CIM_PhysicalMemory | ForEach-Object -Process { [pscustomobject]@{ #DeviceLocator = "" Doesn't actually exist? SerialNumber = $_.SerialNumber Capacity = $_.Capacity Speed = if ($_.speed -ge 1000000000) {"$($_.Speed / 1000000000) Gb/s"} else {"$($_.Speed / 1000000) Mb/s"} PartNumber = $_.PartNumber Manufacturer = $_.Manufacturer } } $monitor = Get-CimInstance -Cimsession $cimsession -ClassName WmiMonitorID -Namespace root\wmi -ErrorAction SilentlyContinue | ForEach-Object -Process { [pscustomobject]@{ ManufacturerName = [char[]]$_.ManufacturerName -join '' ProductCodeID = [char[]]$_.ProductCodeID -join '' UserFriendlyName = [char[]]$_.UserFriendlyName -join '' SerialNumberID = [char[]]$_.SerialNumberID -join '' YearOfManufacture = $_.YearOfManufacture WeekOfManufacture = $_.WeekOfManufacture } } } #if ($null -ne $cimsession) [pscustomobject]@{ ComputerName = $ServerName computerSystem = $computerSystem Bios = $bios BaseBoard = $baseBoard OperatingSystem = $operatingSystem Processor = $processor PhysicalMemory = $physicalMemory VideoController = $videoController Monitor = $monitor DiskDrive = $diskDrive NetworkAdapter = $networkAdapter } #[pscustomobject]@{ } #function Get-HardwareInfo { $global:allHardwareInfo = $null $global:allHardwareInfo = Get-HardwareInfo -ServerName $serverNameTextBox.Text if ([string]::IsNullOrEmpty($global:allHardwareInfo.computerSystem)) { $textBlockOS.Text = '' $textBlockSystemHW.Text = '' $textBlockMotherBoard.Text = '' $textBlockCpu.Text = '' $memorySize = '' $textBlockRam.Text = '' $textBlockGraphic.Text = '' $textBlockMonitor.Text = '' $hddSize = '' $textBlockHDD.Text = '' $textBlockNetwork.Text = '' $exportButton.Visibility = 'Hidden' $checkBoxNoCreds.IsChecked = $true } else { $textBlockOS.Text = "{0} - {1}" -f $global:allHardwareInfo.OperatingSystem.Caption, $global:allHardwareInfo.OperatingSystem.OSArchitecture $textBlockSystemHW.Text = "{0} {1} - Serial No: {2}" -f $global:allHardwareInfo.computerSystem.Manufacturer, $global:allHardwareInfo.computerSystem.Model, $global:allHardwareInfo.Bios.SerialNumber $textBlockMotherBoard.Text = "{0} - Product: {1}" -f $global:allHardwareInfo.BaseBoard.Manufacturer, $global:allHardwareInfo.BaseBoard.Product $textBlockCpu.Text = "{0} - {1} - Id: {2}" -f $global:allHardwareInfo.Processor.Count, $global:allHardwareInfo.Processor[0].Name, $global:allHardwareInfo.Processor[0].ProcessorId $memorySize = $global:allHardwareInfo.PhysicalMemory.Capacity | Measure-Object -Sum | Select-Object -ExpandProperty Sum $textBlockRam.Text = "{0} Gb - Serial No: {1} Part No: {2}" -f [System.Math]::Round($memorySize/1gb), ($global:allHardwareInfo.PhysicalMemory.SerialNumber.trim() -join ', '), ($global:allHardwareInfo.PhysicalMemory.PartNumber.trim() -join ', ') $textBlockGraphic.Text = $global:allHardwareInfo.VideoController.Name.trim() -join ', ' $textBlockMonitor.Text = "{0} - Serial No: {1}" -f ($global:allHardwareInfo.Monitor.ManufacturerName -join ', ') , ($global:allHardwareInfo.Monitor.SerialNumberID -join ', ') $hddSize = $global:allHardwareInfo.DiskDrive.Size | Measure-Object -Sum | Select-Object -ExpandProperty Sum $textBlockHDD.Text = "{0} Gb - {1} Serial No: {2}" -f [System.Math]::Round($hddSize/1gb), ($global:allHardwareInfo.DiskDrive.Model -join ', ' ), ($global:allHardwareInfo.DiskDrive.SerialNumber -join ', ' ) $textBlockNetwork.Text = "{0} - Mac ID: {1}" -f ($global:allHardwareInfo.NetworkAdapter.ProductName -join ', ' ), ($global:allHardwareInfo.NetworkAdapter.MACAddress -join ', ' ) $exportButton.Visibility = 'Visible' $checkBoxNoCreds.IsChecked = $true } }) $exportButton.Add_Click({ function Open-FileDialog { param ( [string]$Path = [Environment]::GetFolderPath('Desktop') ) $fileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{ InitialDirectory = $Path Filter = 'json (*.json)|*.json' } $null = $fileBrowser.ShowDialog() $fileBrowser.FileName } Function Save-FileDialog { param ( [string]$Path = [Environment]::GetFolderPath('Desktop') ) [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null $SaveFileDialog = New-Object System.Windows.Forms.SaveFileDialog $SaveFileDialog.initialDirectory = $Path $SaveFileDialog.filter = 'json (*.json)|*.json' #"All files (*.*)| *.*" $SaveFileDialog.ShowDialog() | Out-Null $SaveFileDialog.filename } $jsonFile = Save-FileDialog $allHardwareInfo | ConvertTo-Json -Depth 20 | Out-File -FilePath $jsonFile }) [void]$form.ShowDialog() |
Useful Articles
Generate random password GUI using powershell
Part 1: Create WPF XAML powershell GUI form with Visual studio
Part 2: Powershell and WPF: Build GUI applications tutorial
Part 3: Create shorter Microsoft Powershell WPF automated clean script
Powershell WPF Themes guide step by step
Part 2: Powershell WPF Themes guide step by step
Part 3: Powershell wpf MahApps.Metro theme step by step
Powershell WPF MahApps.Metro update theme library
Powershell WPF custom Image based ProgressBar
Powershell WPF Charts dashboard demo