<# .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 = @" "@ ################################################## [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 "

System Status Update

Date Gathered: $DataGatheredDate

" | Set-Content $OutputPath } } } else { $Table }