Menu

Virtual Geek

Tales from real IT system administrators world and non-production environment

Powershell GUI System Hardware Information

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.

Microsoft PowerShell Desktop Hardware Information info operating system motherboard mainboard memory ram cpu processor graphic audio video graphic gpu hard disk hdd network ethernet adapter card.jpg

Below screenshot is from the earlier written article Create an interactive HTML report with PowerShell data.

Microsoft PowerShell HTML report from data WPF GUI xaml interactive website report

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

Go Back

Comment

Blog Search

Page Views

8961098

Follow me on Blogarama