How to enable volume shadow copy using Powershell?

How to enable volume shadow copy on specific drive (D:, E:,...) and set up schedule using Powershell ?

I just need some hints how to start.


You can create a Shadow copy on a specific drive using WMI object like it's explained in Microsoft Article.

Enable volume shadow copy on specific drive (D:, E:,...) is two things

  1. Creating a shadow copy as shown in the preceding article
  2. Creating a scheduled task to create shadow copy

It took me a little while to get this to worked (after finding the simple error causing it to fail), and the function is incomplete. I want to add the ability to enable it on a remote computer, which will probably have to be done via a task that is ran once. If anybody modifies this and adds that, let me know!

function Enable-ShadowCopies {
param(
    [String]$ComputerName = $Env:ComputerName,

    [Parameter(Mandatory=$true)]
    [String]$Drive
)
$volumeWMI = Get-WmiObject -ComputerName $ComputerName -Class Win32_Volume -Filter "DriveLetter = '$Drive'";
$volumeID = ($volumeWMI.DeviceID.SubString(10)).SubString(0,($volumeWMI.DeviceID.SubString(10)).Length-1);

$scheduler = New-Object -ComObject Schedule.Service
$scheduler.Connect($ComputerName)
$tskDef = $scheduler.NewTask(0);
$tskRegInfo = $tskDef.RegistrationInfo;
$tskSettings = $tskDef.Settings;
$tskTriggers = $tskDef.Triggers;
$tskActions = $tskDef.Actions;
$tskPrincipals = $tskDef.Principal;

# Registration Info
$tskRegInfo.Author = "PowerShell Script";

# Settings
$tskSettings.DisallowStartIfOnBatteries = $false;
$tskSettings.StopIfGoingOnBatteries = $false
$tskSettings.AllowHardTerminate = $false;
$tskSettings.IdleSettings.IdleDuration = "PT600S";
$tskSettings.IdleSettings.WaitTimeout = "PT3600S";
$tskSettings.IdleSettings.StopOnIdleEnd = $false;
$tskSettings.IdleSettings.RestartOnIdle = $false;
$tskSettings.Enabled = $true;
$tskSettings.Hidden = $false;
$tskSettings.RunOnlyIfIdle = $false;
$tskSettings.WakeToRun = $false;
$tskSettings.ExecutionTimeLimit = "PT259200S";
$tskSettings.Priority = "5";
$tskSettings.StartWhenAvailable = $false;
$tskSettings.RunOnlyIfNetworkAvailable = $false;

# Triggers
$tskTrigger1 = $tskTriggers.Create(3);
$tskTrigger2 = $tskTriggers.Create(3);

## Trigger 1
$tskTrigger1.Id = "Trigger1"
$tskTrigger1.StartBoundary = (Get-Date -format "yyyy-MM-dd")+"T07:00:00";
$tskTrigger1.DaysOfWeek = 0x3E; # Monday - Friday - http://msdn.microsoft.com/en-us/library/windows/desktop/aa384024(v=vs.85).aspx
$tskTrigger1.Enabled = $true;

## Trigger 2
$tskTrigger2.Id = "Trigger2";
$tskTrigger2.StartBoundary = (Get-Date -format "yyyy-MM-dd")+"T12:00:00";
$tskTrigger2.DaysOfWeek = 0x3E; # Monday - Friday - http://msdn.microsoft.com/en-us/library/windows/desktop/aa384024(v=vs.85).aspx
$tskTrigger2.Enabled = $true;

# Principals (RunAs User)
$tskPrincipals.Id = "Author";
$tskPrincipals.UserID = "SYSTEM";
$tskPrincipals.RunLevel = 1;

 # Actions
$tskActions.Context = "Author"
$tskAction1 = $tskActions.Create(0);

# Action 1
$tskAction1.Path = "C:\Windows\system32\vssadmin.exe";
$tskAction1.Arguments = "Create Shadow /AutoRetry=15 /For="+$volumeWMI.DeviceID;
$tskAction1.WorkingDirectory = "%systemroot%\system32";

# Configure VSS, Add scheduled task
vssadmin Add ShadowStorage /For=$Drive /On=$Drive /MaxSize=10%;
$tskFolder = $scheduler.GetFolder("\")
$tskFolder.RegisterTaskDefinition("ShadowCopyVolume$volumeID", $tskDef, 6, "SYSTEM", $null,5);
}

Even though this works, and it mimics what happens when you do it via the GUI, it still shows as disabled for that drive. But if you enable it, nothing changes! (LOL) I'm guessing there's something that needs to also be modified in the registry.


$diskname = "C:\"
$VolumeWmi = gwmi Win32_Volume -Namespace root/cimv2 | ?{ $_.Name -eq $diskname }
$DeviceID = $VolumeWmi.DeviceID.ToUpper().Replace("\\?\VOLUME", "").Replace("\","")
$TaskName = "ShadowCopyVolume" + $DeviceID
$TaskFor = "\\?\Volume" + $DeviceID + "\"
$Task = "C:\Windows\system32\vssadmin.exe"
$Argument = "Create Shadow /AutoRetry=15 /For=$TaskFor"
$WorkingDir = "%systemroot%\system32"

$ScheduledAction = New-ScheduledTaskAction –Execute $Task -WorkingDirectory $WorkingDir -Argument $Argument
$ScheduledTrigger = @()
$ScheduledTrigger += New-ScheduledTaskTrigger -Daily -At 10:00
$ScheduledTrigger += New-ScheduledTaskTrigger -Daily -At 15:00
$ScheduledSettings = New-ScheduledTaskSettingsSet -Compatibility V1 -DontStopOnIdleEnd -ExecutionTimeLimit (New-TimeSpan -Days 3) -Priority 5
$ScheduledTask = New-ScheduledTask -Action $ScheduledAction -Trigger $ScheduledTrigger -Settings $ScheduledSettings
Register-ScheduledTask $TaskName -InputObject $ScheduledTask -User "NT AUTHORITY\SYSTEM"

After a whole bunch of messing with it, got it to work slightly differently (it also seems to show up properly via the GUI).

Shoutouts to this page for a bit of help: https://social.technet.microsoft.com/forums/windowsserver/en-US/fb69840d-5f52-4711-8168-2faa23088233/shadow-copy-schedule-per-script

The downside to using schtasks (what that page uses) is that you can't have multiple triggers as far as I can see.

Also because of the way I troubleshot the solution (Used a bindiff of working/not working xml), I'm not entirely convinced that the flags I use are optimal.


Simpler means using schtasks that shows in the UI, compatible in PowerShell 2. Designed for a standard build, may need to play about with $volumeinfo[x] when creating $taskrun to find the appropriate volume.

$volumeinfo = GWMI -namespace root\cimv2 -class win32_volume
$volumeid = $volumeinfo[1].deviceid
$taskname = "ShadowCopyVolume" + $volumeid.replace("\","").replace("?Volume","")
$taskrun = "C:\Windows\system32\vssadmin.exe Create Shadow /AutoRetry=15 /For=$volumeid"
schtasks /create /RU SYSTEM /SC DAILY /ST 07:00 /RI 60 /DU 12:00 /K /V1 /TN $TaskName /TR "$taskrun "

Can configure the following arguments appropriately:

  • /SC - frequency task is triggered
  • /ST - time task is triggered
  • /RI - repetition of task every x minutes after trigger
  • /DU - duration of task to be repeated

NOTE: The /TR switch REQUIRES the space at the end, if it is not there it replaces the final backslash with a double quote causing the VSS UI to not recognise the task.