This was originally posted here by myself: https://kb.paessler.com/en/topic/65026-monitor-shadow-copies-age#reply-247626
This is my solution for it – we monitor specific drives we enabled for shadow copy and wanted to see amount of shadows, newest should be within x hours and oldest should be at a minimum n hours – those limits can be configured with the limitations rather easily.
Main issue is – we talk about WMI modules that are only available in x64 if you use a x64 system. Now PRTG is executing sensors in x86, even thought it is installed on x64. Now, played around a while and came up with this simple solution.
Parameters for the parser-script (the one you need to execute) are: %host C: %host D: etc.
Parser script, needs to be in EXEXML directory: Name: Get-ShadowCopyStatsXMLx64parser.cmd
1 | @"%SystemRoot%\Sysnative\WindowsPowerShell\v1.0\powershell.exe" -c "&'C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\EXEXML\Get-ShadowCopyStatsXML.ps1' -Servername %1 -Driveletter %2" |
PS1 script, should to be in EXEXML directory – if not, adjust path in parser script: Name: Get-ShadowCopyStatsXML.ps1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | Param( $ServerName, $DriveLetter ) Begin { $script:CurrentErrorActionPreference = $ErrorActionPreference $ShadowCopyStats = @() Function GetShadowCopyStats { Param($Computer) try { Import-Module @("Microsoft.PowerShell.Management","PSDiagnostics") $WMITarget = "$Computer" Get-WmiObject -Class "Win32_ComputerSystem" -Property "Name" -ComputerName $WMITarget | out-null If ($? -eq $False) { $bWMIConnection = $False $WMITarget = "$Computer." Get-WmiObject -Class "Win32_ComputerSystem" -Property "Name" -ComputerName $WMITarget | out-null If($? -eq $False){$bWMIConnection = $False}Else{$bWMIConnection = $True} } $xml = '<prtg>' $Volumes = gwmi Win32_Volume -Property SystemName,DriveLetter,DeviceID -Filter "DriveType=3" -ComputerName $WMITarget | Select SystemName,@{n="DriveLetter";e={$_.DriveLetter.ToUpper()}},DeviceID | Sort DriveLetter $ShadowCopies = gwmi Win32_ShadowCopy -Property VolumeName,InstallDate,Count -ComputerName $WMITarget | Select VolumeName,InstallDate,Count, @{n="CreationDate";e={$_.ConvertToDateTime($_.InstallDate)}} } catch [Exception] { #Write-Warning $_.Exception|format-list -force $Error[0].Exception.StackTrace $Error[0].Exception.InnerException.StackTrace $Error[0].StackTrace write-error "hello" } If($Volumes) { ForEach($Volume in $Volumes) { If($Volume.DriveLetter -eq $DriveLetter) { $VolumeShares = $VolumeShadowStorage = $DiffVolume = $VolumeShadowCopies = $Null $VolumeShadowCopies = $ShadowCopies | ?{$_.VolumeName -eq $Volume.DeviceID} | Sort InstallDate If($VolumeShadowCopies) { $xml += '<result><channel>ShadowCopyCount</channel><value>' + (($VolumeShadowCopies | Measure-Object -Property Count -Sum).Sum) + '</value></result>' $AgeOldest = New-TimeSpan -Start (($VolumeShadowCopies | Select -First 1).CreationDate) -End (Get-Date) $AgeLatest = New-TimeSpan -Start (($VolumeShadowCopies | Select -Last 1).CreationDate) -End (Get-Date) $xml += '<result><channel>OldestShadowCopy</channel><value>' + [math]::Round($AgeOldest.TotalHours,0) + '</value><unit>TimeHours</unit></result>' $xml += '<result><channel>LatestShadowCopy</channel><value>' + [math]::Round($AgeLatest.TotalHours,0) + '</value><unit>TimeHours</unit></result>' }Else{ $xml += '<result><channel>ShadowCopyCount</channel><value>0</value></result>' $xml += '<result><channel>OldestShadowCopy</channel><value>0</value><unit>TimeHours</unit></result>' $xml += '<result><channel>LatestShadowCopy</channel><value>0</value><unit>TimeHours</unit></result>' } If($VolumeShadowStorage -Or $ShowAllVolumes){$Output += $Object} } } } $xml += '</prtg>' WriteXmlToScreen $xml } Function WriteXmlToScreen ([xml]$xml) { $StringWriter = New-Object System.IO.StringWriter; $XmlWriter = New-Object System.Xml.XmlTextWriter $StringWriter; $XmlWriter.Formatting = "indented"; $xml.WriteTo($XmlWriter); $XmlWriter.Flush(); $StringWriter.Flush(); Write-Output $StringWriter.ToString(); } } Process { If($ServerName) {ForEach($Server in $ServerName){$ShadowCopyStats += GetShadowCopyStats $Server}} Else {$ShadowCopyStats += GetShadowCopyStats $_} } End { $ErrorActionPreference = $script:CurrentErrorActionPreference $ShadowCopyStats } |
PS: yes, the PS1 could be further optimized, but it took me already a while to find out the main issue was the x86/x64 combination, what is not possible – see Microsoft articles in MSDN/KB for more information while the Shadow-Copy WMI modules are only available in x64 on a x64 system.
Hope this helps others with the same challenge 🙂