From 1f491ed7c60de1f3a74b67aa221fe2662ec48c8d Mon Sep 17 00:00:00 2001 From: D'Haese Willem Date: Sun, 23 Aug 2015 18:39:47 +0200 Subject: [PATCH] v1.01.150823 => First commit --- README.md | 29 +++++++ check_ms_win_updates.ps1 | 160 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 README.md create mode 100644 check_ms_win_updates.ps1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..8c055e3 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Nagios plugin to query available Windows Updates and alert based on last succesful update date + +### Idea + +Query all WSUS updates for a Windows Server and alert based on the amount of days the server had no updates. + +### Status + +Production ready. + +### How To + +Please check http://outsideit.net/check-ms-win-updates for more information on how to use this plugin. + +### Help + +In case you find a bug or have a feature request, please make an issue on GitHub. + +### On Nagios Exchange + +Not yet. + +### Copyright + +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public +License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later +version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details at . diff --git a/check_ms_win_updates.ps1 b/check_ms_win_updates.ps1 new file mode 100644 index 0000000..5c1f151 --- /dev/null +++ b/check_ms_win_updates.ps1 @@ -0,0 +1,160 @@ +# Script name: check_ms_win_updates.ps1 +# Version: v1.01.150823 +# Created on: 12/05/2015 +# Author: D'Haese Willem +# Purpose: Opens xaml gui from Powershell with information and link to Nagios monitoring system +# On Github: https://github.com/willemdh/check_ms_win_updates +# On OutsideIT: http://outsideit.net/check-ms-win-updates +# Recent History: +# 12/05/2015 => Creation date, thanks to Christian Kaufmann for the idea of caching the WSUS updates +# 05/08/2015 => Removed @() from import-clixml and counts, subtraction for other +# 06/08/2015 => Edit message and severity if lastsuccestime was not found in the registry +# 23/08/2015 => Cleanup for release +# Copyright: +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed +# in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public +# License along with this program. If not, see . + +$WsusStruct = New-object PSObject -Property @{ + CheckStart = (Get-Date -Format 'yyyy/MM/dd HH:mm:ss.fff'); + CheckEnd = ''; + CheckDuration = ''; + Exitcode = 3; + UpdateCacheFile = 'C:\Program Files\NSClient++\scripts\powershell\cache\check_ms_win_updates_cache.xml'; + UpdateCacheExpireHours = 24; + LastSuccesTime = ''; + DaysBeforeWarning = 120; + DaysBeforeCritical = 150; + NumberOfUpdatesPending = ''; + ReturnString = 'UNKNOWN: Please debug the script...' +} + +$VerbosePreference = 'SilentlyContinue' + +#region Functions + +function Test-FileLock { + param ([parameter(Mandatory=$true)][string]$Path) + $oFile = New-Object System.IO.FileInfo $Path + try + { + $oStream = $oFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None) + if ($oStream) + { + $oStream.Close() + } + return $false + } + catch + { + return $true + } +} +function Write-Log { + param ( + [parameter(Mandatory=$true)][string]$Log, + [parameter(Mandatory=$true)][string]$Severity, + [parameter(Mandatory=$true)][string]$Message + ) + $Now = Get-Date -Format 'yyyy-MM-dd HH:mm:ss,fff' + if ($Log -eq 'Verbose') { + Write-Verbose "${Now}: ${Severity}: $Message" + } + elseif ($Log -eq 'Debug') { + Write-Debug "${Now}: ${Severity}: $Message" + } + else { + if (!(Test-Path -Path $Log)){ + Write-Host "Write-Log can't find the path `"$Log`". Please debug.." + } + else { + $Now = Get-Date -Format 'yyyy-MM-dd HH:mm:ss,fff' + while (Test-FileLock $Log) {Start-Sleep (Get-Random -minimum 1 -maximum 10)} + "${Now}: ${Severity}: $Message" | Out-File -filepath $Log -Append + } + } +} + +#endregion Functions + +if (!(Test-path -Path 'C:\Program Files\NSClient++\scripts\powershell\cache')){ + New-Item -Path 'C:\Program Files\NSClient++\scripts\powershell\cache' -Type Directory -Force | Out-Null +} + +$LastSuccessTimeFolder = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\Results\Install' +if (Test-Path $LastSuccessTimeFolder) { + $LastSuccessTimeValue = Get-ItemProperty -Path $LastSuccessTimeFolder -Name LastSuccessTime | Select-Object -ExpandProperty LastSuccessTime + $WsusStruct.LastSuccesTime = Get-date "$LastSuccessTimeValue" +} +else { + Write-Host 'LastSuccesTime value not found in the registry. This server was probably never updated...' + $WsusStruct.LastSuccesTime = Get-date '2015-01-01 00:00:01' +} +$RebootRequiredKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired' +if (Test-Path $RebootRequiredKey){ + $RebootRequired = $true + if (Test-Path $WsusStruct.UpdateCacheFile) { + Remove-Item $WsusStruct.UpdateCacheFile | Out-Null + } +} + +if (!(Test-Path $WsusStruct.UpdateCacheFile) -or (Get-Date) -gt ((Get-Item $WsusStruct.UpdateCacheFile).LastWriteTime.AddHours($WsusStruct.UpdateCacheExpireHours))) { + Write-Log Verbose Info 'Cachefile not found or out of date. Creation initiated.' + $UpdateSession = new-object -ComObject 'Microsoft.Update.Session' + $Updates = $UpdateSession.CreateupdateSearcher().Search(('IsAssigned=1 and IsInstalled=0 and IsHidden=0')) + Export-Clixml -InputObject $Updates -Encoding UTF8 -Path $WsusStruct.UpdateCacheFile +} +else { + Write-Log Verbose Info 'Valid cachefile found.' + $Updates = Import-Clixml $WsusStruct.UpdateCacheFile +} + +$Total = ($updates.updates).count +$Critical = ($Updates.updates | Where-Object { $_.MsrcSeverity -eq 'Critical' }).count +$Important = ($Updates.updates | Where-Object { $_.MsrcSeverity -eq 'Important' }).count +$Other = $Total - $Critical - $Important +if (! $Total ) { + $Total = 0 +} +if (! $Critical ) { + $Critical = 0 +} +if (! $Important ) { + $Important = 0 +} +if (! $Other ) { + $Other = 0 +} +if ($Total -eq 1 -and $Other -eq 1){ + $Total = $Other = 0 +} +$WsusStruct.ReturnString ='' +$WarningLimit = ($WsusStruct.LastSuccesTime).AddDays($WsusStruct.DaysBeforeWarning) +$CriticalLimit = ($WsusStruct.LastSuccesTime).AddDays($WsusStruct.DaysBeforeCritical) +$LastSuccesTimeStr = ($WsusStruct.LastSuccesTime).ToString('yyyy/MM/dd HH:mm:ss') +if($CriticalLimit -lt (Get-Date)) { + $WsusStruct.ReturnString += "CRITICAL: Last succesful update at $LastSuccesTimeStr exceeded critical threshold of $($WsusStruct.DaysBeforeCritical) days. " + $WsusStruct.Exitcode = 2 +} +elseif($WarningLimit -lt (Get-Date)) { + $WsusStruct.ReturnString += "WARNING: Last succesful update at $LastSuccesTimeStr exceeded warning threshold of $($WsusStruct.DaysBeforeWarning) days. " + $WsusStruct.Exitcode = 1 +} +elseif ($RebootRequired) { + $WsusStruct.ReturnString += "WARNING: Reboot required. Last succesful update: $LastSuccesTimeStr. " + $WsusStruct.Exitcode = 1 +} +else { + $WsusStruct.ReturnString += "OK: Last succesful update: $LastSuccesTimeStr. " + $WsusStruct.Exitcode = 0 +} + +$WsusStruct.ReturnString += "Pending updates {Total: $Total {Critical: $Critical}{Important: $Important}{Other: $Other}}" + +Write-Host $WsusStruct.ReturnString +$WsusDuration = New-TimeSpan –Start $WsusStruct.CheckStart –End (Get-Date) +$WsusStruct.CheckDuration = '{0:HH:mm:ss.fff}' -f ([datetime]$WsusDuration.Ticks) +Write-Log Verbose Info "Check took $($WsusStruct.CheckDuration) seconds." +exit $WsusStruct.Exitcode