Get-SystemStatus/Get-SystemStatus.ps1
2021-09-14 14:12:03 -06:00

416 lines
14 KiB
PowerShell

<#
.SYNOPSIS
Gets various information related to a running system's status for reporting.
.DESCRIPTION
Gathers the follow information from a provided list of computers.
The collected data is then returned as a DataTable or can be exported to a variety of file formats.
Optional Metadata can be included to be included in the returned results/reports to assist in machine identification.
Collected Data: Computer Name, Operating System, Operating System Build, Uptime, Recent Updates, Disk Space Utilization, Date Gathered
Optional Metadata: Organization, Role, UpdateWindow
.PARAMETER ComputerName
Specifies a computer or set of computers that should be queried for their status.
.PARAMETER CsvPath
Path to a CSV that contains the list computers that should be collected.
Optional metadata can be included that will be included in returned data.
Columns:
Name*
Organization
Role
UpdateWindow
*: Required
.PARAMETER Credential
Credentials used for connecting to the specified computers to gather the machine's status information.
.PARAMETER CredentialPath
Path to stored credentials that will be imported using Import-Clixml to allow for storing credentials using a secure method.
.PARAMETER OutputType
Style of report that should be output. Defaults to JSON output but can be exported as a CSV or JSON.
.PARAMETER OutputPath
Path to where the report should be saved.
.PARAMETER Organization
Optional metadata that can be included in the returned results.
.PARAMETER Role
Optional metadata that can be included in the returned results.
.PARAMETER UpdateWindow
Optional metadata that can be included in the returned results.
.NOTES
Version: 1.0
Author: Tyler Hale
Creation Date: 2021.09.14
#>
[CmdletBinding(DefaultParameterSetName = 'Local')]
param (
[Parameter(Mandatory = $true, ParameterSetName = "CnDefaultCred")]
[Parameter(Mandatory = $true, ParameterSetName = "CnDefaultCredReport")]
[Parameter(Mandatory = $true, ParameterSetName = "CnCred")]
[Parameter(Mandatory = $true, ParameterSetName = "CnCredReport")]
[Parameter(Mandatory = $true, ParameterSetName = "CnStoredCred")]
[Parameter(Mandatory = $true, ParameterSetName = "CnStoredCredReport")]
[ValidateScript( { Test-Connection $_ -Count 2 } )]
[alias('Cn')]
[string[]]
$ComputerName,
[Parameter(Mandatory = $true, ParameterSetName = "CsvDefaultCred")]
[Parameter(Mandatory = $true, ParameterSetName = "CsvDefaultCredReport")]
[Parameter(Mandatory = $true, ParameterSetName = "CsvCred")]
[Parameter(Mandatory = $true, ParameterSetName = "CsvCredReport")]
[Parameter(Mandatory = $true, ParameterSetName = "CsvStoredCred")]
[Parameter(Mandatory = $true, ParameterSetName = "CsvStoredCredReport")]
[ValidateNotNullOrEmpty()]
[ValidateScript( { Test-Path -Path $_ } )]
[string]
$CsvPath,
[Parameter(Mandatory = $true, ParameterSetName = "CnCred")]
[Parameter(Mandatory = $true, ParameterSetName = "CnCredReport")]
[Parameter(Mandatory = $true, ParameterSetName = "CsvCred")]
[Parameter(Mandatory = $true, ParameterSetName = "CsvCredReport")]
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credential = [System.Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $true, ParameterSetName = "CnStoredCred")]
[Parameter(Mandatory = $true, ParameterSetName = "CnStoredCredReport")]
[Parameter(Mandatory = $true, ParameterSetName = "CsvStoredCred")]
[Parameter(Mandatory = $true, ParameterSetName = "CsvStoredCredReport")]
[ValidateNotNullOrEmpty()]
[ValidateScript( { Test-Path -Path $_ })]
[string]
$CredentialPath,
[Parameter(Mandatory = $false, ParameterSetName = "LocalReport")]
[Parameter(Mandatory = $false, ParameterSetName = "CnDefaultCredReport")]
[Parameter(Mandatory = $false, ParameterSetName = "CnCredReport")]
[Parameter(Mandatory = $false, ParameterSetName = "CnStoredCredReport")]
[Parameter(Mandatory = $false, ParameterSetName = "CsvDefaultCredReport")]
[Parameter(Mandatory = $false, ParameterSetName = "CsvCredReport")]
[Parameter(Mandatory = $false, ParameterSetName = "CsvStoredCredReport")]
[ValidateSet("CSV", "JSON", "HTML")]
[string]
$OutputType = "JSON",
[Parameter(Mandatory = $true, ParameterSetName = "LocalReport")]
[Parameter(Mandatory = $true, ParameterSetName = "CnDefaultCredReport")]
[Parameter(Mandatory = $true, ParameterSetName = "CnCredReport")]
[Parameter(Mandatory = $true, ParameterSetName = "CnStoredCredReport")]
[Parameter(Mandatory = $true, ParameterSetName = "CsvDefaultCredReport")]
[Parameter(Mandatory = $true, ParameterSetName = "CsvCredReport")]
[Parameter(Mandatory = $true, ParameterSetName = "CsvStoredCredReport")]
[string]
$OutputPath,
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]
$Organization,
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]
$Role,
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]
$UpdateWindow
)
############################################### [Script Settings] ################################################
# Define Default Columns
[System.Collections.ArrayList]$Columns = @(
"Computer Name",
"Operating System",
"Operating System Build",
"Uptime",
"Recent Updates",
"Disk Space Utilization",
"Date Gathered"
)
# Define optional columns that will be removed if not provided
[System.Collections.ArrayList]$OptionalColumns = @(
"Role"
"Organization"
"UpdateWindow"
)
# HTML Header for table formatting
$Head = @"
<style>
body {
font-family: "Arial";
font-size: 8pt;
color: #4C607B;
}
th, td {
border: 1px solid #e57300;
border-collapse: collapse;
padding: 5px;
}
th {
font-size: 1.2em;
text-align: left;
background-color: #003366;
color: #ffffff;
white-space:nowrap;
}
td {
color: #000000;
white-space: pre;
}
.even { background-color: #ffffff; }
.odd { background-color: #bfbfbf; }
</style>
"@
################################################## [Functions] ###################################################
function Format-DataSizes ($Size) {
switch ($Size) {
{ $_ -ge 1PB } { "{0:#.#'P'}" -f ($Size / 1PB); break }
{ $_ -ge 1TB } { "{0:#.#'T'}" -f ($Size / 1TB); break }
{ $_ -ge 1GB } { "{0:#.#'G'}" -f ($Size / 1GB); break }
{ $_ -ge 1MB } { "{0:#.#'M'}" -f ($Size / 1MB); break }
{ $_ -ge 1KB } { "{0:#'K'}" -f ($Size / 1KB); break }
default { "{0}" -f ($Size) + "B" }
}
}
function Format-TimeSpan {
process {
"{0:00} Day(s) {1:00}:{2:00}:{3:00}" -f $_.Days, $_.Hours, $_.Minutes, $_.Seconds
}
}
function Add-RowEntry {
[CmdletBinding()]
param (
$Table,
$Name = $OperatingSystem.CSName,
$Role,
$Organization = $OperatingSystem.Organization,
$OperatingSystem,
$LogicalDisk,
$Updates,
$UpdateWindow,
$DateGathered,
$OptionalColumns
)
# Create a row
$Row = $Table.NewRow()
# Define the computer name
$Row."Computer Name" = $Name
$Row."Date Gathered" = $DateGathered
# OS
if ($null -ne ($OperatingSystem)) {
$Row."Operating System" = "$($OperatingSystem.Caption)"
$Row."Operating System Build" = "$($OperatingSystem.Version)"
$Row."Uptime" = "$((New-TimeSpan -Start ($OperatingSystem.LastBootUpTime) -End $DataGatheredDate) | Format-TimeSpan)"
}
# Disk
if ($null -ne ($LogicalDisk)) {
[string]$DiskUsage = ""
$LogicalDisk | ForEach-Object {
if ($null -ne $_.Size) {
$FreeSpace = Format-DataSizes -Size $_.FreeSpace
$TotalSize = Format-DataSizes -Size $_.Size
$DiskUsage += "$($_.DeviceID) $($TotalSize) Total \ $($FreeSpace) Free - {0:P0}`n" -f ($_.FreeSpace / $_.Size)
}
}
$Row."Disk Space Utilization" = $DiskUsage.TrimEnd()
}
# Updates
if ($null -ne ($Updates)) {
$Row."Recent Updates" = ""
$RecentUpdates = ($Updates | Sort-Object InstalledOn -Descending) | Select-Object -First 3
foreach ($Update in $RecentUpdates) {
$Row."Recent Updates" += "$(($Update.InstalledOn | Select-String -Pattern "(.*)(?= 00:00:00)").Matches.Value) - $($Update.HotFixID)`n"
}
}
# Optional Columns
foreach ($OptionalColumn in $OptionalColumns) {
if ($Table.Columns -match "$OptionalColumn") {
$Row."$OptionalColumn" = (Get-Variable -Name "$OptionalColumn").Value
}
}
# Add the row to the table
$Table.Rows.Add($Row)
}
############################################# [Internal Processing] ##############################################
# Change to the script's current directory to include support for relative paths
Set-Location $PSScriptRoot
# Determine execution scope based on parameter set
Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)"
switch -Wildcard ($PSCmdlet.ParameterSetName) {
"Local*" {
$Computers = [PSCustomObject]@{
Name = $env:COMPUTERNAME
Role = $Role
Organization = $Organization
UpdateWindow = $UpdateWindow
}
}
"Cn*" {
# If the only computer name provided is the current machine,
# Switch back to using the Local parameter set and drop the
# ComputerName variable to avoid needing additional permissions
if ($ComputerName -ne $env:COMPUTERNAME) {
$Computers = foreach ($Computer in $ComputerName) {
[PSCustomObject]@{
Name = $Computer
Role = $Role
Organization = $Organization
UpdateWindow = $UpdateWindow
}
}
}
else {
$Computers = [PSCustomObject]@{
Name = $env:COMPUTERNAME
Role = $Role
Organization = $Organization
UpdateWindow = $UpdateWindow
}
Remove-Variable ComputerName
}
}
"Csv*" {
$Computers = Import-Csv -Path $CsvPath
[string[]]$ComputerName = $Computers.Name
}
}
# Add Optional Columns if they exist
foreach ($OptionalColumn in $OptionalColumns) {
switch ($OptionalColumn) {
"Organization" { [int]$ColumnPosition = 1 }
"Role" { [int]$ColumnPosition = 1 }
Default { [int]$ColumnPosition = $Columns.Count }
}
if ($null -ne $Computers."$($OptionalColumn)") {
if ('' -ne ($Computers."$($OptionalColumn)" | Out-String).Trim()) {
$Columns.Insert($ColumnPosition, "$OptionalColumn")
}
}
}
# If using stored credentials, import the credentials for future use
if ($PSCmdlet.ParameterSetName -like "*StoredCred*") {
$Credential = Import-Clixml -Path $CredentialPath
}
# Build Remote Parameters splat variable
$RemoteParameters = @{}
if ($null -ne $ComputerName) {
Write-Verbose "Adding $ComputerName to Remote Parameters splat variable"
$RemoteParameters['ComputerName'] = $ComputerName
$RemoteParameters['Authentication'] = "Kerberos"
}
if ($Credential -ne [System.Management.Automation.PSCredential]::Empty) {
Write-Verbose "Adding provided credentials to Remote Parameters splat variable"
$RemoteParameters['Credential'] = $Credential
$RemoteParameters['Authentication'] = "Kerberos"
}
# Create Table object
$Table = New-Object system.Data.DataTable "Report"
foreach ($Column in $Columns) {
$Table.Columns.Add((New-Object system.Data.DataColumn $Column, ([string])))
$Table.Columns[$Column].DefaultValue = "Unknown"
}
################################################## [Execution] ###################################################
# Gather Cim Data
Write-Verbose "Started Data gathering: $(Get-Date -Format o)"
$CimMasterSession = New-CimSession @RemoteParameters
$CimMasterData = @{}
$CimMasterData["OS"] = Get-CimInstance -ClassName Win32_OperatingSystem -CimSession $CimMasterSession
$CimMasterData["Drives"] = Get-CimInstance -ClassName "Win32_LogicalDisk" -Namespace "root\CIMV2" -CimSession $CimMasterSession
$CimMasterData["Updates"] = Get-CimInstance -ClassName "Win32_QuickfixEngineering" -CimSession $CimMasterSession
$DataGatheredDate = Get-Date -Format o
Write-Verbose "Finished Data gathering: $DataGatheredDate"
Remove-CimSession -CimSession $CimMasterSession
foreach ($Computer in ($Computers | Sort-Object Name)) {
Write-Verbose "Processing $($Computer.Name) - $(Get-Date -Format o)"
# Add Optional Parameters to the row as needed
$OptionalParameters = @{"OptionalColumns" = $OptionalColumns }
foreach ($OptionalColumn in $OptionalColumns) {
if ($null, '' -ne $Computer."$($OptionalColumn)") {
$OptionalParameters["$($OptionalColumn)"] = $Computer."$($OptionalColumn)"
}
}
# Created the row based on the provided data
# Local execution sets the PSComputerName as null
if ($null -ne $ComputerName) {
Add-RowEntry -Table $Table `
-Name $Computer.Name `
-OperatingSystem ($CimMasterData.OS | Where-Object { $_.PSComputerName -eq "$($Computer.Name)" }) `
-LogicalDisk ($CimMasterData.Drives | Where-Object { $_.PSComputerName -eq "$($Computer.Name)" }) `
-Updates ($CimMasterData.Updates | Where-Object { $_.PSComputerName -eq "$($Computer.Name)" }) `
-DateGathered $DataGatheredDate `
@OptionalParameters
}
else {
Add-RowEntry -Table $Table `
-Name $Computer.Name `
-OperatingSystem ($CimMasterData.OS | Where-Object { $null -eq $_.PSComputerName }) `
-LogicalDisk ($CimMasterData.Drives | Where-Object { $null -eq $_.PSComputerName }) `
-Updates ($CimMasterData.Updates | Where-Object { $null -eq $_.PSComputerName }) `
-DateGathered $DataGatheredDate `
@OptionalParameters
}
}
if ($PSCmdlet.ParameterSetName -like "*Report*") {
switch ($OutputType) {
"CSV" { $Table | Export-Csv -Path $OutputPath -NoTypeInformation }
"JSON" { $Table | Select-Object * -ExcludeProperty ItemArray, Table, RowError, RowState, HasErrors | ConvertTo-Json | Out-File -FilePath $OutputPath }
"HTML" { $Table | Select-Object -Property $Columns | ConvertTo-HTML -Head $Head -Body "<font color=`"Black`"><h2>System Status Update</h2><h3>Date Gathered: $DataGatheredDate</h3></font>" | Set-Content $OutputPath }
}
}
else {
$Table
}