PowerShell: Practical Usage of Robocopy

Version 0.1 (Version 0.2 is here)

<#
.Description
1. This script asks the user whether a copying type is Copy-All, Mirror, or Sync Permissions.
2. It will then processes a series of "Robocopy" commands with the desired copying type against a list of Sources and Destinations
3. It outputs the elapsed duration for each copying task as well as the total time for the whole batch.
#>

$sourcesAndDestinations=@(
"\\FILESHERVER01\FOLDER1 \\FILESHERVER01\FOLDER2",
"\\FILESHERVER01\FOLDER2 \\FILESHERVER01\FOLDER3"
)

$dateStamp = Get-Date -Format "yyyy-MM-dd-hh-mm"
$currentPath=(Get-Item -Path ".\").FullName
$log="/LOG+:$currentPath\robocopy-log-$dateStamp.txt"

$switches="";
$switchesCopyAll="/copy:DATSOU /e /zb /r:3 /w:3 /MT:40 /bytes /tee /v /secfix /FFT "
$switchesMirrorDirectories="/MIR /SEC /B /R:1 /W:2 /XO /FFT "
$switchesSyncPermissions="/secfix /copy:SOU /r:3 /w:3 /log:logfile.log /V /NP /FFT "

<# Explanations:
/copy:DATSOU : equivalent to /COPYALL
- /copy:flag[s]
- Flags : D=Data, A=Attributes, T=Timestamps S=Security=NTFS ACLs, O=Owner info, U=aUditing info). /SEC : Copy files with SECurity (equivalent to /COPY:DATS)
/e : copy subdirectories, including empty ones
/z : restartable mode
/b : backup mode
/zb : combination of z and b switches
/r:<count> : retries count
/w:<count> : wait time between retries
/MT:<count> : Multithreaded copying mode with N threads; works well with /LOG option for better performance
/bytes : show sizes as bytes
/tee : write status to the console window and/or log file
/v : verbose progress output
/secfix : fixes file security on all files - meaning to go back to skipped files as well. This is to preempt robocopy version XP010 that will not copy file ACL changes unless the file itself has also been changed
/log <LogFile> : writes status output to a log file with overwriting behavior (not append)
/MIR is the equivalent of /E and /PURGE AND overwriting destination directory security settings. /MIR is slightly slower than /E and /PURGE
/SEC Copies NTFS security information. (Source and destination volumes must both be NTFS). Equivalent to /COPY:DATS
/FFT : use FAT file system timing instead of NTFS. Less time precision with the benefit of lighter processing, meaning more resilient & faster copying operations
/XO : Exclude older files
#>

Function runasAdmin{
# Ensure that script is ran in the context of an Administrator
$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)

# Get the security principal for the Administrator role
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator

# Check to see if we are currently running "as Administrator"
if ($myWindowsPrincipal.IsInRole($adminRole))
{
# We are running "as Administrator" - so change the title and background color to indicate this
$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)"
$Host.UI.RawUI.BackgroundColor = "Black"
clear-host
}
else
{
# We are not running "as Administrator" - so relaunch as administrator

# Create a new process object that starts PowerShell
$newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell";

# Specify the current script path and name as a parameter
$newProcess.Arguments = $myInvocation.MyCommand.Definition;

# Indicate that the process should be elevated
$newProcess.Verb = "runas";

# Start the new process
[System.Diagnostics.Process]::Start($newProcess);

# Exit from the current, unelevated, process
exit
}
}

function selectCopyType{
$selection = Read-Host -Prompt "Please select a robocopy type to perform:`n 1. CopyAll`n 2. Mirror`n 3. Sync Permissions"
switch ($selection){
1 {$switches=$switchesCopyAll;}
2 {$switches=$switchesMirrorDirectories;}
3 {$switches=$switchesSyncPermissions;}
default {"No selection has been made. Program now exits...";break;}
}
"Selection number $selection has been made. The command will follow this format:";
"robocopy <Source> <Destination> $switches $log";
}

function startRobocopy{
$totalTime=0;
foreach ($item in $sourcesAndDestinations){
$stopWatch= [System.Diagnostics.Stopwatch]::StartNew()
try{
invoke-expression "robocopy $item $switches $log";
}
catch{
continue;
}
$elapsedSeconds=$stopWatch.Elapsed.TotalSeconds;
Add-Content $log "Elapsed: $elapsedSeconds seconds.";
$totalTime+=$elapsedSeconds;
}
Add-Content $log "Total Time elapsed: $totalTime";
}

runasAdmin;
selectCopyType;

# Start Robocopy only if a copy type has been made
if ($switches){
startRobocopy;
read-host "Process is completed. Press ENTER to close";
}

Older Version:

$dateStamp = Get-Date -Format "yyyy-MM-dd-hh-mm"
$source="\\FILESERVER01\DIRECTORY01"
$destination="C:\CLUSTER01\VOL01\SHARE01\DIRECTORY01"
$log="/LOG:C:\robocopy-log-$dateStamp.txt"
#$switches="/copy:DATSOU /e /zb /r:3 /w:3 /MT:40 /bytes /tee /v /secfix "
$switches="/MIR "

<# Explanations:
/copy:DATSOU
- /copy:flag[s]
- Flags : D=Data, A=Attributes, T=Timestamps S=Security=NTFS ACLs, O=Owner info, U=aUditing info). /SEC : Copy files with SECurity (equivalent to /COPY:DATS)
/e : copy subdirectories, including empty ones
/z : restartable mode
/b : backup mode
/zb : combination of z and b switches
/r:<count> : retries count
/w:<count> : wait time between retries
/MT:<count> : Multithreaded copying mode with N threads; works well with /LOG option for better performance
/bytes : show sizes as bytes
/tee : write status to the console window and/or log file
/v : verbose progress output
/secfix : fixes file security on all files - meaning to go back to skipped files as well. This is to preempt robocopy version XP010 that will not copy file ACL changes unless the file itself has also been changed
/log <LogFile> : writes status output to a log file with overwriting behavior (not append)
/MIR : mirror complete directory trees from source to destination, including ACLs and hidden files. Repeated runs of this command may remove items at destination should those have been deleted at the source directory.
#>

# Ensure that script is ran in the context of an Administrator
$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)

# Get the security principal for the Administrator role
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator

# Check to see if we are currently running "as Administrator"
if ($myWindowsPrincipal.IsInRole($adminRole))
{
# We are running "as Administrator" - so change the title and background color to indicate this
$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)"
$Host.UI.RawUI.BackgroundColor = "Black"
clear-host
}
else
{
# We are not running "as Administrator" - so relaunch as administrator

# Create a new process object that starts PowerShell
$newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell";

# Specify the current script path and name as a parameter
$newProcess.Arguments = $myInvocation.MyCommand.Definition;

# Indicate that the process should be elevated
$newProcess.Verb = "runas";

# Start the new process
[System.Diagnostics.Process]::Start($newProcess);

# Exit from the current, unelevated, process
exit
}

invoke-expression "robocopy $source $destination $switches $log"

read-host "Press ENTER to close"

Here is a different version of the above script

$sourcesAndDestinations=@(
"\\FILESERVER01\test\test1 \\FILESERVER01\test\test2",
"\\FILESERVER01\test\test2 \\FILESERVER01\test\test3"
)

$dateStamp = Get-Date -Format "yyyy-MM-dd-hh-mm"
$currentPath=(Get-Item -Path ".\").FullName
$log="/LOG:$currentPath\robocopy-log-$dateStamp.txt"
$switchesCopyAll="/copy:DATSOU /e /zb /r:3 /w:3 /MT:40 /bytes /tee /v /secfix "
$switchesMirrorDirectories="/MIR /SEC /ZB /W:3 /FFT "
$switchesSyncPermissions="/secfix /copy:SOU /r:3 /w:3 /log:logfile.log /V /NP"


<# Explanations:
/copy:DATSOU : equivalent to /COPYALL
- /copy:flag[s]
- Flags : D=Data, A=Attributes, T=Timestamps S=Security=NTFS ACLs, O=Owner info, U=aUditing info). /SEC : Copy files with SECurity (equivalent to /COPY:DATS)
/e : copy subdirectories, including empty ones
/z : restartable mode
/b : backup mode
/zb : combination of z and b switches
/r:<count> : retries count
/w:<count> : wait time between retries
/MT:<count> : Multithreaded copying mode with N threads; works well with /LOG option for better performance
/bytes : show sizes as bytes
/tee : write status to the console window and/or log file
/v : verbose progress output
/secfix : fixes file security on all files - meaning to go back to skipped files as well. This is to preempt robocopy version XP010 that will not copy file ACL changes unless the file itself has also been changed
/log <LogFile> : writes status output to a log file with overwriting behavior (not append)
/MIR is the equivalent of /E and /PURGE AND overwriting destination directory security settings. /MIR is slightly slower than /E and /PURGE
/SEC Copies NTFS security information. (Source and destination volumes must both be NTFS). Equivalent to /COPY:DATS
/FFT : use FAT file system timing instead of NTFS. Less time precision with the benefit of less processing, meaning more resilient & faster copying operations
#>

Function runasAdmin{
# Ensure that script is ran in the context of an Administrator
$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)

# Get the security principal for the Administrator role
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator

# Check to see if we are currently running "as Administrator"
if ($myWindowsPrincipal.IsInRole($adminRole))
{
# We are running "as Administrator" - so change the title and background color to indicate this
$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)"
$Host.UI.RawUI.BackgroundColor = "Black"
clear-host
}
else
{
# We are not running "as Administrator" - so relaunch as administrator

# Create a new process object that starts PowerShell
$newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell";

# Specify the current script path and name as a parameter
$newProcess.Arguments = $myInvocation.MyCommand.Definition;

# Indicate that the process should be elevated
$newProcess.Verb = "runas";

# Start the new process
[System.Diagnostics.Process]::Start($newProcess);

# Exit from the current, unelevated, process
exit
}
}

#runasAdmin;

foreach ($item in $sourcesAndDestinations){
try{
"command to run: 'robocopy $item $switchesMirrorDirectories $log'";
invoke-expression "robocopy $item $switchesMirrorDirectories $log";
}
catch{
continue;
}
}

read-host "Process is completed. Press ENTER to close"

Leave a Reply

Your email address will not be published. Required fields are marked *