SCCM Task Sequence – Robocopy and WIM Image Backups from WinPE

This post will show you how to perform automated robocopy and WIM Image backups of all available drives (even Bitlocked!) during a task sequence in WinPE.

Requirements

The Script

Make a new SCCM package named “Scripts – Backups” and save this script as “Run-RobocopyBackup.wsf” in the package source directory.

Notes:

  • You will need to search for and change “sStateRoot” to your backup server path.
  • The script will create a folder within the sStateRoot path with the computer’s name.
  • The script will skip the Program Files, Program Files x86, Windows, and Default User directories.
  • The script will fail the TS Action if it doesn’t find at least 1 volume containing the folder “C:\Users” or “C:\Documents and Settings”.
'Run-RobocopyBackup.wsf

<script type="text/javascript" language="VBScript" src="../../wdpackage/scripts/ZTIUtility.vbs">// <![CDATA[

<script language="VBScript">

' //***************************************************************************
' // ***** Script Header *****
' //
' // Solution: Solution Accelerator for Microsoft Deployment
' // File: Z-Sample.wsf
' //
' // Purpose: Template
' //
' // Usage: cscript Z-Sample.wsf [/debug:true]
' //
' // Customer Build Version: 1.0.0
' // Customer Script Version: 1.0.0
' // Customer History:
' //
' // ***** End Header *****
' //***************************************************************************

'//----------------------------------------------------------------------------
'//
'// Global constant and variable declarations
'//
'//----------------------------------------------------------------------------

'Option Explicit

Dim iRetVal

'//----------------------------------------------------------------------------
'// End declarations
'//----------------------------------------------------------------------------

'//----------------------------------------------------------------------------
'// Main routine
'//----------------------------------------------------------------------------

On Error Resume Next
iRetVal = ZTIProcess
ProcessResults iRetVal
On Error Goto 0

'//---------------------------------------------------------------------------
'//
'// Function: ZTIProcess()
'//
'// Input: None
'//
'// Return: Success - 0
'// Failure - non-zero
'//
'// Purpose: Perform main ZTI processing
'//
'//---------------------------------------------------------------------------
Function Throw_Warning(msg)
	msg = "Run-Robocopy (WARNING): " & msg
	oLogging.CreateEntry msg, LogTypeError
	msgbox msg
End Function

Function Write_Log(msg)
	msg = "Run-Robocopy: " & msg
	oLogging.CreateEntry msg, LogTypeError
End Function

Function Test_UsersDir(aSourcePaths)

	If Right(sPath,1) = "\" Then
		sPath = Left(sPath,(Len(sPath) - 1))
	End If
	retval = False
	For Each sPath in aSourcePaths
		sFolder = sPath & "\Users\"
		sFolder2 = sPath & "\Documents and Settings\"
		'wscript.echo "searching: " & sFolder
		If oFSO.FolderExists(sFolder) = True Then
			retval = True
			Exit For
		ElseIf oFSO.FolderExists(sFolder2) = True Then
			retval = True
			Exit For
		End If
	Next

	Test_UsersDir = retval
End Function

Function Get_ComputerName
	sComputerName = oUtility.ComputerName
	Get_ComputerName = sComputerName
End Function

Function Test_FolderAccessOK(sTargetRoot)
	On Error Resume Next
	sTargetRoot = Trim_Slash(sTargetRoot) + "\"
	sFile = "scanstatetest_1111.txt"

	'create a scanstatetest_1111.txt file at target
	Set objFile = oFSO.CreateTextFile(sTargetRoot & sFile)
	objFile.close

	'test file existance
	bCreated = oFSO.FileExists(sTargetRoot & sFile)

	'delete file
	oFSO.DeleteFile(sTargetRoot & sFile)

	'test file existance
	bExistsAfterDelete = oFSO.FileExists(sTargetRoot & sFile)

	If bCreated = True And bExistsAfterDelete = False Then
		retval = True
	Else
		retval = False
	End If

	Test_FolderAccessOK = retval
End Function

Function Get_TargetRoot(sTargetRoot)
	sTargetRoot = Trim_Slash(sTargetRoot)

	'try path sTargetRoot & \ & "usmt-v4_capture" & i
	'return first path not found
	bFoundPath = False
	bExists = Null
	For i = 0 to 100
		sFolder = sTargetRoot & "\" & "robocopy_" & i & "\"
		bExists = oFSO.FolderExists(sFolder)
		If bExists = False Then
			bFoundPath = True
			Exit For
		End If
	Next

	If bFoundPath = False Then
		retval = False
	Else
		retval = sFolder
	End If

	Get_TargetRoot = retval
End Function

Function Make_FolderPath(strPath)
	'ref: http://www.windowsitpro.com/article/tips/jsi-tip-10441-how-can-vbscript-create-multiple-folders-in-a-path-like-the-mkdir-command-
	Dim strParentPath, objFSO
  Set objFSO = CreateObject("Scripting.FileSystemObject")
	On Error Resume Next
	strParentPath = objFSO.GetParentFolderName(strPath)

  If Not objFSO.FolderExists(strParentPath) Then MakeDir strParentPath
	If Not objFSO.FolderExists(strPath) Then objFSO.CreateFolder strPath
	On Error Goto 0
  MakeDir = objFSO.FolderExists(strPath)
End Function

Function Get_SourcePath
	sFolder = "C:\Users"
	sFolder2 = "C:\Documents and Settings"

	If oFSO.FolderExists(sFolder) = True Then
		retval = sFolder
	Else
		retval = sFolder2
	End If

	If oFSO.FolderExists(retval) = False Then
		retval = False
	End If

	Get_SourcePath = retval
End Function

Function Run_RobocopyBackup(aSourcePaths,sBackupsPath)
	bContinueRun = True

	sTargetRoot = Get_TargetRoot(sBackupsPath)
	bVarTest = False
	bVarTest = Is_Null(sTargetRoot)
	If bVarTest = True Or sTargetRoot = False Then
		bContinueRun = False
		msg = "Failed to generate a target directory."
		Throw_Warning msg
	End If

	If bContinueRun = True Then
		Dim aLines
		Redim aLines(0)
		i = 0
		For Each sSourcePath in aSourcePaths
			bFailThisPath = False
			If sSourcePath = "" Then
				bFailThisPath = True
			End If

			If bFailThisPath = False Then
				sTargetPath = Build_TargetPath(sTargetRoot,sSourcePath)
				If Right(sTargetPath,1) <> "\\" Then
					sTargetPath = sTargetPath & "\"
				End If

				'make folder path @ target
				bPathsMade = Make_FolderPath(sTargetPath)

				''write our batch file
				''write our batch file'ref: http://forums.datamation.com/intranet-journal/130-writing-text-file-using-vbscript.html
				If Ubound(aLines) < i Then
					Redim Preserve aLines(i)
				End If

				'leave this here -- something is betting passed By_Ref in Make_FolderPath and screwing it up. I should fix this...I've been assuming everything was by_val :/
				'If the slash doesn't get added, it won't copy the entire contents of the source if the source is a drive letter and the working path on that drive isn't the root!!
				If Right(sSourcePath,1) = "\" Then
					sSourcePath = sSourcePath
				Else
					sSourcePath = sSourcePath & "\"
				End If
				sExpression = "robocopy.exe " & sSourcePath & " " & sTargetPath & " /MT /XJ /DCOPY:T /R:1 /W:0 /B /E /XF pagefile.sys /XD ""C:\System Volume Information"" ""C:\Windows"" ""C:\Users\Default User"" ""c:\Program Files (x86)"" ""C:\Program Files"""
				aLines(i) = sExpression
				i = i + 1
			End If
		Next

		For Each sLine in aLines
			msg = "Running the following command: " & sLine
			Write_log msg
			sExitCode = oShell.Run(sLine,1,True)
			If sExitCode = "16" Then
				msg = "Robocopy exited with error code 16 - fatal error - no files were copied. Quitting script."
				Throw_Warning msg
				Wscript.Quit(100)
			End If
		Next

		retval = true
	End If

	Run_ScanState = retval
End Function

Function Build_TargetPath(sTargetRoot,sSourcePath)
	If Right(sTargetRoot,1) = "\" Then
		sTargetRoot = Left(sTargetRoot,(Len(sTargetRoot) - 1))
	End If
	If Right(sSourcePath,1) = "\" Then
		sSourcePath = Left(sSourcePath,(Len(sSourcePath) - 1))
	End If

	sSourceTemp = sSourcePath
	sSourceTemp = Replace(sSourceTemp,":","")
	sSourceTemp = Replace(sSourceTemp,"\","_")

	sTargetPath = sTargetRoot & "\" & sSourceTemp

	Build_TargetPath = sTargetPath
End Function

Function Is_Null(oVariable)
	If oVariable = "" Or oVariable = Null Then
		retval = True
	Else
		retval = False
	End If
	Is_Null = retval
End Function

Function Trim_Slash(sString)
	If Right(sString,1) = "\" Then
		sString = Left(sString,(Len(sString) - 1))
	End If
	trim_slash = sString
End Function

''''''''''''merged from enumerate-mountpoints.vbs
Function Get_UniqueFixedMountPoints
	strComputer = "."
	Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\CIMV2")
	Set colItems = objWMIService.ExecQuery _
	    ("SELECT * FROM Win32_MountPoint")

	Dim MPVolumes(1000)

	Dim aMPs
	Redim aMPs(0)
	i = 0
	For Each objItem In colItems
		blnSkipMP = False
		sCurrentVolume = objItem.Volume
		For Each sStoredVolume in MPVolumes
			If sStoredVolume = sCurrentVolume Then
				blnSkipMP = True
			End If
		Next

		If blnSkipMP = False Then
			MPVolumes(i) = objItem.Volume
			If Ubound(aMPs) < i Then
				Redim Preserve aMPs(i)
			End If
			sDirName = objItem.Directory
			aDirName = Split(sDirName,"=")
			sDirectory = Replace(aDirName(1),"\\","\")
			sDirectory = Left(sDirectory,(len(sDirectory) - 1))
			sDirectory = Right(sDirectory,(len(sDirectory) - 1))
			If Right(sDirectory,1) = "\" Then
				sDirectory = Left(sDirectory,(len(sDirectory) - 1))
			End If
			'wscript.echo sDirectory
			aMPs(i) = sDirectory
			i = i + 1
		End If
	Next

	Get_UniqueFixedMountPoints = aMPs
End Function

Function Get_LogicalDisks
	strComputer = "."
	Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\CIMV2")
	Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\CIMV2")
	Set colItems = objWMIService.ExecQuery _
	    ("SELECT * FROM Win32_LogicalDisk")

	Dim aLDs
	Redim aLDs(0)
	i = 0
	For Each objItem In colItems
		If Ubound(aLDs) < i Then
			Redim Preserve aLDs(i)
		End If
		If objItem.DriveType = 3 Then
			sDevID = objItem.DeviceID
			aLDs(i) = sDevID
			i = i + 1
			'wscript.echo "found ld: " & sDevID
		End If
	Next
	Get_LogicalDisks = aLDs
End Function

Function Get_FixedVolumePaths(aMPs,aLDs)
	'strip any mount points that do not start with an LD
	dim aFixedVolumePaths
	Redim aFixedVolumePaths(0)
	For Each sMountPoint in aMPs
		bFoundLDforMP = False
		sMPDriveLetter = Left(sMountPoint,2)
		'wscript.echo "sMPDriveLetter: " & sMPDriveLetter

		'check that the mp has a drive letter in the ld list
		For Each sLogicalDisk in aLDs
			'sLDDriveLetter = Left(sLogicalDisk,2)
			'wscript.echo "sLogicalDisk: " & sLogicalDisk
			If sMPDriveLetter = sLogicalDisk Then
				bFoundLDforMP = True
			End If
		Next

		If bFoundLDforMP = True Then
			If Ubound(aFixedVolumePaths) < i Then
				Redim Preserve aFixedVolumePaths(i)
			End If
			aFixedVolumePaths(i) = sMountPoint
			i = i + 1
		End If
	Next

	Get_FixedVolumePaths = aFixedVolumePaths
End Function

''''''''''''MAIN
Function ZTIProcess()

		iRetVal = Success

		ZTIProcess = iRetval

		'!!!!!!!!!!!   INSERT YOUR CODE HERE   !!!!!!!!!!!!
		sStateRoot = "\\backupserver\share\robocopy"
		Set oTSEnv = WScript.CreateObject("Microsoft.SMS.TSEnvironment")

		'Get all potential source paths
		Dim aMPs
		aMPs = Get_UniqueFixedMountPoints
		Dim aLDs
		aLDs = Get_LogicalDisks
		Dim aSourcePathsTemp
		aSourcePathsTemp = Get_FixedVolumePaths(aMPs,aLDs)

		'strip out X:\ since it's always the WinPE disk
		Dim aSourcePaths
		Redim aSourcePaths(0)
		i = 0
		For Each sPath in aSourcePathsTemp
			If UBound(aSourcePaths) < i Then
				Redim Preserve aSourcePaths(i)
			End If
			If InStr(sPath,"X:") = False And sPath <> "" And sPath <> vbNull Then
					aSourcePaths(i) = sPath
					i = i + 1
			End If
		Next

		'Test all paths for C:\users or C:\documents and settings
		bUsersDirFound = False
		bUsersDirFound = Test_UsersDir(aSourcePaths)
		If bUsersDirFound <> True Then
			Throw_Warning "Failed to find the user directories."
			Wscript.Quit(100)
		End If

		sComputerName = Get_ComputerName
		If sComputerName = null Or sComputerName = "" Or sComputerName = -1 Then
			Throw_Warning "Failed to find the computer name."
			Wscript.Quit(300)
		End If

		bFolderAccess = False
		bFolderAccess = Test_FolderAccessOK(sStateRoot)
		If Is_Null(bFolderAccess) = True Or bFolderAccess = False Then
			msg = "Script doesn't have access to the target root: " & sStateRoot
			Throw_Warning msg
			bContinue = False
		Else
			sTargetPath = (Trim_Slash(sStateRoot)) + "\" + sComputerName
			Run_RobocopyBackup aSourcePaths,sTargetPath
		End If

End Function
// ]]></script>

Getting the Actions Right

We’ll need a few TS Actions to make this work.

  1. First, create a new group called “Backups”.
  2. Next, create a “Use Microsoft Deployment Toolkit Package” action.
  3. It would be a good idea to put a “Decrypt Bitlocker” action directly after the MDT action. See the blog post above under the “Requirements” header for detailed instructions.
  4. Create a “Connect to Network Folder” action with the following parameters. The account must have read\write permissions on the share and the NTFS files\folders in the share.
    Name: Connect to Backup Share
    Share: \\backupserver\sharename
    Account: domain\shareuser
  5. Create a “Set Task Sequence Variable” action with the following parameters. This only affects the WIM captures.
    Name: Set Backup Drive to All
    TS Variable: BackupDrive
    Value: ALL
  6. Create a “Set Task Sequence Variable” action with the following parameters. This is for your WIM captures.
    Name: Set Backup Location
    TS Variable: ComputerBackupLocation
    Value: \\backupserver\sharename\captures
  7. Create a “Run Command Line” action with the following parameters.
    Name: Robocopy Backup
    Command: run-robocopybackup.wsf
    Package: Scripts - Backups
  8. Create a “Run Command Line” action with the following parameters.
    Name: WIM Image Backup
    Command: cscript.exe "%SCRIPTROOT%\ZTIBackup.wsf"
    Package: Scripts - Backups
That’s it! You should now have some backups. 🙂
Advertisements

One thought on “SCCM Task Sequence – Robocopy and WIM Image Backups from WinPE

  1. Pingback: Table of Contents | windowsmasher

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s