Skip to content

Instantly share code, notes, and snippets.

@Nora-Ballard
Created February 15, 2014 13:45
Show Gist options
  • Save Nora-Ballard/9019505 to your computer and use it in GitHub Desktop.
Save Nora-Ballard/9019505 to your computer and use it in GitHub Desktop.
Robocopy wrapper for migrating volumes in two phases; an initial online bulk copy, followed by an offline differential copy. Used for migrating large folders/volumes with minimal downtime. Requires handle.exe, psfile.exe, and robocopy.exe.
@ECHO OFF
SET SCRIPTPATH=%~dp0
setlocal ENABLEDELAYEDEXPANSION
:: ElevationCheck TODO: ADD OS Check/Disable for 2003
::whoami /groups | find "S-1-16-12288" > nul
::if "%errorlevel%"=="0" (
:: echo Running as elevated user. Continuing script.
:: echo.
::) else (
:: echo Not running as elevated user.
:: Pause
:: goto :EOF
::)
:: ---------------------------------------------------------------------
:: Description
:: ---------------------------------------------------------------------
:: -- Script to do a one time bulk copy for the purpose of migrating
:: one volume/folder to a new location. This is done in two phases:
:: Phase 1, an initial complete copy (while files remain accessible)
:: Phase 2 a final cutover after removing access and closing any open
:: files. This is usually scheduled for after hours.
::
:: -- The options include as many files and as much file information as
:: possible to exactly duplicate the Source Files.
::
:: -- For more advanced options see the robocopy.doc included in the
:: Windows 2003 resource kit with the robocopy.exe.
::
:: -- Robocopy uses the /B switch to enable Backup Operator mode and
:: backup inaccesible files.
::
:: -- Robocopy is able to handle paths over 260 characters.
::
:: ---------------------------------------------------------------------
:: Static Settings:
:: ---------------------------------------------------------------------
:: -- Specify name of resource. (Used to name log files)
:: -- No Quotes
SET NAME=
:: -- Specify Source and destination paths.
SET SOURCE=
SET DESTINATION=
:: -- Specify folder path for log files (Without trailing \)
:: -- No Quotes
SET LOGPATH=%SCRIPTPATH%\Logs
:: -- Specify full path to the robocopy executable.
:: -- Quotes optional unless path contains spaces.
SET ROBOCOPYPATH=%SCRIPTPATH%\bin\X86\robocopy.exe
:: -- Exclude directories with these names, paths, wildcard characters.
SET DIREXCLUSIONS="System Volume Information" "RECYCLER" "SIS Common Store" "DFSRoots" "Program Files"
::----------------------------------------------------------------------
::Import settings from file argument to override static settings.
if EXIST "%~dpnx1" (
Call "%~dpnx1"
) else (
echo Settings File Not Found:
echo '%~dpnx1'
GOTO:EOF
)
echo.
:Menu
SET RunAllInPhase=FALSE
if NOT EXIST "%LOGPATH%" mkdir "%LOGPATH%"
IF NOT EXIST "%LOGPATH%" (
ECHO Not Found: %LOGPATH%
GOTO:EOF
)
IF NOT EXIST "%ROBOCOPYPATH%" (
ECHO Not Found: %ROBOCOPYPATH%
GOTO:EOF
)
IF NOT EXIST "%SOURCE%" (
ECHO Not Found: %SOURCE%
GOTO:EOF
)
IF NOT EXIST "%DESTINATION%" (
ECHO Not Found: %DESTINATION%
GOTO:EOF
)
IF EXIST "%LOGPATH%\%NAME%-Step1.log" ( SET Step1=. [Ran] ) else ( SET Step1=. )
IF EXIST "%LOGPATH%\%NAME%-Step2.log" ( SET Step2=. [Ran] ) else ( SET Step2=. )
IF EXIST "%LOGPATH%\%NAME%-Step3.log" ( SET Step3=. [Ran] ) else ( SET Step3=. )
IF EXIST "%LOGPATH%\%NAME%-Step4.log" ( SET Step4=. [Ran] ) else ( SET Step4=. )
IF EXIST "%LOGPATH%\%NAME%-Step5.log" ( SET Step5=. [Ran] ) else ( SET Step5=. )
echo.
echo.
echo Job Name = %NAME%
echo Source = %SOURCE%
echo Destination = %DESTINATION%
echo Log Folder = %LOGPATH%
echo Robocopy EXE = %ROBOCOPYPATH%
echo.
echo.
echo Select Step to Run:
echo 1 - [Phase 1] - Create Empty tree structure%STEP1%
echo 2 - [Phase 1] - Initial Copy%STEP2%
echo.
echo 3 - [Phase 2] - Report Open files%STEP3%
echo 4 - [Phase 2] - Differential Copy%STEP4%
echo 5 - [Phase 2] - Fix up Security and LastWriteTime%STEP5%
echo.
echo 6 - Run all Phase 1 Steps
echo 7 - Run all Phase 2 Steps
echo.
echo 8 - Clear Log Files
echo 9 - Quit
echo.
echo.
CHOICE /C:123456789 /N /M ":"
IF ERRORLEVEL 9 GOTO:EOF
IF ERRORLEVEL 8 GOTO Clear_Logs
IF ERRORLEVEL 7 (
SET RunAllInPhase=TRUE
GOTO Step_3
)
IF ERRORLEVEL 6 (
SET RunAllInPhase=TRUE
GOTO Step_1
)
GOTO Step_%ERRORLEVEL%
cls
GOTO Menu
:Clear_Logs
cls
del "%LOGPATH%\%NAME%-Step?.log"
GOTO Menu
:Step_1
SET STEP=1
:: Creates the tree structure with empty files. Folder and file names
:: will appear, but all will have a size of 0 bytes. See /CREATE.
cls
echo.
echo Step %STEP% -- Creating empty tree structure.
"%ROBOCOPYPATH%" "%SOURCE%" "%DESTINATION%" /R:0 /E /Z /COPY:DAT /B /LOG:"%LOGPATH%\%NAME%-Step%STEP%.log" /XD %DIREXCLUSIONS% /CREATE
echo.
echo Step %STEP% -- Complete.
echo.
IF NOT %RunAllInPhase%==TRUE (GOTO Menu)
:Step_2
SET STEP=2
:: Perform the initial copy of data. Any files that cannot be
:: accessd or are in use will be skipped.
cls
echo.
echo Step %STEP% -- Initial Copy (in use files will be skipped).
"%ROBOCOPYPATH%" "%SOURCE%" "%DESTINATION%" /R:0 /E /Z /COPY:DAT /B /V /LOG:"%LOGPATH%\%NAME%-Step%STEP%.log" /XD %DIREXCLUSIONS%
echo.
echo Step %STEP% -- Complete
echo.
::End Phase 1
GOTO Menu
:Step_3
SET STEP=3
cls
echo.
echo Step %STEP% -- Report Open Files.
echo.
IF EXIST "%SCRIPTPATH%\bin\X86\handle.exe" (
"%SCRIPTPATH%\bin\X86\handle.exe" -accepteula -u %SOURCE% > %LOGPATH%\%NAME%-Step%STEP%.log
echo Log File : %LOGPATH%\%NAME%-Step%STEP%.log
echo.
)
echo. >> %LOGPATH%\%NAME%-Step%STEP%.log
echo ########################################################################## >> %LOGPATH%\%NAME%-Step%STEP%.log
echo. >> %LOGPATH%\%NAME%-Step%STEP%.log
IF EXIST "%SCRIPTPATH%\bin\X86\psfile.exe" (
"%SCRIPTPATH%\bin\X86\psfile.exe" -accepteula "%SOURCE::\=:\\%" >> %LOGPATH%\%NAME%-Step%STEP%.log
echo Log File : %LOGPATH%\%NAME%-Step%STEP%.log
echo.
)
start "" notepad.exe %LOGPATH%\%NAME%-Step%STEP%.log
IF NOT %RunAllInPhase%==TRUE (GOTO Menu)
::IF RunAllInPhase==TRUE ( SET /a NEXTSTEP=STEP+1; GOTO Step_%NEXTSTEP%)
::GOTO Menu
:Step_4
SET STEP=4
cls
echo.
echo *** Ensure files are not in use before continuing or they will be skipped. ***
echo *** ***
echo *** ***
echo *** NOTE: Skipped files in this step are best remediated manually. ***
echo *** Re-running Step 4 can take a long time if there are many ***
echo *** files to check. ***
echo.
choice /C:YN /M Continue?
IF ERRORLEVEL 2 GOTO Menu
echo.
echo Step %STEP% -- Mirroring any changes from initial copy to destination.
"%ROBOCOPYPATH%" "%SOURCE%" "%DESTINATION%" /R:0 /E /Z /COPY:DAT /B /V /LOG+:"%LOGPATH%\%NAME%-Step%STEP%.log" /XD %DIREXCLUSIONS% /MIR
echo.
echo Step %STEP% -- Complete.
echo.
IF NOT %RunAllInPhase%==TRUE (GOTO Menu)
GOTO Menu
:Step_5
SET STEP=5
cls
echo.
echo Step %STEP% -- Running a fix up of file security on all files.
"%ROBOCOPYPATH%" "%SOURCE%" "%DESTINATION%" /E /Copy:ST /IS /IT /B /V /LOG+:"%LOGPATH%\%NAME%-Step%STEP%.log" /XD %DIREXCLUSIONS%
echo.
echo Step %STEP% -- Complete.
echo.
:: End Phase 2
GOTO Menu
:END
:: Extended Info:
:: /r:0 -- Specifies the number of retries on failed copies. Sets to 0, failed copies will be logged and can be dealt with individually. Retry just causes delays.
:: /E -- Copies all subdirectories (including empty ones).
:: /Z -- Copies files in restartable mode (that is, restarts the copy process from the point of failure).
:: /B -- (Optional) Copies files with Backup Operator privilege, which ignores ACLs.
:: /COPYALL -- Copies Everything: Data, Attribues, Timestamps, Security (NTFS ACLS), Ownership, Auditing. See the /COPY: command to specify individual items to copy.
:: /V -- Produces verbose output (including skipped files).
:: /LOG: -- Redirects output to the specified file, overwriting the file if it already exists.
:: /LOG+: -- Redirects output to the specified file, appending it to the file if it already exists.
:: /XD -- Excludes directories with the specified names, paths, or wildcard characters.
:: /CREATE -- Creates a directory tree structure containing zero-length files only (that is, no file data is copied). This will help to reduce fragmentation and increase performance.
:: /MIR -- Mirrors a directory tree.
:: -- Phase 1, the initial copy, runs steps 1 and 2 in sequence. Step 3 should be REM'ed out and steps 1 and 2 should be active.
::
:: -- Phase 2, the differential copy, runs step 3 separately after phase 1 is complete and all access is cut to files. Steps 1 and 2 should be REM'ed
:: out and step 3 made active. Make sure there are no open network locks, and that any services which would have open handles to the files are stopped.
:: -- Step 3 checks tree for changes (additions and deletions to source) and mirrors them on the destination. See /MIR.
:: -- Phase 3, the remediation, must be done manually. Check the MIR.LOG file for any failed files. The common cause is the file being open by a
:: user or app. If needed step 3 can be re-run after resolution to copy the files that failed, or they can be handled manually.
:: Note: On a volume with many files it can take a long time just to scan for changes, even if there is no data to be copied. Because of this it may be
:: quicker to manually resolve skipped files than to re-run step 3.
:: Note: The Sysinternals ProcessXP can be used to check for which process has the handle, then either the process can be closed, or killed, or the handle can be closed.
:: Note: Closing the handle should be used with caution as it is possible to corrupt the data.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment