Menu

Virtual Geek

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

PowerShell GUI get patch updates information with disk space and uptime

This script helps to get last installed patches, updates with dates, Server/System Last boot Uptime and C drive disk space information. This script was asked by one of the my SCCM team to troubleshoot in case if patches/updates installation are failing.

After executing script you can view the information as below.

Powershell GUI wpf Know your updates Operating system and Uptime last boot time c drive space chart hotfix security updates bugfix kb.png

Download this Get-UpdateReport.zip script here or it is also available on github.com.

Below is the PowerShell Graphical interface recipe, It is written in to WPF Xaml file.

MainWindow.xaml

 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
<Window x:Class="WpfApp1.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:WpfApp1"
        mc:Ignorable="d"
        Title="Get update information http://vcloud-lab.com" Height="620" 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="10"/>
            <RowDefinition Height="33"/>
            <RowDefinition Height="33"/>
            <RowDefinition Height="33"/>
            <RowDefinition Height="33"/>
            <RowDefinition Height="33"/>
            <RowDefinition Height="33"/>
            <RowDefinition Height="33"/>
            <RowDefinition Height="33"/>
            <RowDefinition Height="33"/>
            <RowDefinition Height="33"/>
            <RowDefinition Height="33"/>
            <RowDefinition Height="33"/>
            <RowDefinition Height="33"/>
            <RowDefinition Height="33"/>
            <RowDefinition Height="33"/>
            <RowDefinition Height="33"/>
            <RowDefinition Height="33"/>
            <RowDefinition Height="33"/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Column="1" HorizontalAlignment="Left" Margin="55,0,0,0" Grid.Row="1" TextWrapping="Wrap" Text="Know Your" VerticalAlignment="Center" FontSize="24" TextAlignment="Right" Grid.RowSpan="2"/>
        <Image x:Name="imageIcon" Grid.Column="2" Margin="0" Grid.RowSpan="2" Grid.Row="1" />

        <CheckBox x:Name="checkBoxNoCreds" Grid.Column="5" Content="No Credentials" Margin="5" Grid.Row="1" IsChecked="True"/>
        <Label x:Name="labelUserName" Grid.Column="3" Margin="5" Content="UserName:" HorizontalAlignment="Left" Grid.Row="2" VerticalAlignment="Center" FontWeight="Bold" Visibility="Hidden"/>
        <TextBox x:Name="textBoxUserName" Grid.Column="4" Margin="-42,0,5,0" Grid.Row="2" TextWrapping="Wrap" Text="Type UserName" VerticalAlignment="Center" Visibility="Hidden" Foreground="DarkGray"/>
        <Label x:Name="labelPassword" Grid.Column="5" Margin="5" Content="Password:" HorizontalAlignment="Left" Grid.Row="2" VerticalAlignment="Center" FontWeight="Bold" Visibility="Hidden"/>
        <PasswordBox x:Name="passwordBoxPassword" Grid.Column="6" Margin="-44,0,5,0" Grid.Row="2" VerticalAlignment="Center" Visibility="Hidden" Password="Type Password" Foreground="DarkGray"/>
        <Label x:Name="labelComputer" Grid.Column="3" Content="ComputerName: " HorizontalAlignment="Left" Margin="0" Grid.Row="1" VerticalAlignment="Center" FontWeight="Bold"/>
        <TextBox x:Name="textBoxComputer" Grid.Column="4" Margin="-14,5,5,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top"></TextBox>
        <TextBox x:Name="textBoxComputerName" Grid.Column="2" Margin="5,5,5,5" Grid.Row="4" TextWrapping="Wrap" Grid.ColumnSpan="4" Grid.RowSpan="2" FontWeight="Bold" FontSize="24" IsReadOnly="True" TextAlignment="Center"/>
        <Button x:Name="buttonAudit" Grid.Column="5" Content="Audit" Margin="5" Grid.Row="3"/>
        <GroupBox x:Name="groupBoxUptime" Margin="5,5" Grid.Column="1" Header="OS and Uptime" Grid.Row="6" Grid.ColumnSpan="3" Grid.RowSpan="6">
            <RichTextBox x:Name="richTextBoxOSUptime" VerticalAlignment="Top" Margin="5" IsReadOnly="True"/>
        </GroupBox>
        <GroupBox x:Name="groupBoxCDrive" Margin="5,5,5,5" Grid.Column="4" Header="C Drive space" Grid.Row="6" Grid.ColumnSpan="2" Grid.RowSpan="6">
            <RichTextBox x:Name="richTextBoxCDrive" VerticalAlignment="Top" Margin="5" IsReadOnly="True"/>
        </GroupBox>
        <WebBrowser x:Name='WebBrowserPieChart' Margin="5,5,55,5" Grid.Column="6" Grid.RowSpan="6" Grid.Row="6" Grid.ColumnSpan="2"/>
        <DataGrid x:Name="dataGridUpdates" Grid.Column="1" Grid.Row="12" Grid.ColumnSpan="6" Margin="5" Grid.RowSpan="6" IsReadOnly="True"/>
        <WebBrowser x:Name='WebBrowserLoading' Grid.Column="6" Grid.Row="1" Margin="5,5,60,5" Grid.ColumnSpan="2" Grid.RowSpan="4" Visibility="Hidden"/>
    </Grid>
</Window>

Next part is the PowerShell script it uses CIM/WMI Object to fetch information from local or remote server/computer. Information contains Patch Updates, Last Boot Uptime and C disk drive Total, free and used space in GB, which will show up on the GUI.

Get-UpdateReport.ps1

  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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
#Load required libraries
Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase, System.Windows.Forms, System.Drawing 

#Website: http://vcloud-lab.com
#Written By: vJanvi
#Date: 20 Jan 2023
#Tested Environment:
    #Microsoft Windows 11, Windows 2022
    #PowerShell Version 5.1, 7

#Read xaml file
$xamlFile = "$PSScriptRoot\SideLoaders\MainWindow.xaml" #'D:\Projects\PowerShell\PatchingInfo\WpfApp1\MainWindow.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 
}

$ImageIcon.Source = "$PSScriptRoot\SideLoaders\Get_UpdateInfo.png"
#$hardwareInfo.Content = "Hardware Info V1"

#$WebBrowserPieChart.Navigate('file:///C:/Temp/dounght%20chart/donut-chart.html')

$textBoxComputer.Text = $env:COMPUTERNAME
$textBoxComputerName.Text = $env:COMPUTERNAME

$textboxUserName.Add_GotFocus({
    if ($textboxUserName.Text -eq 'Type UserName') {
        $textboxUserName.Foreground = 'Black'
        $textboxUserName.Text = ''
    }
})
$textboxUserName.Add_LostFocus({
    if ($textboxUserName.Text -eq '') {
        $textboxUserName.Text = 'Type UserName'
        $textboxUserName.Foreground = 'Darkgray'
    }
})

$passwordboxPassword.Add_GotFocus({
    if ($passwordboxPassword.Password -eq 'Type Password') {
        $passwordboxPassword.Foreground = 'Black'
        $passwordboxPassword.Password = ''
    }
})
$passwordboxPassword.Add_LostFocus({
    if ($passwordboxPassword.Password -eq '') {
        $passwordboxPassword.Password = 'Type Password'
        $passwordboxPassword.Foreground = 'Darkgray'
    }
})

$checkBoxNoCreds.Add_Checked({
    $textboxUserName.Visibility = 'Hidden'
    $passwordboxPassword.Visibility = 'Hidden'
    $labelUserName.Visibility = 'Hidden'
    $labelPassword.Visibility = 'Hidden'
})
$checkBoxNoCreds.Add_Unchecked({
    $textboxUserName.Visibility = 'Visible'
    $passwordboxPassword.Visibility = 'Visible'
    $labelUserName.Visibility = 'Visible'
    $labelPassword.Visibility = 'Visible'
})
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)   
}

$buttonAudit.Add_Click({
    $WebBrowserLoading.Visibility = 'Visible'
    $WebBrowserLoading.Navigate("file:///$PSScriptRoot/SideLoaders/loading.html")

    function Get-UpdateInfo {
        [CmdletBinding()]
        param (
            [Parameter()]
            [System.String]
            $ServerName = $textBoxComputer.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 {
                try {
                    $cimSessionOption = New-CimSessionOption -Protocol Dcom
                    $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)
        {
            $os = Get-CimInstance -ClassName Win32_OperatingSystem -Cimsession $cimsession
            #$uptime =  [Management.ManagementDateTimeConverter]::ToDateTime($os.LastBootUpTime)
            #$os.LastBootUpTime.DateTime
            $driveCInfo = Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DeviceID = "C:"' -Cimsession $cimsession | Select-Object DeviceId, VolumeName, @{n="TotalSizeGB";e={[System.Math]::Round($_.Size/1GB,2)}}, @{n="UsedSpaceGB";e={[System.Math]::Round(($_.Size - $_.FreeSpace)/1GB,2)}}, @{n="FreeSpaceGB";e={[System.Math]::Round($_.FreeSpace/1GB,2)}}
            $lastUpdates = Get-CimInstance -ClassName Win32_QuickFixEngineering -Cimsession $cimsession | Group-Object InstalledOn | Sort-Object -Property {$_.Name -as [datetime]} | Select-Object -Last 1
    
            [pscustomobject]@{
                ComputerName    = $ServerName
                OS              = $os.Caption
                OSLastBootTime  = $os.LastBootUpTime.DateTime
                OSUptime        = New-TimeSpan -Start (Get-Date) -End $os.LastBootUpTime.DateTime | Select-Object -ExpandProperty Hours
                DeviceID        = $driveCInfo.DeviceId
                VolumeName      = $driveCInfo.VolumeName
                TotalSizeGB     = $driveCInfo.TotalSizeGB
                UsedSpaceGB     = $driveCInfo.UsedSpaceGB
                FreeSpaceGB     = $driveCInfo.FreeSpaceGB
                Updates         = $lastUpdates
            } #[pscustomobject]@{
        } #if ($null -ne $cimsession)
    }
    
    function Format-RichTextBox {  
        #https://msdn.microsoft.com/en-us/library/system.windows.documents.textelement(v=vs.110).aspx#Propertiesshut  
        param (  
            [parameter(Position=0, Mandatory=$true, ValueFromPipelineByPropertyName=$true)]  
            [System.Windows.Controls.RichTextBox]$RichTextBoxControl,  
            [String]$Text,  
            [String]$ForeGroundColor = 'Black',  
            [String]$BackGroundColor = 'White',  
            [String]$FontSize = '12',  
            [String]$FontStyle = 'Normal',  
            [String]$FontWeight = 'Normal',  
            [Switch]$NewLine  
        )  
        $ParamOptions = $PSBoundParameters  
        $RichTextRange = New-Object System.Windows.Documents.TextRange(<#$RichTextBoxControl.Document.ContentStart#>$RichTextBoxControl.Document.ContentEnd, $RichTextBoxControl.Document.ContentEnd)  
        if ($ParamOptions.ContainsKey('NewLine')) {  
            $RichTextRange.Text = "`n$Text"  
        }  
        else {  
            $RichTextRange.Text = $Text  
        }  
        
        $Defaults = @{ForeGroundColor='Black';BackGroundColor='White';FontSize='12'; FontStyle='Normal'; FontWeight='Normal'}  
        foreach ($Key in $Defaults.Keys) {  
            if ($ParamOptions.Keys -notcontains $Key) {  
                $ParamOptions.Add($Key, $Defaults[$Key])  
            }  
        }   
        
        $AllParameters = $ParamOptions.Keys | Where-Object {@('RichTextBoxControl','Text','NewLine') -notcontains $_}  
        foreach ($SelectedParam in $AllParameters) {  
            if ($SelectedParam -eq 'ForeGroundColor') {$TextElement = [System.Windows.Documents.TextElement]::ForegroundProperty}  
            elseif ($SelectedParam -eq 'BackGroundColor') {$TextElement = [System.Windows.Documents.TextElement]::BackgroundProperty}  
            elseif ($SelectedParam -eq 'FontSize') {$TextElement = [System.Windows.Documents.TextElement]::FontSizeProperty}  
            elseif ($SelectedParam -eq 'FontStyle') {$TextElement = [System.Windows.Documents.TextElement]::FontStyleProperty}  
            elseif ($SelectedParam -eq 'FontWeight') {$TextElement = [System.Windows.Documents.TextElement]::FontWeightProperty}  
            $RichTextRange.ApplyPropertyValue($TextElement, $ParamOptions[$SelectedParam])  
        }  
    }

    $textBoxComputerName.Text = $textBoxComputer.Text
    $updateInfo = Get-UpdateInfo
    Format-RichTextBox -RichTextBoxControl $richTextBoxOSUptime -Text 'OS: ' -FontWeight Bold
    Format-RichTextBox -RichTextBoxControl $richTextBoxOSUptime -Text $updateInfo.OS 
    Format-RichTextBox -RichTextBoxControl $richTextBoxOSUptime -Text 'Last Boot Time: ' -FontWeight Bold -NewLine
    Format-RichTextBox -RichTextBoxControl $richTextBoxOSUptime -Text $updateInfo.OSLastBootTime 
    Format-RichTextBox -RichTextBoxControl $richTextBoxOSUptime -Text 'Uptime Hrs: ' -FontWeight Bold -NewLine
    $osUptime = $updateInfo.OSUptime
    $uptime = -$osUptime
    Format-RichTextBox -RichTextBoxControl $richTextBoxOSUptime -Text $uptime

    Format-RichTextBox -RichTextBoxControl $richTextBoxCDrive -Text 'Device ID: ' -FontWeight Bold
    Format-RichTextBox -RichTextBoxControl $richTextBoxCDrive -Text $updateInfo.DeviceId
    Format-RichTextBox -RichTextBoxControl $richTextBoxCDrive -Text 'Volume Name: ' -FontWeight Bold -NewLine
    Format-RichTextBox -RichTextBoxControl $richTextBoxCDrive -Text $updateInfo.VolumeName
    Format-RichTextBox -RichTextBoxControl $richTextBoxCDrive -Text 'Total Size GB: ' -FontWeight Bold -NewLine
    Format-RichTextBox -RichTextBoxControl $richTextBoxCDrive -Text $updateInfo.TotalSizeGB
    Format-RichTextBox -RichTextBoxControl $richTextBoxCDrive -Text 'Used Space GB: ' -FontWeight Bold -NewLine
    Format-RichTextBox -RichTextBoxControl $richTextBoxCDrive -Text $updateInfo.UsedSpaceGB
    Format-RichTextBox -RichTextBoxControl $richTextBoxCDrive -Text 'Free Space GB: ' -FontWeight Bold -NewLine
    Format-RichTextBox -RichTextBoxControl $richTextBoxCDrive -Text $updateInfo.FreeSpaceGB

    $freeSpaceDegree = [Math]::Round(([Math]::Round($updateInfo.FreeSpaceGB)/[Math]::Round($updateInfo.TotalSizeGB))*100)
    $usedSpaceDegree = [Math]::Round(([Math]::Round($updateInfo.UsedSpaceGB)/[Math]::Round($updateInfo.TotalSizeGB))*100)
    #$freeSpaceDegree = 359 - $usedSpaceDegree

$chart = @"
<!DOCTYPE html>
<html lang='en'>
<head>
  <style>
    body {
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    }
  </style>
</head>
<body>
  <p><Strong>C Disk </strong>Disk Usage</p>

  <table>
    <tr>
      <td >Free</td>
      <td style='width: 70%;'>
        <div style='background-color:coral; width:$freeSpaceDegree%'>
            $freeSpaceDegree%
        </div>
      </td>
    </tr>
    <tr>
      <td>Used</td>
      <td style='width: 70%;'>
        <div style='background-color: aquamarine; width:$usedSpaceDegree%'>
            $usedSpaceDegree%
        </div>
      </td>
    </tr>
  </table>
</body>
</html>

"@
    $chart | Out-File $PSScriptRoot\SideLoaders\chart.html
    $WebBrowserPieChart.Navigate("file:///$PSScriptRoot\SideLoaders\chart.html")
   
    #$updateList = New-Object System.Collections.ArrayList
    #Build Datagrid if there
    if (-not [string]::IsNullOrEmpty($updateInfo)) {
        $latestUpdate = $updateInfo.Updates.Group | Select-Object HotFixID, InstalledBy, Description, InstalledOn, Caption
        #foreach ($update in $latestUpdate)
        #{
            #$updateList.AddRange($latestUpdate)
        #}
        $dataGridUpdates.ItemsSource = @($latestUpdate)
    }
    $WebBrowserLoading.Visibility = 'Hidden'
})
[void]$form.ShowDialog()

Useful Articles
Powershell GUI encode decode images
Powershell GUI format text on TextBox and RichTextBox
Powershell PoshGUI: Convert user to SID and vice versa using
Microsoft Powershell GUI: Change Internet Options connections Lan settings proxy server grayed out
Powshell GUI Date and Time converter tool
Powershell wpf gui, date and time converter select timezone get-date, timzone standard time ps1 script utc time, now.png

Powershell WPF GUI: ToolBox control Textbox watermark placeholder demo
Powershell WPF GUI: LinkLabel hyperlink demo
PowerShell WPF before and after image slider
POWERSHELL: USE PARAMETERS AND CONFIGURATION FROM INI FILE, USE AS SPLATTING
POWERSHELL CREATE HASHTABLE FROM MICROSOFT EXCEL

Go Back

Comment

Blog Search

Page Views

11377005

Follow me on Blogarama