What is this?

This knowledgebase contains questions and answers about PRTG Network Monitor and network monitoring in general.

Learn more

PRTG Network Monitor

Intuitive to Use. Easy to manage.
More than 500,000 users rely on Paessler PRTG every day. Find out how you can reduce cost, increase QoS and ease planning, as well.

Free Download

Top Tags


View all Tags

Monitor Shadow Copies age

Votes:

0

Hello, guys!

Is it possible to monitor the age of volume shadow copies in a windows server?

age shadow-copy windows

Created on Jun 22, 2015 1:37:23 PM



Best Answer

Accepted Answer

Votes:

1

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

@"%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

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 :-)

Regards, Florian Rossmark

Created on Mar 8, 2017 8:04:14 PM



12 Replies

Votes:

0

The following script should get what you want - Get-ShadowCopyStats.ps1

Note that you have to use it in conjunction with the EXE/Script (Advanced) sensor and modify it to output the data in proper PRTG XML Format,
see http://your-prtg-server/api.htm?tabid=7 for the details :)

Created on Jun 22, 2015 2:00:32 PM by  Stephan Linke [Paessler Support]



Accepted Answer

Votes:

1

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

@"%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

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 :-)

Regards, Florian Rossmark

Created on Mar 8, 2017 8:04:14 PM



Votes:

0

Hi Florian,

Thanks for your submission! I added to to our script world ;)

Created on Mar 9, 2017 9:08:47 AM by  Stephan Linke [Paessler Support]



Votes:

0

Hi,

First of all thank you for this script, unfortunatly the sensor does not work :

This sensor requires the PowerShell 2.0 (or higher) to be installed on the probe system. (GetShadowCopyStats : hello At C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\EXEXML\Get-ShadowCopyStatsXML.ps1:89 char:55 + {ForEach($Server in $ServerName){$ShadowCopyStats += GetShadowCopyStats $Server ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorExcep tion + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorExceptio n,GetShadowCopyStats WriteXmlToScreen : Cannot process argument transformation on parameter 'xml'. Cannot convert value "</prtg>" to type "System.Xml.XmlDocument". Error: "Unexpected end tag. Line 1, position 3." At C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\EXEXML\ Get-ShadowCopyStatsXML.ps1:71 char:30 + WriteXmlToScreen $xml + ~~~~ + CategoryInfo : InvalidData: (:) [WriteXmlToScreen], ParameterBi ndingArgumentTransformationException + FullyQualifiedErrorId : ParameterArgumentTransformationError,WriteXmlToS creen at System.Management.ManagementScope.Initialize() at System.Management .ManagementObjectSearcher.Initialize() at System.Management.ManagementObjectSearcher.Get() at Microsoft.PowerShell.Commands.GetWmiObjectCommand.BeginProcessing() at System.Management.Automation.Cmdlet.DoBeginProcessing() at System.Management.Automation.CommandProcessorBase.DoBegin() ) (code: PE181)

If I launch the script manualy on the PRTG server I have to following :

<prtg> <result> <channel>ShadowCopyCount</channel> <value>12</value> </result> <result> <channel>OldestShadowCopy</channel> <value>177</value> <unit>TimeHours</unit> </result> <result> <channel>LatestShadowCopy</channel> <value>4</value> <unit>TimeHours</unit> </result> </prtg>

How can I solve this? PowerShell is of course installed on the probe server and on the remote one..

Created on Nov 28, 2017 3:28:11 PM



Votes:

0

You need to install the latest version of the Windows Management Framework, as it contains the latest PowerShell version. Yours is seemingly rather old and due for an update anyway :) It should work then.


Kind regards,
Stephan Linke, Tech Support Team

Created on Nov 28, 2017 4:29:28 PM by  Stephan Linke [Paessler Support]



Votes:

0

Hi all. Trying to implement this. The sensor returns 0 data when it runs. The following appears in the sensor logs. Running the parser command works on the server console.

Appreciate any help.

Thanks, Perry

+ ~~~~~~~~~~ + CategoryInfo : InvalidData: (:) [Add-Type], InvalidOperationExc eption + FullyQualifiedErrorId : COMPILER_ERRORS,Microsoft.PowerShell.Commands.Ad dTypeCommand

Import-Module : The module to process 'PSDiagnostics', listed in field 'ModuleToProcess/RootModule' of module manifest 'C:\Windows\system32\WindowsPow erShell\v1.0\Modules\PSDiagnostics\PSDiagnostics.psd1' was not processed because no valid module was found in any module directory. At C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\EXEXML\Get-ShadowCopyStatsXML.ps1:14 char:9 + Import-Module @("Microsoft.PowerShell.Management","PSDiagnost ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : ResourceUnavailable: (PSDiagnostics:String) [Imp ort-Module], PSInvalidOperationException + FullyQualifiedErrorId : Modules_ModuleFileNotFound,Microsoft.PowerShell. Commands.ImportModuleCommand

gwmi : Initialization failure At C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\EXEXML\Get-ShadowCopyStatsXML.ps1:28 char:33 + ... dowCopies = gwmi Win32_ShadowCopy -Property VolumeName,InstallDate,Co ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [Get-WmiObject], Managemen tException + FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.C ommands.GetWmiObjectCommand

<prtg> <result> <channel>ShadowCopyCount</channel> <value>0</value> </result> <result> <channel>OldestShadowCopy</channel> <value>0</value> <unit>TimeHours</unit> </result> <result> <channel>LatestShadowCopy</channel> <value>0</value> <unit>TimeHours</unit> </result> </prtg>

Created on Mar 22, 2018 2:56:55 AM



Votes:

0

Hi Perry,

What PowerShell Version do you have installed? Issue $PSVersionTable to find out and let me know.


Kind regards,
Stephan Linke, Tech Support

Created on Mar 22, 2018 11:51:09 AM by  Stephan Linke [Paessler Support]



Votes:

0

When I run it from the Powershell in the PRTG server works correctly and returns the XML to me but when I do it from the advanced script sensor it does not return any value and it does timeout. What I want to monitor is a connected USB device, a biometric device, that does not appear in the main panel of My PC.

Created on Aug 23, 2018 7:51:56 PM



Votes:

0

Hector - a biometric USB device? Sorry for my confusion - but what does this have to do with ShadowCopies?

Please activate the logging for the sensor and if necessary extend the timeout for the sensor - those information will help to determine why the sensor isn't working but your manual execution of the script.

More detailed information will help as well.

Created on Aug 24, 2018 1:43:12 PM



Votes:

2

I was having some trouble getting this script set up as a PRTG sensor, it was returning 0s because of this error:

gwmi : Initialization failure 
At C:\Program Files (x86)\PRTG Network Monitor\Custom 
Sensors\EXEXML\Get-ShadowCopyStatsXML.ps1:28 char:33
+                 $ShadowCopies = gwmi Win32_ShadowCopy -Property 
VolumeName,Insta ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~
    + CategoryInfo          : InvalidOperation: (:) [Get-WmiObject], Managemen 
   tException
    + FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.C 
   ommands.GetWmiObjectCommand

I tried several different things to try to resolve this error, but I ended up compiling it to an exe using this: https://gallery.technet.microsoft.com/PS2EXE-GUI-Convert-e7cb69d5

The exe worked great with PRTG - returns exactly the numbers I expect. Not sure why the exe works for me, but the powershell script doesn't.

Created on Mar 8, 2019 8:06:37 PM

Last change on Mar 11, 2019 7:32:53 AM by  Stephan Linke [Paessler Support]



Votes:

1

I'm having issues with the script in PRTG. The script manually runs without issues.

PS C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\EXEXML> .\Get-ShadowCopyStatsXML.ps1 cfs f: <prtg> <result> <channel>ShadowCopyCount</channel> <value>64</value> </result> <result> <channel>OldestShadowCopy</channel> <value>767</value> <unit>TimeHours</unit> </result> <result> <channel>LatestShadowCopy</channel> <value>3</value> <unit>TimeHours</unit> </result> </prtg>

But I get the following error in PRTG.

XML: XML Parser mismatch: Wanted </>, got </prtg> -- JSON: The returned JSON does not match the expected structure (Invalid JSON.). (code: PE231)

PRTG Version: PRTG Network Monitor 19.2.50.2842 x64 Auto-Update Status[8/19/2019 12:51:41 PM] The downloaded PRTG version (19.2.50.2842) is not newer than your current version (19.2.50.2842). Operating System: Microsoft Windows Server 2016 Standard (10.0 Build 14393), 8 CPUs (8x x64 Model 79 Step 1), code page "Windows-1252", on "LSI UCSB-MRAID12G SCSI DISK DEVICE"

Created on Aug 28, 2019 4:26:53 PM



Votes:

0

@Everyone having trouble with errors in PRTG: XML: XML Parser mismatch: Wanted </>, got </prtg> -- JSON: The returned JSON does not match the expected structure (Invalid JSON.). (code: PE231)

mbright solution works.

Created a .exe file from the .ps1 with PS2EXE-GUI. Put the EXE in D:\PRTG Network Monitor\Custom Sensors\EXEXML

Created in PRTG a custom sensor "Exe/script advanced" In settings, be sure to select "Write result to disks" and "Use Windows credentials of parent device" as the script has to be run with allowed user, which is not always the user running PRTG probe.

EXE runs pretty quickly on multiple hosts without trouble.

Thanks Florian Rossmark and mbright for this great custom check and help :)

Created on Oct 3, 2019 12:42:28 PM




Disclaimer: The information in the Paessler Knowledge Base comes without warranty of any kind. Use at your own risk. Before applying any instructions please exercise proper system administrator housekeeping. You must make sure that a proper backup of all your data is available.