https://www.techtarget.com/searchwindowsserver/tutorial/Implement-simple-server-monitoring-with-PowerShell
As your server inventory expands, you will need assistance to ensure you can head off any problems.
There are many server monitoring and reporting options in the market, but certain situations may call for a lightweight solution. Monitoring with PowerShell is a way to use the native functionality in Windows to create scripts that check your systems and send regular updates.
This article explores how to create a simple framework for Windows Server checks with the added benefit of generating reports at different intervals to assist with your monitoring efforts. While PowerShell 7 is available, this tutorial is based on Windows PowerShell 5.1 due to the ease of its default PowerShell remoting setup process.
While there are many server checks you can perform, this article will concentrate on just a few to demonstrate the capabilities of a simple PowerShell monitoring framework. This tutorial will cover:
The overall idea behind this server monitoring framework is to run a series of checks on one or more servers, save the results and then review the findings. To do this, we will create functions in the PowerShell script.
There are many ways to structure a potential script to do server checks, but this tutorial will place all checks in a hashtable that uses scriptblocks, which makes it easy to run the code on the requested computers, either local or remotely.
Additionally, the script uses a condition key and associated scriptblock to report whether the check has passed or failed. Every condition should return a Boolean value.
Adding new checks is as easy as adding a new top-level key with a sub-key of check and condition. Both are scriptblocks run by Invoke-Command within the Invoke-ServerCheck function.
$Checks = @{
'OSVersion' = @{
'Check' = {
Get-CimInstance Win32_OperatingSystem | Select-Object Caption, Version, ServicePackMajorVersion, OSArchitecture
}
'Condition' = {$_.Version -LT 6.2}
}
'Certificates' = @{
'Check' = {
Get-ChildItem -Path 'Cert:' -Recurse -ExpiringInDays 30 | Select-Object Subject, NotAfter
}
'Condition' = {$Result.Count -GT 0}
}
'DiskSpace' = @{
'Check' = {
Get-CIMInstance -Class 'Win32_logicaldisk' -Filter "DriveType = '3'" | Select-Object -Property DeviceID, @{L='FreeSpaceGB';E={"{0:N2}" -f ($_.FreeSpace /1GB)}}, @{L="Capacity";E={"{0:N2}" -f ($_.Size/1GB)}}
}
'Condition' = { ($Result | Where-Object { (($_.FreeSpaceGB / $_.Capacity) * 100) -LT 10 }).Count -GT 0 }
}
'License' = @{
'Check' = {
Enum Licensestatus {
Unlicensed = 0
Licensed = 1
OOBGrace = 2
OOTGrace = 3
NonGenuineGrace = 4
Notification = 5
ExtendedGrace = 6
}
Get-CimInstance -ClassName SoftwareLicensingProduct -Filter "PartialProductKey IS NOT NULL" | Select-Object Name, ApplicationId, @{N='LicenseStatus'; E={[LicenseStatus]$_.LicenseStatus} }
}
'Condition' = {($Results | Where-Object LicenseStatus -NE 'Licensed').Count -EQ 0}
}
}
The Invoke-ServerCheck function handles the bulk of the server monitoring with PowerShell. This function takes in an array of checks from the $Checks variable, then each set of checks will be run across the servers or the local computer.
The script executes the following steps:
Function Invoke-ServerCheck {
[CmdletBinding()]
Param(
[Parameter(Position = 0, Mandatory = $True)]$Checks,
[Parameter(Position = 1, ValueFromPipeline = $True)]$ComputerName,
[Parameter(Position = 2)]$Path = $Env:TEMP
)
Process {
If ($ComputerName) {
$Computer = $ComputerName
} Else {
$Computer = $Env:COMPUTERNAME
}
$CheckResults = @()
$Checks.GetEnumerator() | ForEach-Object {
Write-Host "Running Check, $($_.Key), on $Computer" -ForegroundColor 'Green'
$Params = @{
"ScriptBlock" = $_.Value.Check
"Verbose" = $MyInvocation.BoundParameters.Verbose
}
If ($ComputerName) {
$Params.Add('ComputerName', $Computer)
}
$Result = Invoke-Command @Params
$CheckResults += ,[PSCustomObject]@{
"Check" = $_.Key
"Result" = $Result
"Condition" = (Invoke-Command -ScriptBlock $_.Value.Condition -ArgumentList $Result)
}
}
$Output = [PSCustomObject]@{
"Server" = $Computer
"Results" = $CheckResults
}
$FileName = "ServerResults-{0}-{1}.xml" -F $Computer, (Get-Date -Format "yyyy_MM_dd_HH_mm_ss")
Export-Clixml -Path (Join-Path -Path $Path -ChildPath $FileName) -InputObject $Output
}
}
Next, we will want to generate a report telling which checks have passed or failed for any of the given servers.
To make better sense of which checks have run against which servers, we will use the New-ServerReport function.
The function performs the following steps:
Function New-ServerReport {
[CmdletBinding()]
Param(
[Parameter(Position = 0)]
[ValidateSet('Daily','Monthly')]
[String]$Type = 'Daily',
[Parameter(Position = 1)]$Path = $Env:TEMP,
[Parameter(Position = 2)]$ReportPath = $Env:TEMP
)
Process {
$Files = Get-ChildItem -Path $Path -Filter '*.xml' | Where-Object Name -Match 'ServerResults'
Switch ($Type) {
'Daily' {
$Results = $Files | Where-Object 'CreationTime' -GT (Get-Date -Hour 0 -Minute 00 -Second 00)
$ResultArray = @()
$Results | ForEach-Object {
$ResultArray += ,[PSCustomObject]@{
'Results' = (Import-Clixml -Path $_.FullName)
'DateTime' = $_.CreationTime
}
}
$Report = $ResultArray | Foreach-Object {
$DateTime = $_.DateTime
$_.Results | Group-Object -Property 'Server' | Foreach-Object {
$Server = $_.Name
$_.Group.Results | ForEach-Object {
$Object = [PSCustomObject]@{
"Server" = $Server
"Check" = $_.Check
"Result" = $_.Condition
"DateTime" = $DateTime
}
$Object
}
}
}
$FileName = "ServersReport-{0}.csv" -F (Get-Date -Format "yyyy_MM_dd_HH_mm_ss")
$Report | Export-CSV -Path (Join-Path -Path $ReportPath -ChildPath $FileName) -NoTypeInformation
$Report
Break
}
'Monthly' {
$Results = $Files | Where-Object 'CreationTime' -GT (Get-Date).AddDays(-30)
$ResultArray = @()
$Results | ForEach-Object {
$ResultArray += ,[PSCustomObject]@{
'Results' = (Import-Clixml -Path $_.FullName)
'DateTime' = $_.CreationTime
}
}
$Report = $ResultArray | Foreach-Object {
$DateTime = $_.DateTime
$_.Results | Group-Object -Property 'Server' | Foreach-Object {
$Server = $_.Name
$_.Group.Results | ForEach-Object {
$Object = [PSCustomObject]@{
"Server" = $Server
"Check" = $_.Check
"Result" = $_.Condition
"DateTime" = $DateTime
}
$Object
}
}
}
$FileName = "ServersReport-{0}.csv" -F (Get-Date -Format "yyyy_MM_dd_HH_mm_ss")
$Report | Export-CSV -Path (Join-Path -Path $ReportPath -ChildPath $FileName) -NoTypeInformation
$Report
Break
}
}
}
}
There are several ways to use this script, but the simplest is to add the servers to check and then use the New-ServerReport command to determine which checks have run over time.
# Perform Checks on Requested Servers
@("Server1","Server2","Server3") | Invoke-ServerCheck
# Generate Daily Report
New-ServerReport
By using PowerShell to create a modular framework to easily create and execute checks on servers, a system administrator can quickly gain better visibility and control of their environment. Although these checks are simple, there are many ways to extend the capabilities to include more in-depth inquiries into your server inventory.
With new cross-platform abilities of PowerShell 7, you can extend these checks to work on Linux systems to handle things such as necessary OS-specific updates.
11 Aug 2020