Microsoft Windows has a great feature called Shadow Copies (or Restore Points in desktop OS versions) that performs local point-in-time snapshots of disks.
Brian Scheewe
Friday, August 11, 2023
Microsoft Windows has a great feature called Shadow Copies (or Restore Points in desktop OS versions) that performs local point-in-time snapshots of disks. These snapshots are "differential, block-level backups" which means that only changes are tracked, so they're fast and space-efficient. This feature is built on top of Volume Shadow Copy Service (VSS) which allows the OS to take disk-level snapshots without locking files or needing to worry about changes during a backup window.
A best practice for most Windows Servers (especially file servers) is to enable Shadow Copies in order to protect against unintentional deletion or modification of files. Restoring from a Shadow Copy can take just a few seconds and can even be initiated by an end-user (because it's built right into Explorer).
There are few gut-wrenching moments worse than realizing (after it's too late) that a backup has failed, and sometimes it's great to carry a secondary parachute. Shadow Copies can often act as this secondary parachute!
In order to monitor Shadow Copies in an automated way, the command "vssadmin" will give us what we need.
copy
PS C:\> vssadmin -?
vssadmin 1.1 - Volume Shadow Copy Service administrative command-line tool
(C) Copyright 2001-2013 Microsoft Corp.
---- Commands Supported ----
Delete Shadows - Delete volume shadow copies
List Providers - List registered volume shadow copy providers
List Shadows - List existing volume shadow copies
List ShadowStorage - List volume shadow copy storage associations
List Volumes - List volumes eligible for shadow copies
List Writers - List subscribed volume shadow copy writers
Resize ShadowStorage - Resize a volume shadow copy storage association
Unfortunately vssadmin is not a native PowerShell command, which means the output is not formatted in a manner that is easy to work with! The rest of this post will cover how we created a PowerShell script that Level can use to monitor Shadow Copies.
Regex parsing
First we look at some sample output of "vssadmin list shadowstorage". Yuck! We will need the drive letters and the timestamps in order to accurately report on Shadow Copy health.
copy
PS C:\> vssadmin list shadows
vssadmin 1.1 - Volume Shadow Copy Service administrative command-line tool
(C) Copyright 2001-2013 Microsoft Corp.
Contents of shadow copy set ID: {84808a27-bd1d-404e-93f2-6b1720418615}
Contained 1 shadow copies at creation time: 8/6/2023 5:15:41 PM
Shadow Copy ID: {0454e1f0-44f5-4712-9ab5-fe905472f033}
Original Volume: (C:)\\?\Volume{582d645a-4743-4ba5-af42-badc47ebbc83}\
Shadow Copy Volume: \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy17
Originating Machine: MyComputer
Service Machine: MyComputer
Provider: 'Microsoft Software Shadow Copy provider 1.0'
Type: ApplicationRollback
Attributes: Persistent, No auto release, Differential, Auto recovered
Contents of shadow copy set ID: {8e8938f7-b44c-4564-8c5f-e4004d4b168b}
Contained 1 shadow copies at creation time: 8/7/2023 11:46:31 AM
Shadow Copy ID: {18c681eb-edbc-4782-8b60-fdff1655caf2}
Original Volume: (C:)\\?\Volume{582d645a-4743-4ba5-af42-badc47ebbc83}\
Shadow Copy Volume: \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy20
Originating Machine: MyComputer
Service Machine: MyComputer
Provider: 'Microsoft Software Shadow Copy provider 1.0'
Type: ApplicationRollback
Attributes: Persistent, No auto release, Differential, Auto recovered
Contents of shadow copy set ID: {ba45a428-4f84-459e-b382-d4a7dd16c235}
Contained 2 shadow copies at creation time: 8/7/2023 11:39:26 PM
Shadow Copy ID: {dd1eb6c4-f662-40fb-b3c2-726d5a02999d}
Original Volume: (C:)\\?\Volume{582d645a-4743-4ba5-af42-badc47ebbc83}\
Shadow Copy Volume: \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy24
Originating Machine: MyComputer
Service Machine: MyComputer
Provider: 'Microsoft Software Shadow Copy provider 1.0'
Type: ClientAccessibleWriters
Attributes: Persistent, Client-accessible, No auto release, Differential, Auto recovered
Shadow Copy ID: {18aef9bd-9b35-49f4-b709-9f66a8a174da}
Original Volume: (E:)\\?\Volume{bbec2460-cabf-48fa-9841-e6e26a440bc1}\
Shadow Copy Volume: \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy25
Originating Machine: MyComputer
Service Machine: MyComputer
Provider: 'Microsoft Software Shadow Copy provider 1.0'
Type: ClientAccessibleWriters
Attributes: Persistent, Client-accessible, No auto release, Differential, Auto recovered
Since none of this data is in a PowerShell object format, manually matching patterns in the text will be required. The following snippet demonstrates how we can match the text for the drive letters and timestamps in order to push them into a PowerShell custom object. In our case we only care about the latest snapshot, so we'll only store the newest point-in-time per drive. In the example output above, we'll have "C" and "E" drives with Shadow Copies, and the newest copies were both taken in a set on August 7th at 11:39:26 PM.
copy
# Run vssadmin to list all the shadow copies present
$vssShadows = vssadmin list shadows
# Create a custom object list to store the latest shadow copy creation time for each drive
$latestShadowCopies = @()
$currentTime = $null
$currentDriveLetters = @()
# Iterate through the vssadmin output to extract drive letters and creation times
foreach ($line in $vssShadows) {
# Regex to parse the vssadmin output for snapshot creation times
if ($line -match 'Contained \d+ shadow copies at creation time: (.*)') {
$currentTime = Get-Date $matches[1]
}
# Regex to parse the vssadmin output for drive letters
elseif ($line -match 'Original Volume: \((\w):') {
$currentDriveLetters = $currentDriveLetters + $matches[1]
}
elseif ($line -eq '') {
if ($currentTime -ne $null) {
# Update the custom object list with the latest creation time for each drive letter
foreach ($driveLetter in $currentDriveLetters) {
$existingCopyIndex = -1
for ($i = 0; $i -lt $latestShadowCopies.Count; $i++) {
if ($latestShadowCopies[$i].DriveLetter -eq $driveLetter) {
$existingCopyIndex = $i
break
}
}
if ($existingCopyIndex -ne -1) {
if ($currentTime -gt $latestShadowCopies[$existingCopyIndex].CreationTime) {
$latestShadowCopies[$existingCopyIndex].CreationTime = $currentTime
}
}
else {
$latestShadowCopy = [PSCustomObject]@{
DriveLetter = $driveLetter
CreationTime = $currentTime
}
$latestShadowCopies += $latestShadowCopy
}
}
}
$currentDriveLetters = @()
}
}
This will create 2 entries in the custom object called "$latestShadowCopies" with the drive letters and the timestamps. The next step is to query all NTFS hard drives on the system so that we can enumerate all disks that should have Shadow Copy enabled.
copy
# List all the known volumes and filter on volumes with drive letters using NTFS. Server 2012 uses FileSystem, newer uses FileSystemType
$knownDriveLetters = Get-Volume | Where-Object { $_.DriveLetter -ne $null -and ($_.FileSystemType -EQ 'NTFS' -or $_.FileSystem -EQ 'NTFS') } | Select-Object -ExpandProperty DriveLetter
Lastly, we put it all together by using our list of NTFS volumes and compare it against the list of Shadow Copy snapshots. This section is also where we validate that the snapshot is under a defined time threshold (for example a snapshot was taken within the past 24 hours.
copy
# Specify the maximum allowed hours since the last shadow copy before alerting
$hoursSinceLastShadowCopy = 24
# Using the list of all known NTFS volumes, compare if there are shadow copies within the specified time period.
foreach ($knownDriveLetter in $knownDriveLetters) {
$matchingDrive = $latestShadowCopies | Where-Object { $_.DriveLetter -eq $knownDriveLetter }
if ($matchingDrive) {
$shadowCreationTime = $matchingDrive.CreationTime
$timeDifference = (Get-Date) - $shadowCreationTime
$maxAllowedTimeDifference = [TimeSpan]::FromHours($hoursSinceLastShadowCopy)
if ($timeDifference -lt $maxAllowedTimeDifference) {
Write-Host "Drive $knownDriveLetter has a recent Shadow Copy from $shadowCreationTime."
}
else {
Write-Host "ALERT: Drive $knownDriveLetter has a Shadow Copy, but it's not within $hoursSinceLastShadowCopy hours."
exit 1
}
}
else {
Write-Host "ALERT: No Shadow Copies found for drive $knownDriveLetter!"
exit 1
}
}
There are three outcomes from this check.
There is a current snapshot for a given NTFS volume. Good!
There is a snapshot for the NTFS volume, but it's outside the time limit set. Bad!
There is not a snapshot for the NTFS volume. Bad!
The latter two will generate a console message beginning with "ALERT" and that's what we will use in Level to trigger the alert.
Once the script has been added to Level, now it's time to add it to a monitor policy. We will give it a meaningful name (because that shows up in the alert), chose the script in Level, and then chose a triggering output of "Contains" and "ALERT".
Once the monitor is saved, it will start to query all the target machines. If there's a problem, you'll see an alert in Level, and if you enabled email notifications, you'll see that too. Below is an example alert, and when we open the payload of the alert, we can see that the C drive does not have Shadow Copy enabled! We better fix that!
Hopefully this will get your mental gears churning as you consider all the possibilities! What else would you like to monitor? Let us know, we'd love to hear from you!
Level: Simplify IT Management
At Level, we understand the modern challenges faced by IT professionals. That's why we've crafted a robust, browser-based Remote Monitoring and Management (RMM) platform that's as flexible as it is secure. Whether your team operates on Windows, Mac, or Linux, Level equips you with the tools to manage, monitor, and control your company's devices seamlessly from anywhere. Ready to revolutionize how your IT team works? Experience the power of managing a thousand devices as effortlessly as one. Start with Level today—sign up for a free trial or book a demo to see Level in action.