
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!
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 associationRegex parsing
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# 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.
# 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 DriveLetterLastly, 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.
# 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.
The entire script can be found in our Github community repo.

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!
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.
Even though you’ve named your iteration variables $Line and $Line2, they do not contain lines, but System.IO.FileInfo and System.IO.DirectoryInfo instances, as emitted by Get-ChildItem.
In short: In Windows PowerShell,
Get-ChildItemoutput situationally stringifies to the file/directory name only, whereas in PowerShell 7+ it always stringifies to the full paths.The
Get-ChildItemcalls in your question happen to produce name-only stringification in Windows PowerShell; see this answer for background information.
Therefore, to make your code work in PowerShell 7+ and exhibit the same behavior you saw in Windows PowerShell, you must explicitly refer to the .Name property of the objects output by Get-ChildItem:
$Results = foreach ($dir in $User) { foreach ($file in $Logs) { if ($file.Name -like "*$($dir.Name)*") { $file } }
}Before you use any of the Systems Manager command documents, ensure that you’ve met all
Prerequisites.
Topics
Parameters for Systems Manager VSS snapshot
documents
- ExcludeBootVolume (string, optional)
This setting excludes boot volumes from the backup process if you
create snapshots. To exclude boot volumes from your snapshots, set
ExcludeBootVolume toTrue,
and CreateAmi toFalse.If you create an AMI for your backup, this parameter should be set
toFalse. The default value for this parameter is
False.- NoWriters (string, optional)
To exclude application VSS writers from the snapshot process, set this parameter
toTrue. Excluding application VSS writers can help you resolve
conflicts with third-party VSS backup components. The default value for this
parameter isFalse.- CopyOnly (string, optional)
If you use the native SQL Server backup in addition to AWS VSS, performing a
Copy-only backup prevents AWS VSS from breaking the native differential
backup chain. To perform a Copy-only backup operation, set this parameter to
True.The default value for this parameter is
False, which causes AWS VSS
to perform a full backup operation.- CreateAmi (string, optional)
To create a VSS-enabled Amazon Machine Image (AMI) to back up your instance,
set this parameter toTrue. The default value for this parameter is
False, which backs up your instance with an EBS snapshot instead.- AmiName (string, optional)
If the CreateAmi option is set to
True, specify the name of the AMI that the backup creates.- description (string, optional)
Specify a description for the snapshots or image that this process creates.
- tags (string, optional)
We recommend that you tag your snapshots and images to help you locate and
manage your resources, for example, to restore volumes from a list of snapshots.
The system adds theNamekey, with a blank value where you can specify
the name that you want to apply to your output snapshots or images.Device – For VSS-enabled snapshots,
this is the device name of the EBS volume that the snapshot captures.AppConsistent – This tag indicates
the successful creation of a VSS-enabled snapshot or AMI.AwsVssConfig – This identifies
snapshots and AMIs that are created with VSS enabled. The tag includes meta
information such as theAwsVssComponentsversion.
Warning
Specifying any of these reserved tags in your parameter list will
cause an error.- executionTimeout (string, optional)
Specify the maximum time in seconds to run the snapshot creation process on the
instance, or to create an AMI from the instance. Increasing this timeout allows the
command to wait longer for VSS to start its freeze and complete tagging of the resources
it creates. This timeout only applies to the snapshot or AMI creation steps. The initial
step to install or update theAwsVssComponentspackage is not
included in the timeout.- CollectDiagnosticLogs (string, optional)
- VssVersion (string, optional)
For the
AWSEC2-VssInstallAndSnapshotdocument only, you can specify
theVssVersionparameter to install a specific version of
AwsVssComponentspackage on your instance. Leave this parameter
blank to install the recommended default version.If the specified version of the
AwsVssComponentspackage
is already installed, the script skips the install step and moves on to the
backup step. For a list ofAwsVssComponentspackage versions
and operating support, see AWS VSS solution version history.
Run Systems Manager VSS snapshot
command documents
The script first installs or updates the
AwsVssComponents
package on your instance, depending on whether it’s already installed.The script creates the application-consistent snapshots after the first
step completes.
Before you begin
This process uses a PowerShell script
(CreateVssSnapshotAdvancedScript.ps1) to take
snapshots of all volumes on the instances you specify, except root
volumes. If you need to take snapshots of root volumes, then you must
use theAWSEC2-CreateVssSnapshotSSM
document.The script calls the
AWSEC2-ManageVssIOdocument
twice. The first time with theActionparameter set to
Freeze, which pauses all I/O on the instances. The
second time, theActionparameter is set to
Thaw, which forces I/O to resume.Don’t attempt to use the
AWSEC2-ManageVssIO
document without using the CreateVssSnapshotAdvancedScript.ps1 script.
Microsoft’s VSS framework requires that theFreezeand
Thawactions be called no more than ten seconds apart,
and manually calling these actions without the script could result in
errors.
To create VSS-enabled EBS snapshots by using the AWSEC2-ManageVssIO SSM
document
Download the CreateVssSnapshotAdvancedScript.zip file and extract the
file contents.Open
CreateVssSnapshotAdvancedScript.ps1in a text
editor, edit the sample call at the bottom of the script with a valid
EC2 instance ID, snapshot description, and desired tag values, and then
run the script from PowerShell.
If successful, the command populates the list of EBS snapshots with the new
snapshots. You can locate these snapshots in the list of EBS snapshots by
searching for the tags you specified, or by searching for
AppConsistent. If the command execution failed, view the
command output for details about why the execution failed. If the command was
successfully completed, but a specific volume backup failed, you can
troubleshoot the failure in the list of EBS volumes.




