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.
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: 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