Can someone help me by taking this code and making it run as Job/Runspace so it runs on all systems and reports back? This works when I run against a list of servers, but takes for ever as it goes 1 by 1. I want to have all servers run through and append to CSV files. Am I thinking right here or what are your thoughts?
$CurrentDir = split-path -Path $MyInvocation.MyCommand.Definition -Parent
## This is the location the script will save the output file
$OutputFile = "$CurrentDir\PatchStatus.htm"
$Computers = Get-Content "$CurrentDir\Servers.txt"
$Date = (Get-Date).AddDays(-30)
Function Get-RebootEvents
{
[CmdletBinding()]
Param (
[string[]]$ComputerName = $env:COMPUTERNAME
)
try
{
$RebootEvents = Get-WinEvent -ComputerName $Computer -FilterHashTable @{ LogName = "System"; StartTime = $Date; ID = 1074, 1076 } -ErrorAction Stop
}
catch
{
if ($_.Exception.Message -match "No events were found that match the specified selection criteria")
{
$ErrorMessage = "$Computer - $($Error[0])"
}
}
IF (($ErrorMessage) -or ($RebootEvents -eq $null))
{
$RebootObject = [PSCustomObject]@{
'Date' = $null
'User' = $null
'Action' = $null
'Process' = $null
'Reason' = $null
'ReasonCode' = $null
'Comment' = $ErrorMessage
}
$RebootObject
}
ELSE
{
ForEach ($RebootEvent in $RebootEvents)
{
$RebootObject = [PSCustomObject]@{
'Date' = Get-Date -UFormat "%m/%d/%Y %r" $RebootEvent.TimeCreated
'User' = $RebootEvent.Properties[6].Value
'Action' = $RebootEvent.Properties[0].Value
'Process' = $RebootEvent.Properties[4].Value
'Reason' = $RebootEvent.Properties[2].Value
'ReasonCode' = $RebootEvent.Properties[3].Value
'Comment' = $RebootEvent.Properties[5].Value
}
$RebootObject
}
}
}
Function Get-UpdateEvents
{
[CmdletBinding()]
Param (
[string[]]$ComputerName = $env:COMPUTERNAME
)
try
{
$UpdateEvents = Get-WinEvent -ComputerName $Computer -FilterHashTable @{ LogName = "System"; StartTime = $Date; ID = 19, 20 } -ErrorAction Stop | Where { $_.Message -notlike "*Defender*" -and ($_.Message -like "Installation*") -and ($_.Message -notlike "*Malicious*") } | Select TimeCreated, Message
}
catch
{
if ($_.Exception.Message -match "No events were found that match the specified selection criteria")
{
$ErrorMessage = "$Computer - $($Error[0])"
}
}
IF (($ErrorMessage) -or ($UpdateEvents -eq $null))
{
$UpdateObject = [PSCustomObject]@{
'Date' = $null
'Status' = $null
'Message' = $ErrorMessage
'KB' = $null
}
$UpdateObject
}
ELSE
{
ForEach ($UpdateEvent in $UpdateEvents)
{
$UpdateMessage = $UpdateEvent.Message -split '(?<=:\s*\S+)\s+'
$UpdateStatus = $UpdateMessage[0] -split ":"
$Status = $UpdateStatus[0]
$Message = $UpdateMessage[2]
$KB = [regex]::matches($Message, '(?<=\().+?(?=\))').value
Try
{
#$UpdateDate = Get-Date -UFormat "%m/%d/%Y %r" $UpdateEvent.TimeCreated
$UpdateDate = $UpdateEvent.TimeCreated
}
CATCH
{
if ($_.Exception.Message -like "*Cannot convert null to type *System.DateTime*")
{
$ErrorMessage = "$Computer - $($Error[0])"
$UpdateDate = $null
Write-Host $ErrorMessage
}
}
$UpdateObject = [PSCustomObject]@{
'Date' = $UpdateDate
'Status' = $Status
'Message' = $Message
'KB' = $KB
}
$UpdateObject
}
}
}
Function Get-OSInformation
{
[CmdletBinding()]
Param (
[string[]]$ComputerName = $env:COMPUTERNAME
)
try
{
$OS = Get-WmiObject Win32_OperatingSystem -ComputerName $Computer -ErrorAction Stop
}
catch
{
$ErrorMessage = "$($Error[0])"
}
IF (($ErrorMessage) -or ($OS -eq $null))
{
$OSObject = [PSCustomObject]@{
'OS' = $ErrorMessage
'OSVersion' = $null
'Lastboot' = $null
'UptimeDays' = $null
}
$OSObject
}
ELSE
{
$Uptime = (Get-Date) - $OS.ConvertToDateTime($OS.LastBootUpTime)
$OSObject = [PSCustomObject]@{
'OS' = $OS.Caption
'OSVersion' = $OS.Version
'Lastboot' = $OS.ConvertToDateTime($OS.LastBootUpTime)
'UptimeDays' = ([String]$Uptime.Days)
}
$OSObject
}
}
Function Get-PendingRebootStatus
{
<#
.Synopsis
This will check to see if a server or computer has a reboot pending.
For updated help and examples refer to -Online version.
.DESCRIPTION
This will check to see if a server or computer has a reboot pending.
For updated help and examples refer to -Online version.
.NOTES
Name: Get-PendingRebootStatus
Author: The Sysadmin Channel
Version: 1.00
DateCreated: 2018-Jun-6
DateUpdated: 2018-Jun-6
.LINK
https://thesysadminchannel.com/remotely-check-pending-reboot-status-powershell -
.PARAMETER ComputerName
By default it will check the local computer.
.EXAMPLE
Get-PendingRebootStatus -ComputerName PAC-DC01, PAC-WIN1001
Description:
Check the computers PAC-DC01 and PAC-WIN1001 if there are any pending reboots.
#>
[CmdletBinding()]
Param (
[Parameter(
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 0)]
[string[]]$ComputerName = $env:COMPUTERNAME,
[switch]$ShowErrors
)
BEGIN
{
$ErrorsArray = @()
}
PROCESS
{
foreach ($Computer in $ComputerName)
{
try
{
$PendingReboot = $false
$HKLM = [UInt32] "0x80000002"
$WMI_Reg = [WMIClass] "\\$Computer\root\default:StdRegProv"
if ($WMI_Reg)
{
if (($WMI_Reg.EnumKey($HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\")).sNames -contains 'RebootPending') { $PendingReboot = $true }
if (($WMI_Reg.EnumKey($HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\")).sNames -contains 'RebootRequired') { $PendingReboot = $true }
#Checking for SCCM namespace
$SCCM_Namespace = Get-WmiObject -Namespace ROOT\CCM\ClientSDK -List -ComputerName $Computer -ErrorAction Ignore
if ($SCCM_Namespace)
{
if (([WmiClass]"\\$Computer\ROOT\CCM\ClientSDK:CCM_ClientUtilities").DetermineIfRebootPending().RebootPending -eq 'True') { $PendingReboot = $true }
}
if ($PendingReboot -eq $true)
{
$Properties = @{
ComputerName = $Computer.ToUpper()
PendingReboot = 'True'
}
$Object = New-Object -TypeName PSObject -Property $Properties | Select ComputerName, PendingReboot
}
else
{
$Properties = @{
ComputerName = $Computer.ToUpper()
PendingReboot = 'False'
}
$Object = New-Object -TypeName PSObject -Property $Properties | Select ComputerName, PendingReboot
}
}
}
catch
{
$Properties = @{
ComputerName = $Computer.ToUpper()
PendingReboot = 'Error'
}
$Object = New-Object -TypeName PSObject -Property $Properties | Select ComputerName, PendingReboot
$ErrorMessage = $Computer + " Error: " + $_.Exception.Message
$ErrorsArray += $ErrorMessage
}
finally
{
Write-Output $Object
$Object = $null
$ErrorMessage = $null
$Properties = $null
$WMI_Reg = $null
$SCCM_Namespace = $null
}
}
if ($ShowErrors)
{
Write-Output "`n"
Write-Output $ErrorsArray
}
}
END { }
}
function Get-UpdateStatus
{
Foreach ($Computer in $Computers)
{
$OnlineStatus = "Offline"
Write-Host "Testing Connection to $Computer"
$IPAddress = Test-Connection -ComputerName $Computer -Count 1 -Quiet
if ($IPAddress)
{
$OnlineStatus = "Online"
$Rebootedby = (Get-RebootEvents -ComputerName $Computer | Sort Date -Descending | Select -First 1).User
### Adding [PSObject[]] Forces this to be an array only no matter how many objects
[PSObject[]]$UpdateEvents = Get-UpdateEvents -ComputerName $Computer | select Date, Status, Message, KB, ErrorMessage
$OSInformation = Get-OSInformation -ComputerName $Computer | select OS, OSVersion, Lastboot, UptimeDays
$RebootInformation = Get-PendingRebootStatus -ComputerName $Computer | select ComputerName, PendingReboot
$infoColl = @()
ForEach ($UpdateEvent in $UpdateEvents)
{
$infoObject = New-Object PSObject
$infoObject | add-member -memberType NoteProperty -name "ComputerName" -value $Computer
$infoObject | add-member -memberType NoteProperty -name "OnlineStatus" -value $OnlineStatus
$infoObject | add-member -memberType NoteProperty -name "OS" -value $OSInformation.OS
$infoObject | add-member -memberType NoteProperty -name "OSVersion" -value $OSInformation.OSVersion
$infoObject | add-member -memberType NoteProperty -name "PendingReboot" -value $RebootInformation.PendingReboot
$infoObject | add-member -memberType NoteProperty -name "LastBoot" -value $OSInformation.Lastboot
$infoObject | add-member -memberType NoteProperty -name "Rebootedby" -value $Rebootedby
$infoObject | add-member -memberType NoteProperty -name "Uptime_Days" -value $OSInformation.UptimeDays
$infoObject | add-member -memberType NoteProperty -name "Date" -value $UpdateEvent.Date
$infoObject | add-member -memberType NoteProperty -name "Status" -value $UpdateEvent.Status
$infoObject | add-member -memberType NoteProperty -name "Message" -value $UpdateEvent.Message
$infoObject | add-member -memberType NoteProperty -name "KB" -value $UpdateEvent.KB
$infoObject | add-member -memberType NoteProperty -name "ErrorMessage" -value $UpdateEvent.ErrorMessage
$infoColl += $infoObject
$infoObject
}
}
ELSE
{
Write-Host "$Computer is $OnlineStatus"
$infoObject = New-Object PSObject
$infoObject | add-member -memberType NoteProperty -name "ComputerName" -value $Computer
$infoObject | add-member -memberType NoteProperty -name "OnlineStatus" -value $OnlineStatus
$infoObject | add-member -memberType NoteProperty -name "OS" -value $null
$infoObject | add-member -memberType NoteProperty -name "OSVersion" -value $null
$infoObject | add-member -memberType NoteProperty -name "PendingReboot" -value $null
$infoObject | add-member -memberType NoteProperty -name "LastBoot" -value $null
$infoObject | add-member -memberType NoteProperty -name "Rebootedby" -value $null
$infoObject | add-member -memberType NoteProperty -name "Uptime_Days" -value $null
$infoObject | add-member -memberType NoteProperty -name "Date" -value $null
$infoObject | add-member -memberType NoteProperty -name "Status" -value $null
$infoObject | add-member -memberType NoteProperty -name "Message" -value $null
$infoObject | add-member -memberType NoteProperty -name "ErrorMessage" -value $null
$infoColl += $infoObject
$infoObject
}
}
}
Get-UpdateStatus | Export-Csv -path "$CurrentDir\Update_Status_$((Get-Date).ToString('MM-dd-yyyy')).csv" -Append -NoTypeInformation