diskshadow.exe is a VSS requester. It drives the Volume Shadow Copy Service directly, so from one command prompt you can freeze a live volume, mount that frozen copy as a drive letter, read the locked files out of it, and then throw it away. Microsoft’s Volume Shadow Copy Service overview describes it as a requester for managing hardware and software snapshots on a system.
This is the tool vssadmin points at. vssadmin will list and delete shadow copies happily, but creating one and exposing it as a drive letter is not something it does everywhere, and diskshadow is the documented answer in both cases. If you have ever needed a consistent copy of a file that something else holds open, this is the command that gets it.
The reference is a table of twenty subcommands, each documented on its own page, and three of the links in that table land on diskpart and cmd documentation instead of diskshadow’s own. So this article walks a single volume end to end: create, expose, read, parse, delete. Then it covers the part that costs the most time, which is that the percent sign means four different things inside the same workflow.
Quick answer
This sequence creates a shadow copy of drive C: that is still there after you leave the interpreter. Type diskshadow at an elevated prompt, then enter the rest at the diskshadow> prompt.
set context persistent nowriters
add volume c: alias SysVol
create
list shadows all
exit
The follow-up you will want straight after is exposing it, because a shadow copy you cannot browse is not much use. Back in diskshadow, with the copy already created:
expose %VSS_SHADOW_1% p:
exit
set context persistent and the shadow copy is gone the moment you type exit. Microsoft’s VSS API reference states that the default context is VSS_CTX_BACKUP, and describes that context as auto release and nonpersistent. Nothing in diskshadow prints the context it is using, so that one line is the difference between a copy you can use and a copy that never survives the session.
What diskshadow does
VSS has three sides: writers (applications that flush their state before a snapshot), providers (the components that actually make the snapshot), and requesters (whatever asks for one). Backup products are requesters. diskshadow is a requester you can drive by hand, which is why it can do things a backup product’s UI will not let you ask for.
It runs in two modes. Typed on its own it opens an interactive interpreter, in the same family as diskpart and diskraid, with its own diskshadow> prompt. Given a script file it runs the same commands unattended.
| Subcommand | What it is for |
|---|---|
set context | Decides whether the copy survives exit, and whether writers are involved |
set option | differential or plex, transportable, and the autorecover behaviour |
set metadata | Names the .cab file that a transportable copy is imported from later |
set verbose | Turns on writer inclusion and exclusion detail during creation |
add volume | Adds a volume to the shadow copy set. At least one is required |
add alias | Names a shadow copy so scripts do not carry raw GUIDs |
create | Makes the shadow copy using the current context and options |
list | list writers, list shadows, list providers |
expose | Surfaces a persistent copy as a drive letter, share or mount point |
unexpose | Removes that drive letter, share or mount point again |
exec | Runs a .cmd file on the local machine, mid session |
begin backup / end backup | Marks a full backup session rather than the default copy backup |
writer verify / writer exclude | Requires or drops a named writer or component |
load metadata / import / mask | Moves a transportable copy onto this or another machine |
begin restore / end restore | Wraps a restore, raising the prerestore and postrestore events for writers |
delete shadows | Deletes by id, by set, by volume, by oldest, by exposed path, or all |
revert | Rolls a volume back to a shadow copy |
break | Disassociates a shadow copy volume from VSS |
reset | Returns diskshadow to its default state |
exit | Leaves the interpreter, or ends the script |
Only add and create are strictly required to produce a shadow copy. The reference is explicit that doing only that forfeits the context and option settings, gives you a copy backup, and leaves you with no backup execution script. That is the whole reason set context comes first in every example below.
set context takes one of clientaccessible, persistent or volatile, and either of the last two can be followed by nowriters. clientaccessible is persistent by default. nowriters excludes every writer, which makes creation faster and far less likely to fail, at the cost of application consistency.
Before the first example
Four things need to be true before the examples work. Every example after this uses the same volume (C:), the same alias (SysVol), the same folder (C:\diskshadow\) and the same exposed letter (P:), so set these up once.
1. An elevated prompt. Membership in the local Administrators group is the documented minimum. Do not test this by looking for the group name in your token, because it is present on a non-elevated prompt too. Check the integrity level instead:
rem S-1-16-12288 is the High Mandatory Level SID. It appears only when the
rem prompt is actually elevated, unlike the Administrators group name.
whoami /groups | findstr /c:"S-1-16-12288"
2. A working folder. Scripts, saved output and the metadata cab all land here:
md C:\diskshadow
3. A registered provider. Without one there is nothing to make the snapshot. Start diskshadow and ask:
list providers
Microsoft’s reference shows a single registered provider, the in-box software one. That is the provider used whenever you do not name one explicitly, and its output looks like this:
* ProviderID: {b5946137-7b9f-4925-af80-51abd60b20d5}
Type: [1] VSS_PROV_SYSTEM
Name: Microsoft Software Shadow Copy provider 1.0
Version: 1.0.0.7
CLSID: {65ee1dba-8ff4-4a58-ac1c-3470ee2f376a}
1 provider registered.
4. Healthy writers, if you intend to use them. Skip this only if every example you run will say nowriters. list writers status is the short form that prints identity and state and nothing else:
list writers status
Status: 1 (VSS_WS_STABLE) with a failure code of 0x00000000 (S_OK) is a writer at rest. A different state is not automatically a problem: the System Writer in Microsoft’s own sample sits at Status: 5 (VSS_WS_WAITING_FOR_BACKUP_COMPLETE), which only means a backup is already in flight. What matters is a non-zero failure code, and diskshadow spells the state out in full rather than collapsing it to one word:
Listing writer status ...
* WRITER System Writer
- Status: 5 (VSS_WS_WAITING_FOR_BACKUP_COMPLETE)
- Writer Failure code: 0x00000000 (S_OK)
- Writer ID: {e8132975-6f93-4464-a53e-1050253ae220}
- Instance ID: {7e631031-c695-4229-9da1-a7de057e64cb}
* WRITER Shadow Copy Optimization Writer
- Status: 1 (VSS_WS_STABLE)
- Writer Failure code: 0x00000000 (S_OK)
- Writer ID: {4dc3bdd4-ab48-4d07-adb0-3bee2926fd7f}
- Instance ID: {9e362607-9794-4dd4-a7cd-b3d5de0aad20}
* WRITER Registry Writer
- Status: 1 (VSS_WS_STABLE)
- Writer Failure code: 0x00000000 (S_OK)
- Writer ID: {afbab4a2-367d-4d15-a586-71dbb18f8485}
- Instance ID: {e87ba7e3-f8d8-42d8-b2ee-c76ae26b98e8}
8 writers listed.
list writers on its own does not print that short list. The reference states that without parameters it gives you the metadata output, which includes component detail and excluded files and is very long. status is the one you want for a quick health check.
Practical examples
1. Create a shadow copy that survives exit
The problem: you created a shadow copy, typed exit, came back with list shadows all, and it is not there. Nothing reported an error.
The solution: you got the default context, which the VSS API reference names as VSS_CTX_BACKUP and describes as auto release and nonpersistent. diskshadow’s own word for that behaviour is volatile, defined as deleting the shadow copy on exit or reset. set context persistent is the line that changes it, and it has to come before create.
Start the interpreter from the elevated prompt, then enter these at diskshadow>. Expect set verbose on to make create noisy, which is what you want the first time.
# persistent keeps the copy after exit; nowriters skips every writer, which is
# faster and cannot fail on a sick writer. Drop nowriters when you need
# application consistency rather than a crash-consistent volume image.
set context persistent nowriters
# verbose prints writer inclusion and exclusion while the copy is made
set verbose on
# the alias is the name you will use later instead of a raw GUID
add volume c: alias SysVol
create
Now leave and come back. The copy should still be listed:
exit
diskshadow
list shadows all
Each shadow copy in that output carries an id and a default alias. The reference prints the pair like this:
* Shadow Copy ID = {ff47165a-1946-4a0c-b7f4-80f46a309278}
%VSS_SHADOW_1%
diskshadow, which means it is genuinely persistent. If your copy is missing from the listing, set context ran after create or did not run at all.
2. Expose it as a drive letter and read the locked files
The problem: the file you need is open and the process holding it is not going to let go. Copying it fails or produces a torn file.
The solution: expose the shadow copy as a drive letter and read it from there. Nothing on P: is live, so nothing on P: is locked.
%VSS_SHADOW_1% is the default alias from the listing above. Expect P: to appear immediately, holding the volume exactly as it was at the instant create ran.
# expose takes a shadow id, an alias, or an environment variable, and a target.
# The target can be a drive letter, a share, or a mount point such as C:\snap\.
expose %VSS_SHADOW_1% p:
Leave the interpreter and read the frozen copy with ordinary tools. This directory exists on every Windows install, so it is a safe thing to test against:
exit
dir P:\Windows\System32\drivers\etc
When you are finished, take the letter back. unexpose accepts the drive letter directly, so you do not need the id again:
unexpose P:
expose %SysVol% p: before create. The reference is explicit that aliases are not added to the alias environment until the shadow copy is created, so the alias you typed into add volume does not exist yet. If you need a name earlier in the script, define it yourself with add alias.
3. Run the whole thing from a script file
The problem: the sequence works when you type it, but it needs to run from a scheduled task at 02:00 with nobody watching.
The solution: put the same commands in a .dsh file and hand it to diskshadow. The interpreter reads them in order and exits at the end.
Save this as C:\diskshadow\snap.dsh. It is adapted from the sequence in Microsoft’s reference, using one volume instead of two. # starts a comment in a .dsh file.
# C:\diskshadow\snap.dsh
# context first: everything after this inherits it
set context persistent nowriters
# the cab only matters if you later import this copy somewhere else,
# but writing it costs nothing and makes the copy reusable
set metadata C:\diskshadow\snap.cab
set verbose on
begin backup
add volume c: alias SysVol
create
# SysVol exists only now, because create is what publishes the alias
expose %SysVol% p:
# exec hands control to a .cmd file while P: is mounted and the session is open
exec C:\diskshadow\copy-out.cmd
end backup
Run it from the elevated prompt. Expect the verbose output of every step to scroll past, ending with the backup session closing:
diskshadow /s C:\diskshadow\snap.dsh
diskshadow -s script.txt and its Examples section writes diskshadow /s script.dsh. The complete worked example uses the slash form, which is also the form every other Windows command in this family uses, so that is the one used here.
4. Pull the shadow copy id back out of the output
The problem: a later step needs the id, and the only place it exists is a wall of text from list shadows all.
The solution: redirect the listing to a file and tokenize the one line that carries the id.
First, a tiny .dsh that does nothing but list. Save it as C:\diskshadow\list.dsh:
# C:\diskshadow\list.dsh
list shadows all
exit
Run it and keep the output. Expect a file of a few dozen lines, one of which is the Shadow Copy ID line:
diskshadow /s C:\diskshadow\list.dsh > C:\diskshadow\shadows.txt
Now tokenize it. The id line reads * Shadow Copy ID = {guid}, and the count that matters is six, not five: the leading asterisk is a token and so is the equals sign.
rem This line is typed at the prompt, so the loop variable is a single %G.
rem tokens=6 because * Shadow Copy ID = {guid} is six space separated tokens.
for /f "tokens=6" %G in ('findstr /c:"Shadow Copy ID" C:\diskshadow\shadows.txt') do @echo %G
{ff47165a-1946-4a0c-b7f4-80f46a309278}
If you want the bare GUID without the braces, tokenize on the braces themselves. This version is also immune to how deeply the line happens to be indented:
rem Typed at the prompt, so a single %G again.
rem delims={} makes the braces the separators, so token 2 is the GUID itself.
for /f "tokens=2 delims={}" %G in ('findstr /c:"Shadow Copy ID" C:\diskshadow\shadows.txt') do @echo %G
ff47165a-1946-4a0c-b7f4-80f46a309278
tokens=5. Counting the words by eye gives Shadow, Copy, ID, =, {guid} and lands on five, because the leading asterisk is easy to miss. tokens=5 does not fail, it does not warn, it prints =. A script that feeds that into delete shadows id then fails somewhere else entirely.
5. Delete it, and check that it is gone
The problem: persistent means persistent. A copy you made for a one-off file recovery is still consuming shadow storage a month later.
The solution: delete it by id, the narrowest of the forms on offer. Unexpose first, then delete, then confirm.
# take the drive letter back before removing what is behind it
unexpose P:
# by id, so exactly one copy is affected and it is the one you just read
delete shadows id {ff47165a-1946-4a0c-b7f4-80f46a309278}
# confirm: the copy you deleted should no longer be in this listing
list shadows all
exit
delete shadows also takes volume c: for every copy of one volume, oldest c: for just the oldest of them, set for a whole shadow copy set, exposed P: for whatever is behind a drive letter, and all.
delete shadows all to tidy up. It deletes every shadow copy on the machine, which includes the ones your backup product made and is relying on, and the ones behind Previous Versions. Delete by id, or by oldest if you are trimming on a schedule.
One percent sign, two, or a diskshadow alias
A diskshadow workflow spans several places at once: the .dsh script, the .cmd file that exec runs, the prompt you started it from, and often a .ps1 wrapper around the lot. The percent sign appears in all four and means something different in each. This is the most common reason a sequence that worked by hand fails when it is scheduled.
| Where you are typing it | What the percent sign means | The same line in full |
|---|---|---|
A .dsh script, or the diskshadow> prompt | A diskshadow alias, expanded by diskshadow itself | expose %SysVol% p: |
| A CMD prompt | A for loop variable, one percent sign | for /f "tokens=6" %G in ('findstr /c:"Shadow Copy ID" shadows.txt') do @echo %G |
| A .bat or .cmd file | A for loop variable, two percent signs | for /f "tokens=6" %%G in ('findstr /c:"Shadow Copy ID" shadows.txt') do @echo %%G |
| A PowerShell script | Nothing. It is ordinary text | $line = '%VSS_SHADOW_1%' |
Rows two and three are the same line. The percent sign is the only thing that changes, which is exactly why the difference survives a copy and paste out of a blog post or a forum answer and then breaks. Paste the script form straight into a prompt and CMD stops on the first character it cannot parse:
C:\diskshadow>for /f "tokens=6" %%G in ('findstr /c:"Shadow Copy ID" shadows.txt') do @echo %%G
%%G was unexpected at this time.
The other direction is the dangerous one, because it is quiet. Microsoft’s for reference says of a single percent sign left in a batch file that “the variable is ignored and an error message is displayed”. The batch file does not stop. In a long overnight script, a loop that quietly skipped itself is noticed days later, when somebody asks where the copied files went.
The exec line from example 3 is where these rules collide inside one workflow. snap.dsh uses %SysVol%, and the file it hands control to is a batch file, so that file needs %%G. Save this as C:\diskshadow\copy-out.cmd:
@echo off
rem Save this as a .cmd file: in a script the loop variable is %%G, at the
rem prompt the same line uses %G. Nothing else on the line changes.
rem P: is the shadow copy snap.dsh exposed, so every file read here is frozen
rem at the moment create ran, even if the live file is open and being written.
if not exist C:\diskshadow\out md C:\diskshadow\out
for /f "tokens=*" %%G in ('dir /b P:\Windows\System32\drivers\etc') do (
copy "P:\Windows\System32\drivers\etc\%%G" "C:\diskshadow\out\" >nul
echo copied %%G
)
PowerShell is the fourth rule and the easiest to forget, because nothing complains. To PowerShell the alias is a plain string, and no amount of expansion turns it into anything else:
# Only diskshadow expands %VSS_SHADOW_1%. PowerShell has no idea what it is,
# so a .dsh line pasted into a .ps1 file silently becomes literal text.
$line = '%VSS_SHADOW_1%'
$line.GetType().Name
$ExecutionContext.InvokeCommand.ExpandString($line)
String
%VSS_SHADOW_1%
%VSS_SHADOW_1% to whatever comes next as though it were a path.
Hidden gems
The alias does not exist until create runs. add volume c: alias SysVol reads like a variable assignment and is not one. The reference states that aliases are not added to the alias environment until the shadow copy is created. If a script needs a name before that point, add alias SysVol {guid} defines one immediately.
The alias parameter is not in the syntax block. Microsoft documents add volume as add volume <volume> [provider <providerid>], with no mention of alias in the syntax line or the parameter table. Then both its own example and the complete script on the diskshadow page use add volume c: alias System1. The parameter works and is the normal way to write it, but you will not find it by reading the syntax.
set verbose on is the cheapest diagnostic you have. Turn it on before create and the interpreter prints which writers were included and which were excluded, plus metadata compression detail. When creation fails on a machine with a sick writer, that output names the writer without a separate list writers pass.
exec runs while the copy is mounted and the session is still open. That is the combination that makes diskshadow worth scripting: create, expose, run your copy job, end backup, all inside one VSS session, so the writers are told the backup completed rather than being left waiting. A copy job run after diskshadow has exited gets none of that.
reset is not just tidying up. It is the documented way to separate compound operations in one session, and it discards state from add, set, load and writer. It also destroys non-persistent shadow copies, which is a second way to lose a copy you thought you had.
create and list go to diskpart‘s pages, which document create partition and list disk, and exit goes to cmd.exe’s exit /b. The diskshadow pages exist under different file names, and the Official documentation section below links to the correct ones.
Reading the result from PowerShell
There is no diskshadow cmdlet, and no PowerShell module that creates a shadow copy the way this tool does. What PowerShell does give you is the read side. The Win32_ShadowCopy WMI class exposes the same shadow copies, and it exposes them as properties rather than as text you have to tokenize.
This is worth knowing because the class carries the context flags. Persistent, ClientAccessible and NoWriters are properties on it, which means you can check after the fact which set context a copy was actually created with instead of trusting that the script ran the line you think it ran.
# ID is the same GUID diskshadow prints, and ExposedName is the drive letter or
# mount point if the copy is exposed. The three booleans are the context readback.
Get-CimInstance -ClassName Win32_ShadowCopy |
Select-Object ID, VolumeName, Persistent, ClientAccessible, NoWriters, ExposedName
If you do still have to work from saved list shadows all output, one regex takes the id out of the line. This is the PowerShell equivalent of the for /f in example 4, and it does not care about indentation or token counts:
# A GUID in braces is 38 characters: 36 of hex and hyphens, plus the braces.
$line = '* Shadow Copy ID = {ff47165a-1946-4a0c-b7f4-80f46a309278}'
if ($line -match '\{[0-9a-fA-F-]{36}\}') { $Matches[0] }
{ff47165a-1946-4a0c-b7f4-80f46a309278}
There is one trap in that pattern worth measuring rather than assuming. -match is an operator, not a command, so a failed match does not disturb $?. A script that runs the match and then tests $? to decide whether it found an id will always decide that it did:
# $? describes the previous statement, and an operator that returned False
# is still a statement that succeeded. Test the operator's own result instead.
$line = '* Shadow Copy ID = {ff47165a-1946-4a0c-b7f4-80f46a309278}'
$found = $line -match 'NO SUCH TEXT'
$q = $?
"found=$found `$?=$q"
found=False $?=True
`$? stops PowerShell expanding the variable inside the double quoted string, so the label prints literally. Drop the backtick and the same line prints found=False True=True, because the label itself gets substituted.
Where this matters
Copying a file that something else holds open. A log, a database file or a mailbox store that an agent keeps locked copies cleanly off an exposed shadow copy, because nothing on P: is running.
Collecting the registry hives a live system will not release. The hives under \Windows\System32\config are readable on the shadow copy with an ordinary copy, which is what makes offline analysis possible without taking the server down.
Creating a shadow copy where vssadmin cannot. vssadmin is the tool most people reach for first, and its create shadow verb is not available everywhere. diskshadow is the documented route in that case, and it is also what exposes the copy as a drive letter afterwards.
Giving a backup script a consistent source. Running the copy job from inside the diskshadow session, through exec, means every file in the job comes from the same instant rather than from whenever the job happened to reach it.
Testing a restore without touching production. Expose yesterday’s copy as a drive letter, restore from it into a scratch folder, and prove the backup is readable before you need it to be.
Tips and limitations
- Membership in the local Administrators group, or equivalent, is the documented minimum to run diskshadow at all.
- Microsoft publishes the reference under Windows Server Commands. Confirm the executable is present before building a client workstation process around it.
revertis supported only for shadow copies in theclientaccessiblecontext, which only the system provider can create. A copy you made withpersistent nowriterscannot be reverted to.noautorecovercannot be combined withtxfrecoverorrollbackrecoverin the sameset optionline.- If a file run by
execfails, diskshadow returns an error and quits. A failing copy script therefore takes the rest of the .dsh sequence with it, includingend backup. - Each
add volumeis checked against VSS support for that volume as it is added, and the reference notes that a laterset contextcan invalidate that check. Keepset contextaboveadd volume. exposeworks on persistent shadow copies. A volatile copy has nothing to expose once the session that made it is gone.- Microsoft’s VSS documentation states that persistent shadow copies can be made only for NTFS volumes. Nonpersistent copies can be made for NTFS and non-NTFS volumes alike, which is a second reason
set context persistentcan fail where the default succeeded. - The reference gives both
-sand/sfor script mode, on the same page. The worked example uses/s.
Official documentation
- Diskshadow | Microsoft Learn
- list (diskshadow) | Microsoft Learn
- set context | Microsoft Learn
- add volume | Microsoft Learn
- expose | Microsoft Learn
- delete shadows | Microsoft Learn
- for | Microsoft Learn
- Volume Shadow Copy Service | Microsoft Learn
- VSS_SNAPSHOT_CONTEXT enumeration | Microsoft Learn
- Shadow Copy Context Configurations | Microsoft Learn
Related tools
- ROBOCOPY Command Builder builds the copy line that the .cmd file run by
execneeds, including the retry and wait switches that matter when the source is an exposed shadow copy.
Related guides
- vssadmin command in Windows is the other half of this pair: it lists, resizes and deletes shadow copies, and it is the article that sends you here when it cannot create one.
- wbadmin command in Windows covers the scheduled backup side, where VSS is doing the same work underneath but you never see the shadow copy.
- ROBOCOPY backup on Windows is what you point at the exposed drive letter once the copy is mounted.
- findstr and find in Windows covers the search side of the parsing in example 4, including the literal matching that
/c:gives you. - whoami /all and the Windows access token explains why the integrity level SID, not the Administrators group name, is the reliable elevation check used in the prerequisites above.