Ensuring the health and performance of servers is a critical task for IT administrators. PowerShell provides a powerful way to automate these tasks and log relevant information. In this blog, we’ll walk through a PowerShell script that gathers various health metrics from a server and logs them to the Windows Event Log. This script includes system information, uptime, patch details, CPU and memory usage, disk space, network activity, stopped services, and top resource-consuming applications.
You can schedule the script every 15/10 mins as per your need using task scheduler, this script will log the output to the Event Log, in application logs with event id 1000 and Source as “ServerCheckReport”
This script uses the thresholds for CPU and RAM usage, categorizing them as OK (0-80%), Warning (80-90%), and Critical (over 90%).
- Defines the log file location: The log file is named with the current year and month.
- Function to log data: This function adds a timestamped message to the log file.
- System Information: Logs system name, OS name, and total physical memory.
- Checks if server is Physical/Virtual
- System Uptime: Calculates and logs the system uptime in days.
- Latest Patch: Logs the latest installed patch details.
- CPU Usage: Logs the average CPU load percentage.
- Memory Usage: Calculates and logs the memory usage percentage.
- Virtual Memory Usage: Logs the virtual memory usage.
- Disk Usage: Logs the usage for each logical disk.
- Network Usage: Logs bytes sent and received per second for each network adapter.
- Automatic Services: Logs any automatically starting services that are currently stopped.
- RDP Logons: Logs details of successful RDP logons in the last day.
- Top 5 CPU Consuming Apps: Logs the top 5 processes consuming the most CPU.
- Top 5 RAM Consuming Apps: Logs the top 5 processes consuming the most RAM.
- The script also formats the log messages with timestamps, making it easier to analyze later.
# Allow all scripts to run
Set-ExecutionPolicy Unrestricted -Scope LocalMachine -Force
# Define event log source and log name
$eventSource = "ServerCheckReport"
$eventLog = "Application"
# Create event source if it doesn't exist
if (-not [System.Diagnostics.EventLog]::SourceExists($eventSource)) {
New-EventLog -LogName $eventLog -Source $eventSource
}
# Initialize an array to hold log messages
$global:logMessages = @()
# Function to collect messages for event log
function Log-Data {
param (
[string]$message
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logMessage = "$timestamp - $message"
$global:logMessages += $logMessage
}
# Log system information
$systemInfo = Get-WmiObject -Class Win32_OperatingSystem
Log-Data "System Name: $($systemInfo.CSName)"
Log-Data "OS Name: $($systemInfo.Caption)"
Log-Data "Total Physical Memory: $([math]::Round($systemInfo.TotalVisibleMemorySize / 1MB, 2)) GB"
# Log system uptime
$lastBootTime = $systemInfo.ConvertToDateTime($systemInfo.LastBootUpTime)
$uptime = (Get-Date) - $lastBootTime
$uptimeDays = [math]::Floor($uptime.TotalDays)
$uptimeTime = $uptime.ToString("hh\:mm\:ss")
$uptimeFormatted = "$uptimeDays days, $uptimeTime"
Log-Data "System Uptime: $uptimeFormatted"
# Determine if the server is virtual or physical
$computerSystem = Get-WmiObject -Class Win32_ComputerSystem
$manufacturer = $computerSystem.Manufacturer
$model = $computerSystem.Model
if ($manufacturer -match "Microsoft Corporation" -and $model -match "Virtual") {
Log-Data "Server Type: Virtual (Microsoft)"
} elseif ($manufacturer -match "VMware, Inc." -or $model -match "VMware Virtual Platform") {
Log-Data "Server Type: Virtual (VMware)"
} elseif ($manufacturer -match "Xen" -or $model -match "HVM domU") {
Log-Data "Server Type: Virtual (Xen)"
} elseif ($manufacturer -match "KVM" -or $model -match "KVM") {
Log-Data "Server Type: Virtual (KVM)"
} else {
Log-Data "Server Type: Physical"
}
# Log the last 2 patches installed details
$patches = Get-Hotfix | Sort-Object InstalledOn -Descending | Select-Object -First 2
foreach ($patch in $patches) {
Log-Data "Patch Installed: $($patch.Description) (KB$($patch.HotFixID)) on $($patch.InstalledOn)"
}
# Log CPU usage
$cpuLoad = Get-WmiObject -Class Win32_Processor | Measure-Object -Property LoadPercentage -Average | Select -ExpandProperty Average
$statusCpu = if ($cpuLoad -lt 80) { "OK" } elseif ($cpuLoad -lt 90) { "Warning" } else { "Critical" }
Log-Data "CPU Load: $cpuLoad% - Status: $statusCpu"
# Log memory usage
$memoryStatus = Get-WmiObject -Class Win32_OperatingSystem
$usedMemory = $memoryStatus.TotalVisibleMemorySize - $memoryStatus.FreePhysicalMemory
$memoryUsage = [math]::Round(($usedMemory / $memoryStatus.TotalVisibleMemorySize) * 100, 2)
$statusMemory = if ($memoryUsage -lt 80) { "OK" } elseif ($memoryUsage -lt 90) { "Warning" } else { "Critical" }
Log-Data "Memory Usage: $memoryUsage% - Status: $statusMemory"
# Log virtual memory usage
$virtualMemoryStatus = Get-WmiObject -Class Win32_PageFileUsage
# Get total and used virtual memory
$totalVirtualMemory = [math]::Round($virtualMemoryStatus.AllocatedBaseSize, 2) # Total virtual memory in MB
$usedVirtualMemory = [math]::Round($virtualMemoryStatus.CurrentUsage, 2) # Used virtual memory in MB
# Calculate the percentage of used virtual memory
$usedVirtualMemoryPercentage = [math]::Round(($usedVirtualMemory / $totalVirtualMemory) * 100, 2)
$statusVirtualMemory = if ($usedVirtualMemoryPercentage -lt 80) { "OK" } elseif ($usedVirtualMemoryPercentage -lt 90) { "Warning" } else { "Critical" }
Log-Data "Total Virtual Memory: $totalVirtualMemory MB"
Log-Data "Used Virtual Memory: $usedVirtualMemory MB"
Log-Data "Used Virtual Memory Percentage: $usedVirtualMemoryPercentage% - Status: $statusVirtualMemory"
# Log disk usage for each logical disk
$logicalDisks = Get-WmiObject -Class Win32_LogicalDisk -Filter "DriveType=3"
foreach ($disk in $logicalDisks) {
$freeSpaceGB = [math]::Round($disk.FreeSpace / 1GB, 2)
$totalSpaceGB = [math]::Round($disk.Size / 1GB, 2)
$diskUsage = [math]::Round((($disk.Size - $disk.FreeSpace) / $disk.Size) * 100, 2)
$statusDisk = if ($diskUsage -lt 80) { "OK" } elseif ($diskUsage -lt 90) { "Warning" } else { "Critical" }
Log-Data "Disk $($disk.DeviceID): $diskUsage% used ($freeSpaceGB GB free of $totalSpaceGB GB) - Status: $statusDisk"
}
# Log network usage
$networkAdapters = Get-WmiObject -Class Win32_PerfFormattedData_Tcpip_NetworkInterface
foreach ($adapter in $networkAdapters) {
Log-Data "Network Adapter $($adapter.Name): Bytes Sent/sec $($adapter.BytesSentPersec), Bytes Received/sec $($adapter.BytesReceivedPersec)"
}
# Log status of services that are stopped and have startup type "Automatic"
$automaticServices = Get-WmiObject -Class Win32_Service -Filter "StartMode='Auto' AND State='Stopped'"
foreach ($service in $automaticServices) {
Log-Data "Service $($service.DisplayName): Stopped"
}
# Get the current date and time
$currentDate = Get-Date
# Calculate the date 1 days ago
$startDate = $currentDate.AddDays(-1)
# Query the Security Event Log for successful logons (Event ID 4624) in the last 5 days
$logonEvents = Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4624
StartTime = $startDate
} | Where-Object {
$_.Properties[8].Value -eq '10' # Logon Type 10 indicates RDP logon
}
# Process each logon event
foreach ($event in $logonEvents) {
$userName = $event.Properties[5].Value
$sourceIP = $event.Properties[18].Value # Source IP address
$logonTime = $event.TimeCreated
Log-Data "User: $userName logged in via RDP at $logonTime from IP: $sourceIP"
}
# Log top 5 apps consuming CPU in percentage
$topCpuProcesses = Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 5
Log-Data "Top 5 CPU Consuming Apps:"
foreach ($process in $topCpuProcesses) {
$cpuUsageSample = Get-Counter -Counter "\Process($($process.Name))\% Processor Time" -SampleInterval 1 -MaxSamples 1
$cpuPercent = [math]::Round($cpuUsageSample.CounterSamples[0].CookedValue / (Get-WmiObject Win32_ComputerSystem).NumberOfLogicalProcessors, 2)
Log-Data "App: $($process.Name) - CPU: $cpuPercent%"
}
# Log top 5 apps consuming RAM in percentage
$totalMemory = (Get-WmiObject Win32_ComputerSystem).TotalPhysicalMemory
$topRamProcesses = Get-Process | Sort-Object -Property WorkingSet64 -Descending | Select-Object -First 5
Log-Data "Top 5 RAM Consuming Apps:"
foreach ($process in $topRamProcesses) {
$ramPercent = [math]::Round((($process.WorkingSet64 / $totalMemory) * 100), 2)
Log-Data "App: $($process.Name) - RAM: $ramPercent%"
}
# Insert a line break at the end
Log-Data "`n"
# Write consolidated log message to event log
if ($global:logMessages.Count -gt 0) {
$consolidatedMessage = [string]::Join("`n", $global:logMessages)
Write-EventLog -LogName $eventLog -Source $eventSource -EntryType Information -EventId 1000 -Message $consolidatedMessage
}
Write-Host "Performance, RDP logon data, and app information logged to Windows Event Log"





