Last active
July 5, 2024 23:16
-
-
Save jdhitsolutions/b5b2fcc047cedc252b7f169ffa7e9642 to your computer and use it in GitHub Desktop.
A PowerShell script to create a computer status report. In addition to the native object output, you can format the output as text or html. Read the help and examples
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#requires -version 5.1 | |
<# | |
.Synopsis | |
Create System Report | |
.Description | |
Create a system status report with information gathered from WMI using Get-CimInstanxce. T | |
he default output to the pipeline is a collection of custom objects. You can also use -TEXT | |
to write a formatted text report, suitable for sending to a file or printer, or -HTML to | |
create HTML code. You will need to pipe the results to Out-File if you want to save either | |
the text or html output. | |
.Parameter Computername | |
The name of the computer to query. The default is the localhost. | |
.Parameter Credential | |
The name of an alternate credential, or a saved credential object. | |
.Parameter Quick | |
Run a quick report with no event logs queries. This speeds up the report generation. | |
This parameter has an alias of 'NoLogs'. | |
.Parameter ReportTitle | |
The title for your report. This parameter has an alias of 'Title'. | |
.Parameter Hours | |
The number of hours to search for errors and warnings. The default is 24. | |
.Parameter HTML | |
Create HTML report. You must pipe to Out-File to save the results. | |
.Parameter ImagePath | |
The local path to an image file which can be embedded into the report. | |
Valid file types are PNG, JPG and GIF. The image will be resize to 120x120. | |
.Parameter Text | |
Create a formatted text report. You must pipe to Out-File to save the results. | |
.Example | |
PS C:\Scripts\> .\MorningReport.ps1 | Export-Clixml ("c:\work\{0:yyyy-MM-dd}_{1}.xml" -f (get-date),$env:computername) | |
Run a morning report and export it to an XML file with a date stamped file name. | |
.Example | |
PS C:\Scripts\> .\MorningReport Quark -Text | Out-file c:\work\quark-report.txt | |
Run a morning report for a remote computer and save the results to an text file. | |
.Example | |
PS C:\Scripts\> .\MorningReport -html -hours 30 | Out-file C:\work\MyReport.htm | |
Run a morning report for the local computer and get last 30 hours of event log information. Save as an HTML report. | |
.Example | |
PS C:\Scripts\> get-content computers.txt | .\Morningreport -quick -html | out-file c:\work\morningreport.htm | |
Get the list of computers and create a single HTML report without the event log information. | |
.Link | |
Get-CimInstance | |
Get-WinEvent | |
ConvertTo-HTML | |
.Inputs | |
String | |
.Outputs | |
Custom object, text or HTML code | |
.Notes | |
Version : 6.0 | |
Last Updated: 1 June, 2022 | |
Author : Jeffery Hicks (@JeffHicks) | |
Originally published at http://jdhitsolutions.com/blog/2013/02/powershell-morning-report-with-credentials | |
Learn more about PowerShell: | |
http://jdhitsolutions.com/blog/essential-powershell-resources/ | |
#> | |
[cmdletbinding(DefaultParameterSetName = "object")] | |
Param( | |
[Parameter( | |
Position = 0, | |
ValueFromPipeline, | |
ValueFromPipelineByPropertyName | |
)] | |
[ValidateNotNullOrEmpty()] | |
[string]$Computername = $env:computername, | |
[ValidateNotNullOrEmpty()] | |
[PSCredential]$Credential, | |
[ValidateNotNullOrEmpty()] | |
[alias("title")] | |
[string]$ReportTitle = "Morning System Report", | |
[ValidateScript( { $_ -ge 1 })] | |
[int]$Hours = 24, | |
[alias("NoLogs")] | |
[switch]$Quick, | |
[Parameter(ParameterSetName = "html")] | |
[switch]$HTML, | |
[Parameter(ParameterSetName = "html")] | |
[string]$ImagePath, | |
[Parameter(ParameterSetName = "text")] | |
[switch]$Text | |
) | |
Begin { | |
#script internal version number used in output | |
[string]$reportVersion = "6.0" | |
#if an image path is specified, convert it to Base64 | |
if ($ImagePath -AND (Test-Path $ImagePath)) { | |
#use correct parameter depending on PS Version | |
if ($PSVersionTable.PSVersion.Major -eq 7) { | |
$imgBytes = Get-Content $ImagePath -AsByteStream | |
} | |
else { | |
$imgBytes = Get-Content $ImagePath -Encoding Byte | |
} | |
$ImageBits = [Convert]::ToBase64String($imgBytes) | |
$ImageFile = Get-Item $ImagePath | |
$ImageType = $ImageFile.Extension.Substring(1) | |
$ImageHead = "<Img src='data:image/$ImageType;base64,$($ImageBits)' Alt='$($ImageFile.Name)' style='float:left' width='120' height='120' hspace=10>" | |
} | |
#region define html head | |
<# | |
define some HTML style | |
here's a source for HTML color codes | |
http://www.immigration-usa.com/html_colors.html | |
the code must be left justified | |
#> | |
$head = @" | |
<style> | |
body { background-color:#FFFFFF; | |
font-family:Segoe UI; | |
font-size:10pt; } | |
td, th { border:0px solid #000033; | |
border-collapse:collapse; } | |
th { color:white; | |
background-color:#000033; } | |
table, tr, td, th { padding: 5px; margin: 0px ;width:65%} | |
tr:nth-child(odd) {background-color: lightgray} | |
table { margin-left:15px; } | |
</style> | |
<Title>$ReportTitle</Title> | |
$ImageHead | |
<H1> The Morning Report</H1> | |
<H4>$(Get-Date -DisplayHint Date | Out-String)</H4> | |
<br> | |
"@ | |
#endregion | |
#prepare HTML code | |
$fragments = @() | |
#Write-Progress parameters | |
$progParam = @{ | |
Activity = $MyInvocation.MyCommand | |
Status = "" | |
CurrentOperation = "" | |
PercentComplete = 0 | |
} | |
} #Begin | |
Process { | |
$progParam.Status = $Computername | |
$progParam.CurrentOperation = "Connecting to computer" | |
Write-Progress @progParam | |
#region ping test | |
#set a default value for the ping test | |
$ok = $False | |
If ($computername -eq $env:computername) { | |
#local computer so no ping test is necessary | |
$OK = $True | |
} | |
elseIf (($computername -ne $env:computername) -AND (Test-Connection -ComputerName $computername -Quiet -Count 2)) { | |
#not local computer and it can be pinged so proceed | |
$OK = $True | |
} | |
#endregion | |
#create a CIMSession | |
$csParam = @{ | |
ErrorAction = 'stop' | |
Computername = $Computername | |
} | |
if ($Credential) { | |
$csParam.add('Credential', $Credential) | |
} | |
Try { | |
$session = New-CimSession @csParam | |
} | |
Catch { | |
Write-Warning "Failed to create CIMSession to $Computername" | |
#Bail out | |
Return | |
} | |
#region define a parameter hashtable | |
$paramhash = @{ | |
Classname = "Win32_OperatingSystem" | |
CIMSession = $session | |
Property = "Caption", "LastBootUpTime", "NumberOfProcesses", "FreePhysicalMemory", "TotalVisibleMemorySize" | |
ErrorAction = "Stop" | |
} | |
#endregion | |
If ($OK) { | |
Try { | |
#get Operating system information from WMI | |
$os = Get-CimInstance @paramhash | |
#set a variable to indicate WMI can be reached | |
$cim = $True | |
} | |
Catch { | |
Write-Warning "Failed to connect to $($computername.ToUpper()). $($_.exception.message)" | |
} | |
if ($cim) { | |
#Write-Host "Preparing morning report for $($os.CSname)" -ForegroundColor Cyan | |
$progParam.CurrentOperation = "Preparing morning report" | |
$progParam.PercentComplete = 5 | |
Write-Progress @progParam | |
#region OS Summary | |
#Write-Host "...Operating System" -ForegroundColor Cyan | |
$progParam.CurrentOperation = "...Operating system" | |
$progParam.PercentComplete += 15 | |
Write-Progress @progParam | |
$osdata = $os | Select-Object @{Name = "OS"; Expression = { $_.Caption } }, | |
freePhysicalmemory, TotalVisibleMemorySize, NumberOfProcesses, LastBootUpTime, | |
@{Name = "Uptime"; Expression = { (Get-Date) - $_.LastBootupTime } } | |
#endregion | |
#region Computer system | |
#Write-Host "...Computer System" -ForegroundColor Cyan | |
$progParam.CurrentOperation = "...computer system" | |
$progParam.PercentComplete += 15 | |
Write-Progress @progParam | |
$paramhash.Classname = "Win32_ComputerSystem" | |
$paramhash.Property = "Manufacturer", "Model", "SystemType", "NumberofProcessors", "NumberOfLogicalProcessors", "HypervisorPresent" | |
$cs = Get-CimInstance @paramhash | |
$csdata = $cs | Select-Object Manufacturer, Model, SystemType, Number*, HypervisorPresent | |
#endregion | |
#region Get Service information | |
#Write-Host "...Services" -ForegroundColor Cyan | |
$progParam.CurrentOperation = "...Services" | |
$progParam.PercentComplete += 15 | |
Write-Progress @progParam | |
#get all services via WMI and group into a hash table | |
$paramhash.Classname = "Win32_Service" | |
$paramhash.Property = "Name", "State", "StartMode", "StartName", "DisplayName" | |
$cimservices = Get-CimInstance @paramhash | |
$services = $cimservices | Group-Object State -AsHashTable -AsString | |
#get services set to auto start that are not running | |
$failedAutoStart = ($cimservices).where( { ($_.startmode -eq "Auto") -AND ($_.state -ne "Running") } ) | |
#endregion | |
#region Disk Utilization | |
#Write-Host "...Logical Disks" -ForegroundColor Cyan | |
$progParam.CurrentOperation = "...disks" | |
$progParam.PercentComplete += 15 | |
Write-Progress @progParam | |
$paramhash.Property = "DeviceID", "Size", "FreeSpace" | |
$paramhash.Classname = "Win32_LogicalDisk" | |
$paramhash.Add("Filter", "Drivetype=3") | |
$disks = Get-CimInstance @paramhash | |
$diskData = $disks | Select-Object DeviceID, | |
@{Name = "SizeGB"; Expression = { $_.size / 1GB -as [int] } }, | |
@{Name = "FreeGB"; Expression = { "{0:N2}" -f ($_.Freespace / 1GB) } }, | |
@{Name = "PercentFree"; Expression = { "{0:P2}" -f ($_.Freespace / $_.Size) } } | |
#endregion | |
#region NetworkAdapters | |
#Write-Host "...Network Adapters" -ForegroundColor Cyan | |
$progParam.CurrentOperation = "...network adapters" | |
$progParam.PercentComplete += 15 | |
Write-Progress @progParam | |
$paramhash.Property = "Name", "DeviceID", "MACAddress", "Speed", "AdapterType", "Status" | |
$paramhash.classname = "Win32_NetworkAdapter" | |
$paramhash.filter = "MACAddress Like '%'" | |
#get NICS that have a MAC address only | |
$nics = Get-CimInstance @paramhash | |
$nicdata = $nics | ForEach-Object { | |
$tempHash = @{Name = $_.Name; DeviceID = $_.DeviceID; AdapterType = $_.AdapterType; MACAddress = $_.MACAddress } | |
#get related configuation information | |
$config = $_ | Get-CimAssociatedInstance -ResultClassName "Win32_NetworkadapterConfiguration" | |
#add to temporary hash | |
$tempHash.Add("IPAddress", $config.IPAddress) | |
$tempHash.Add("IPSubnet", $config.IPSubnet) | |
$tempHash.Add("DefaultGateway", $config.DefaultIPGateway) | |
$tempHash.Add("DHCP", $config.DHCPEnabled) | |
#convert lease information if found | |
if ($config.DHCPEnabled -AND $config.DHCPLeaseObtained) { | |
$tempHash.Add("DHCPLeaseExpires", $config.DHCPLeaseExpires) | |
$tempHash.Add("DHCPLeaseObtained", $config.DHCPLeaseObtained) | |
$tempHash.Add("DHCPServer", $config.DHCPServer) | |
} | |
New-Object -TypeName PSObject -Property $tempHash | |
} | |
#endregion | |
If ($Quick) { | |
Write-Verbose "Skipping event log queries" | |
} | |
#region Event log queries | |
else { | |
#Event log errors and warnings in the last $Hours hours | |
$last = (Get-Date).AddHours(- $Hours) | |
#System Log | |
#Write-Host "...System Event Log Error/Warning since $last" -ForegroundColor Cyan | |
$progParam.CurrentOperation = "...Eventlogs since $last" | |
$progParam.PercentComplete += 15 | |
Write-Progress @progParam | |
#hashtable of optional parameters for Invoke-Command | |
$InvokeCommandParam = @{ | |
Computername = $Computername | |
ArgumentList = @("System", $Last) | |
ScriptBlock = { | |
Param ([string]$LogName, [datetime]$after) | |
$get = @{ | |
Logname = $LogName | |
Level = 2, 3 | |
StartTime = $After | |
} | |
Get-WinEvent -FilterHashtable $get | |
} | |
} | |
if ($Credential) { | |
$InvokeCommandParam.Add("Credential", $Credential) | |
} | |
$syslogData = Invoke-Command @InvokeCommandParam | | |
Select-Object -Property TimeCreated, LevelDisplayName, ID, ProviderName, Message | |
#Application Log | |
#rite-Host "...Application Event Log Error/Warning since $last" -ForegroundColor Cyan | |
#update invoke-command parameters | |
$InvokeCommandParam.ArgumentList = @("Application", $Last) | |
$applogdata = Invoke-Command @InvokeCommandParam | | |
Select-Object TimeCreated, LevelDisplayName, ID, ProviderName, Message | |
} | |
#endregion | |
} #if CIM data is available | |
#write results depending on parameter set | |
$footer = "Report v{3} run {0} by {1}\{2}" -f (Get-Date), $env:USERDOMAIN, $env:USERNAME, $reportVersion | |
#region Create HTML | |
if ($HTML) { | |
#add each computer to a navigation menu in the header | |
$head += ("<a href=#{0}_Summary>{0}</a> " -f $computername.ToUpper()) | |
$fragments += ("<H2><a name='{0}_Summary'>{0}: System Summary</a></H2>" -f $computername.ToUpper()) | |
$fragments += $osdata | ConvertTo-Html -As List -Fragment | |
$fragments += $csdata | ConvertTo-Html -As List -Fragment | |
#insert navigation bookmarks | |
if ($Quick) { | |
$nav = @" | |
<br> | |
<a href=#{0}_Services>{0} Services</a> | |
<a href='#{0}_NoAutoStart'>{0} Failed Auto Start</a> | |
<a href='#{0}_Disks'>{0} Disks</a> | |
<a href='#{0}_Network'>{0} Network</a> | |
<br> | |
"@ -f $Computername.ToUpper() | |
} | |
else { | |
$nav = @" | |
<br> | |
<a href=#{0}_Services>{0} Services</a> | |
<a href='#{0}_NoAutoStart'>{0} Failed Auto Start</a> | |
<a href='#{0}_Disks'>{0} Disks</a> | |
<a href='#{0}_Network'>{0} Network</a> | |
<a href='#{0}_SysLog'>{0} System Log</a> | |
<a href='#{0}_AppLog'>{0} Application Log</a> | |
<br> | |
"@ -f $Computername.ToUpper() | |
} | |
#add a link to the document top | |
$nav += "`n<a href='#' target='_top'>Top</a>" | |
$fragments += $nav | |
$fragments += "<br clear='All'>" | |
$fragments += ConvertTo-Html -Fragment -PreContent ("<H2><a name='{0}_Services'>{0}: Services</a></H2>" -f $computername.ToUpper()) | |
$services.keys | ForEach-Object { | |
$fragments += ConvertTo-Html -Fragment -PreContent "<H3>$_</H3>" | |
$fragments += $services.$_ | Select-Object Name, Displayname, StartMode | ConvertTo-Html -Fragment | |
#insert navigation link after each section | |
$fragments += $nav | |
} | |
$fragments += $failedAutoStart | Select-Object Name, Displayname, StartMode, State | | |
ConvertTo-Html -Fragment -PreContent ("<h3><a name='{0}_NoAutoStart'>{0}: Failed Auto Start</a></h3>" -f $computername.ToUpper()) | |
$fragments += $nav | |
$fragments += $diskdata | ConvertTo-Html -Fragment -PreContent ("<H2><a name='{0}_Disks'>{0}: Disk Utilization</a></H2>" -f $computername.ToUpper()) | |
$fragments += $nav | |
#convert nested object array properties to strings | |
$fragments += $nicdata | Select-Object Name, DeviceID, DHCP*, AdapterType, MACAddress, | |
@{Name = "IPAddress"; Expression = { $_.IPAddress | Out-String } }, | |
@{Name = "IPSubnet"; Expression = { $_.IPSubnet | Out-String } }, | |
@{Name = "IPGateway"; Expression = { $_.DefaultGateway | Out-String } } | | |
ConvertTo-Html -Fragment -PreContent ("<H2><a name='{0}_Network'>{0}: Network Adapters</a></H2>" -f $computername.ToUpper()) | |
$fragments += $nav | |
if (-Not $Quick) { | |
$fragments += $syslogData | ConvertTo-Html -Fragment -PreContent ("<H2><a name='{0}_SysLog'>{0}: System Event Log Summary</a></H2>" -f $computername.toUpper()) | |
$fragments += $nav | |
$fragments += $applogData | ConvertTo-Html -Fragment -PreContent ("<H2><a name='{0}_AppLog'>{0}: Application Event Log Summary</a></H2>" -f $computername.toUpper()) | |
$fragments += $nav | |
} | |
} | |
#endregion | |
#region Create TEXT | |
elseif ($TEXT) { | |
#prepare formatted text | |
$ReportTitle | |
"-" * ($ReportTitle.Length) | |
"System Summary" | |
($osdata | Out-String).trim() | |
$csdata | Format-List | Out-String | |
"Services" | |
$services.keys | ForEach-Object { | |
$services.$_ | Select-Object Name, Displayname, StartMode, State | |
} | Format-List | Out-String | |
"Failed Autostart Services" | |
$failedAutoStart | Select-Object Name, Displayname, StartMode, State | |
"Disk Utilization" | |
$diskdata | Format-Table -AutoSize | Out-String | |
"Network Adapters" | |
$nicdata | Format-List | Out-String | |
if (-Not $Quick) { | |
"System Event Log Summary" | |
$syslogdata | Format-List | Out-String | |
"Application Event Log Summary" | |
$applogdata | Format-List | Out-String | |
} | |
$Footer | |
} | |
#endregion | |
#region Create custom object | |
else { | |
#Write data to the pipeline as part of a custom object | |
New-Object -TypeName PSObject -Property @{ | |
OperatingSystem = $osdata | |
ComputerSystem = $csdata | |
Services = $services.keys | ForEach-Object { $services.$_ | Select-Object Name, Displayname, StartMode, State } | |
FailedAutoStart = $failedAutoStart | Select-Object Name, Displayname, StartMode, State | |
Disks = $diskData | |
Network = $nicData | |
SystemLog = $syslogdata | |
ApplicationLog = $applogdata | |
ReportVersion = $reportVersion | |
RunDate = Get-Date | |
RunBy = "$env:USERDOMAIN\$env:USERNAME" | |
} | |
} | |
#endregion | |
} #if OK | |
else { | |
#can't ping computer so fail | |
Write-Warning "Failed to ping $computername" | |
} | |
} #process | |
End { | |
#if HTML finish the report here so that if piping in | |
#computer names we get one report for all computers | |
If ($HTML) { | |
#copying fragments to clipboard for troubleshooting | |
#Write $fragments | clip | |
$head += "<br><br><hr>" | |
ConvertTo-Html -Head $head -Title $ReportTitle -PreContent ($fragments | Out-String) -PostContent "<br><I>$footer</I>" | |
} | |
#clean up | |
if ($session) { | |
$session | Remove-CimSession | |
} | |
#Write-Host "Finished!" -ForegroundColor Green | |
$progParam.PercentComplete = 100 | |
$progParam.CurrentOperation = "Completed" | |
Write-Progress @progParam | |
} | |
#end of script |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sample HTML report