Skip to content

Instantly share code, notes, and snippets.

@jdhitsolutions
Last active November 10, 2023 13:40
Show Gist options
  • Save jdhitsolutions/b5b2fcc047cedc252b7f169ffa7e9642 to your computer and use it in GitHub Desktop.
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
#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
@jdhitsolutions
Copy link
Author

Sample HTML report
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment