1432 lines
47 KiB
XML
1432 lines
47 KiB
XML
<job id="ZTIUserState">
|
|
<script language="VBScript" src="ZTIUtility.vbs"/>
|
|
<script language="VBScript" src="ZTIDiskUtility.vbs"/>
|
|
<script language="VBScript">
|
|
' // ***************************************************************************
|
|
' //
|
|
' // Copyright (c) Microsoft Corporation. All rights reserved.
|
|
' //
|
|
' // Microsoft Deployment Toolkit Solution Accelerator
|
|
' //
|
|
' // File: ZTIUserState.wsf
|
|
' //
|
|
' // Version: 6.3.8456.1000
|
|
' //
|
|
' // Purpose: Capture and restore user state using USMT
|
|
' //
|
|
' // Usage: cscript.exe [//nologo] ZTIUserState.wsf [/debug:true]
|
|
' //
|
|
' // ***************************************************************************
|
|
|
|
Option Explicit
|
|
RunNewInstance
|
|
|
|
|
|
'//----------------------------------------------------------------------------
|
|
'// Global Constants
|
|
'//----------------------------------------------------------------------------
|
|
|
|
' No constants are required
|
|
|
|
|
|
'//----------------------------------------------------------------------------
|
|
'// Main Class
|
|
'//----------------------------------------------------------------------------
|
|
|
|
Class ZTIUserState
|
|
|
|
'//----------------------------------------------------------------------------
|
|
'// Class instance variable declarations
|
|
'//----------------------------------------------------------------------------
|
|
|
|
Dim iRetVal
|
|
Dim sUSMTPath
|
|
Dim sUSMTArchitecture
|
|
Dim sUSMTVersion
|
|
Dim sConfigFile
|
|
Dim bUseHardlinks
|
|
Dim sBaseArgs
|
|
Dim sDestinationLogicalDrive
|
|
|
|
|
|
'//----------------------------------------------------------------------------
|
|
'// Constructor to initialize needed global objects
|
|
'//----------------------------------------------------------------------------
|
|
|
|
Private Sub Class_Initialize
|
|
|
|
If oEnvironment.Item("DeploymentMethod") <> "SCCM" Then
|
|
sDestinationLogicalDrive = oUtility.GetOSTargetDriveLetter
|
|
End If
|
|
|
|
End Sub
|
|
|
|
|
|
'//----------------------------------------------------------------------------
|
|
'// Main routine
|
|
'//----------------------------------------------------------------------------
|
|
|
|
Function Main
|
|
|
|
Dim sScanStateFolder
|
|
Dim sFoundScanState
|
|
|
|
Dim sStatePath
|
|
Dim oMigXML
|
|
Dim sMigXMLArgs
|
|
Dim sXML, sFoundXML
|
|
Dim arrTmp
|
|
Dim sElement
|
|
Dim iRetVal
|
|
Dim sVariable
|
|
Dim sDefaultArgs
|
|
Dim bUnMapDrive, oDiskPart
|
|
Dim bValidStatePath
|
|
|
|
|
|
iRetVal = Success
|
|
|
|
|
|
'//----------------------------------------------------------------------------
|
|
'// Exit if user state capture/restore was bypassed or if on a server
|
|
'//----------------------------------------------------------------------------
|
|
|
|
If UCase(oEnvironment.Item("UserDataLocation")) = "NONE" then
|
|
If oUtility.Arguments.Exists("restore") then
|
|
oLogging.CreateEntry "User state restore bypassed.", LogTypeInfo
|
|
Else
|
|
oLogging.CreateEntry "User state capture bypassed.", LogTypeInfo
|
|
End if
|
|
Main = Success
|
|
Exit Function
|
|
End if
|
|
|
|
If UCase(oEnvironment.Item("IsServerOS")) = "TRUE" then
|
|
oLogging.CreateEntry "User state migration is not supported on server operating systems, exiting.", LogTypeInfo
|
|
Main = Success
|
|
Exit Function
|
|
End if
|
|
|
|
|
|
'//----------------------------------------------------------------------------
|
|
'// Determine the architecture to use (needed to find the right files later)
|
|
'//----------------------------------------------------------------------------
|
|
|
|
If oEnvironment.Item("Architecture") = "X86" Then
|
|
sUSMTArchitecture = "X86"
|
|
ElseIf oEnvironment.Item("Architecture") = "X64" and oEnvironment.Item("DeploymentMethod") = "SCCM" Then
|
|
sUSMTArchitecture = "amd64"
|
|
Else
|
|
sUSMTArchitecture = "X64"
|
|
End If
|
|
|
|
|
|
'//----------------------------------------------------------------------------
|
|
'// Determine the version of USMT to use
|
|
'//----------------------------------------------------------------------------
|
|
|
|
|
|
sUSMTVersion = ""
|
|
sUSMTPath = ""
|
|
|
|
' Always use hardlinks (when not using a network path)
|
|
bUseHardlinks = True
|
|
|
|
If oEnvironment.Item("DeploymentMethod") = "SCCM" then
|
|
|
|
sScanStateFolder = oShell.CurrentDirectory & "\" & sUSMTArchitecture
|
|
|
|
oLogging.CreateEntry "Checking for USMT Files in " & sScanStateFolder, LogTypeInfo
|
|
If oFSO.FileExists (sScanStateFolder & "\scanstate.exe") then
|
|
oLogging.CreateEntry "Found scanstate.exe version = " & oFSO.GetFileVersion(sScanStateFolder & "\scanstate.exe"), LogTypeInfo
|
|
sUSMTVersion = "USMT5"
|
|
sUSMTPath = sScanStateFolder
|
|
Else
|
|
oLogging.ReportFailure "Unable to find USMT files, cannot capture/restore user state.", 9705
|
|
End if
|
|
|
|
Else
|
|
|
|
' Use the USMT files in the USMT5 folder (which can be a later version)
|
|
|
|
sUSMTVersion = "USMT5"
|
|
sScanStateFolder = oEnvironment.Item("DeployRoot") & "\Tools\" & sUSMTArchitecture & "\" & sUSMTVersion
|
|
|
|
oLogging.CreateEntry "Checking for USMT Files in " & sScanStateFolder, LogTypeInfo
|
|
If oFSO.FileExists(sScanStateFolder & "\ScanState.exe") then
|
|
oLogging.CreateEntry "Found scanstate.exe version = " & oFSO.GetFileVersion(sScanStateFolder & "\scanstate.exe"), LogTypeInfo
|
|
sUSMTPath = oEnvironment.Item("DeployRoot") & "\Tools\" & sUSMTArchitecture & "\" & sUSMTVersion
|
|
Else
|
|
oLogging.ReportFailure "Unable to find USMT files, cannot capture/restore user state.", 9705
|
|
End if
|
|
|
|
End If
|
|
|
|
|
|
If sUSMTVersion = "" then
|
|
oLogging.ReportFailure "Unable to find any version of USMT, unable to perform user state migration.", 9701
|
|
End if
|
|
|
|
|
|
'//----------------------------------------------------------------------------
|
|
'// Install the appropriate USMT version
|
|
'//----------------------------------------------------------------------------
|
|
|
|
sUSMTPath = InstallUSMT
|
|
|
|
|
|
'//----------------------------------------------------------------------------
|
|
'// Determine the migration XML files to use and copy them locally
|
|
'//----------------------------------------------------------------------------
|
|
|
|
' Build a list of files
|
|
|
|
Set oMigXML = oEnvironment.ListItem("USMTMigFiles")
|
|
If oMigXML.Count = 0 then
|
|
oMigXML.Add "MigApp.xml", ""
|
|
oMigXML.Add "Migdocs.xml", ""
|
|
If oEnvironment.Item("OSVersion") = "WinPE" then
|
|
oMigXML.Add "UDIOffline.xml", ""
|
|
End if
|
|
End if
|
|
|
|
|
|
' Find each of the files and add them to the list of parameters
|
|
|
|
sMigXMLArgs = ""
|
|
|
|
For each sXML in oMigXML.Keys
|
|
|
|
' Figure out where the XML file resides.
|
|
|
|
sFoundXML = ""
|
|
|
|
If oFSO.FileExists(sUSMTPath & "\" & sXML) then
|
|
|
|
' This is where we want the file to be.
|
|
|
|
sFoundXML = sUSMTPath & "\" & sXML
|
|
|
|
ElseIf oFSO.FileExists(".\" & sUSMTArchitecture & "\" & sXML) then
|
|
|
|
' Typically the file would only be found under the working directory (in an architecture folder)
|
|
' with ConfigMgr and USMT 4. It needs to be copied to the local install folder.
|
|
|
|
sFoundXML = oShell.CurrentDirectory & "\" & sUSMTArchitecture & "\" & sXML
|
|
|
|
Else
|
|
|
|
' Try to find the file in any known location
|
|
|
|
iRetVal = oUtility.FindFile(sXML, sFoundXML)
|
|
|
|
End if
|
|
|
|
|
|
' Copy the file to the local install folder if it doesn't already exist or if the existing file is older
|
|
|
|
If sFoundXML = "" then
|
|
|
|
oLogging.CreateEntry "WARNING: Unable to find specified USMT migration XML file " & sXML, LogTypeWarning
|
|
|
|
ElseIf not oFSO.FileExists(sUSMTPath & "\" & sXML) then
|
|
|
|
oLogging.CreateEntry "Copying " & sFoundXML & " to local install directory " & sUSMTPath, LogTypeInfo
|
|
oFSO.CopyFile sFoundXML, sUSMTPath & "\" & sXML, true
|
|
|
|
' Add the file to the arguments list
|
|
|
|
sMigXMLArgs = sMigXMLArgs & " /i:""" & sUSMTPath & "\" & sXML & """"
|
|
|
|
Else
|
|
If oFSO.GetFile(sFoundXML).DateLastModified > oFSO.GetFile(sUSMTPath & "\" & sXML).DateLastModified then
|
|
|
|
oLogging.CreateEntry "Copying updated " & sFoundXML & " to local install directory overwriting existing file.", LogTypeInfo
|
|
oFSO.CopyFile sFoundXML, sUSMTPath & "\" & sXML, true
|
|
|
|
Else
|
|
|
|
oLogging.CreateEntry "Migration XML file " & sFoundXML & " is already present and up to date.", LogTypeInfo
|
|
End if
|
|
|
|
' Add the file to the arguments list
|
|
|
|
sMigXMLArgs = sMigXMLArgs & " /i:""" & sUSMTPath & "\" & sXML & """"
|
|
|
|
End if
|
|
|
|
Next
|
|
|
|
|
|
'//----------------------------------------------------------------------------
|
|
'// If a config XML file was specified, copy it to the install folder
|
|
'//----------------------------------------------------------------------------
|
|
|
|
' See if a config file was specified. Otherwise, use a default one.
|
|
|
|
If oEnvironment.Item("USMTConfigFile") <> "" then
|
|
sConfigFile = oEnvironment.Item("USMTConfigFile")
|
|
Else
|
|
sConfigFile = "ZTIUserState_Config.xml"
|
|
End if
|
|
|
|
|
|
' Find the file
|
|
|
|
sFoundXML = ""
|
|
|
|
If oFSO.FileExists(sUSMTPath & "\" & sConfigFile ) then
|
|
|
|
' This is where we want the file to be.
|
|
|
|
sFoundXML = sUSMTPath & "\" & sConfigFile
|
|
|
|
ElseIf oFSO.FileExists(".\" & sUSMTArchitecture & "\" & sConfigFile ) then
|
|
|
|
' Typically the file would only be found under the working directory (in an architecture folder)
|
|
' with ConfigMgr and USMT 4. It needs to be copied to the local install folder.
|
|
|
|
sFoundXML = oShell.CurrentDirectory & "\" & sUSMTArchitecture & "\" & sConfigFile
|
|
Else
|
|
|
|
' Try to find the file in any known location
|
|
|
|
iRetVal = oUtility.FindFile(sConfigFile, sFoundXML)
|
|
End If
|
|
|
|
|
|
' Copy the file to the local install folder if it doesn't already exist or if the existing file is older
|
|
|
|
If sUSMTVersion = "USMT3" then
|
|
|
|
oLogging.CreateEntry "Using USMT 3, not specifying USMT configuration XML file.", LogTypeInfo
|
|
|
|
ElseIf sFoundXML = "" then
|
|
|
|
oLogging.CreateEntry "WARNING: Unable to find specified USMT configuration XML file " & oEnvironment.Item("USMTConfigFile"), LogTypeWarning
|
|
|
|
ElseIf not oFSO.FileExists(sUSMTPath & "\" & sConfigFile) then
|
|
|
|
oLogging.CreateEntry "Copying " & sFoundXML & " to local install directory " & sUSMTPath, LogTypeInfo
|
|
oFSO.CopyFile sFoundXML, sUSMTPath & "\" & sConfigFile, true
|
|
|
|
' Add the file to the arguments list
|
|
|
|
sMigXMLArgs = sMigXMLArgs & " /config:""" & sUSMTPath & "\" & sConfigFile & """"
|
|
|
|
Else
|
|
If oFSO.GetFile(sFoundXML).DateLastModified > oFSO.GetFile(sUSMTPath & "\" & sConfigFile).DateLastModified then
|
|
|
|
oLogging.CreateEntry "Copying updated " & sFoundXML & " to local install directory overwriting existing file.", LogTypeInfo
|
|
oFSO.CopyFile sFoundXML, sUSMTPath & "\" & sConfigFile, true
|
|
|
|
Else
|
|
oLogging.CreateEntry "Migration XML file " & sFoundXML & " is already present and up to date.", LogTypeInfo
|
|
End if
|
|
|
|
' Add the file to the arguments list
|
|
|
|
sMigXMLArgs = sMigXMLArgs & " /config:""" & sUSMTPath & "\" & sConfigFile & """"
|
|
|
|
End if
|
|
|
|
|
|
'//----------------------------------------------------------------------------
|
|
'// Determine command line options to use
|
|
'//----------------------------------------------------------------------------
|
|
|
|
|
|
' Map the OSDMigrateAdditional*Options variables to the equivalent MDT variables
|
|
|
|
If oEnvironment.Item("OSDMigrateAdditionalCaptureOptions") <> "" and oEnvironment.Item("ScanStateArgs") = "" then
|
|
oEnvironment.Item("ScanStateArgs") = oEnvironment.Item("OSDMigrateAdditionalCaptureOptions")
|
|
End If
|
|
If oEnvironment.Item("OSDMigrateAdditionalRestoreOptions") <> "" and oEnvironment.Item("LoadStateArgs") = "" then
|
|
oEnvironment.Item("LoadStateArgs") = oEnvironment.Item("OSDMigrateAdditionalRestoreOptions")
|
|
End If
|
|
|
|
|
|
' Set the base capture and restore parameters
|
|
|
|
If oUtility.Arguments.Exists("restore") then
|
|
sVariable = "LoadStateArgs"
|
|
sDefaultArgs = " /v:5 /c /lac"
|
|
Else
|
|
sVariable = "ScanStateArgs"
|
|
sDefaultArgs = " /v:5 /c /o"
|
|
End if
|
|
If oEnvironment.Item(sVariable) <> "" Then
|
|
sBaseArgs = oEnvironment.Item(sVariable)
|
|
Else
|
|
sBaseArgs = sDefaultArgs
|
|
End If
|
|
|
|
|
|
' See if there are specific users to capture
|
|
|
|
If oEnvironment.Item("UDProfiles") <> "" then
|
|
|
|
arrTmp = split(oEnvironment.Item("UDProfiles"), ",")
|
|
For each sElement in arrTmp
|
|
sBaseArgs = sBaseArgs & " /ui:" & Trim(sElement)
|
|
Next
|
|
|
|
End if
|
|
|
|
|
|
' Add the XML files
|
|
|
|
sBaseArgs = sBaseArgs & " " & sMigXMLArgs
|
|
oLogging.CreateEntry "Base arguments: " & sBaseArgs, LogTypeInfo
|
|
|
|
|
|
'//----------------------------------------------------------------------------
|
|
'// See what we need to do
|
|
'//----------------------------------------------------------------------------
|
|
|
|
If oUtility.Arguments.Exists("estimate") then
|
|
|
|
' This is a ConfigMgr-only scenario.
|
|
|
|
' If the OSDStateStore path is already pointing to a network location, just make sure the path exists (creating the folder if it doesn't)
|
|
If Left(oEnvironment.Item("OSDStateStorePath"), 2) = "\\" then
|
|
|
|
oEnvironment.Item("USMTLocal") = "True" ' This causes the "Request State Store" step to be skipped
|
|
oUtility.ValidateConnection oEnvironment.Item("OSDStateStorePath")
|
|
oUtility.VerifyPathExists oEnvironment.Item("OSDStateStorePath")
|
|
|
|
' If using USMT 4, set hardlink parameters and exit
|
|
ElseIf sUSMTVersion <> "USMT3" then
|
|
|
|
oLogging.CreateEntry "Setting ConfigMgr variables to perform a hardlink migration.", LogTypeInfo
|
|
|
|
If oEnvironment.Item("OSDMigrateAdditionalCaptureOptions") = "" Then
|
|
oEnvironment.Item("OSDMigrateAdditionalCaptureOptions") = "/hardlink /nocompress"
|
|
Else
|
|
If Instr(oEnvironment.Item("OSDMigrateAdditionalCaptureOptions"), "/hardlink") = 0 Then
|
|
oEnvironment.Item("OSDMigrateAdditionalCaptureOptions") = oEnvironment.Item("OSDMigrateAdditionalCaptureOptions") & " /hardlink /nocompress"
|
|
End If
|
|
End If
|
|
If oEnvironment.Item("OSDMigrateAdditionalRestoreOptions") = "" Then
|
|
oEnvironment.Item("OSDMigrateAdditionalRestoreOptions") = "/hardlink /nocompress"
|
|
Else
|
|
If Instr(oEnvironment.Item("OSDMigrateAdditionalRestoreOptions"), "/hardlink") = 0 Then
|
|
oEnvironment.Item("OSDMigrateAdditionalRestoreOptions") = oEnvironment.Item("OSDMigrateAdditionalRestoreOptions") & " /hardlink /nocompress"
|
|
End If
|
|
End If
|
|
|
|
oEnvironment.Item("USMTLocal") = "True" ' This causes the "Request State Store" step to be skipped
|
|
oEnvironment.Item("OSDStateStorePath") = oUtility.StatePath ' Force a local path for the state store
|
|
|
|
' Using USMT 3, so perform an estimate.
|
|
Else
|
|
|
|
' Perform a state capture estimate
|
|
|
|
iRetVal = DoStateCapture(oUtility.LogPath & "\usmtestimate", true, sBaseArgs)
|
|
If iRetVal = 0 then
|
|
If UseLocal then
|
|
oEnvironment.Item("USMTLocal") = "True" ' This causes the "Request State Store" step to be skipped
|
|
oEnvironment.Item("OSDStateStorePath") = oUtility.StatePath ' Force a local path for the state store
|
|
Else
|
|
oEnvironment.Item("USMTLocal") = "False" ' This causes the "Request State Store" step to be run, using the SMP
|
|
End if
|
|
Else
|
|
oLogging.CreateEntry "Unable to determine user state size, assuming it will fit locally.", LogTypeWarning
|
|
oEnvironment.Item("USMTLocal") = "True" ' This causes the "Request State Store" step to be skipped
|
|
oEnvironment.Item("OSDStateStorePath") = oUtility.StatePath ' Force a local path for the state store
|
|
End if
|
|
|
|
End if
|
|
|
|
|
|
ElseIf oUtility.Arguments.Exists("restore") then
|
|
|
|
' Make sure the boot volume is visible, just in case it contains a state store
|
|
|
|
bUnMapDrive = false
|
|
set oDiskPart = GetBootDriveEx ( false, "", true )
|
|
If oDiskPart is Nothing then
|
|
|
|
oLogging.CreateEntry "The drive letter for the boot partition was not found.", LogTypeInfo
|
|
set oDiskPart = GetBootDriveEx ( true, "", true )
|
|
If oDiskPart is Nothing then
|
|
oLogging.CreateEntry "Unable to find boot drive.", LogTypeInfo
|
|
Else
|
|
oLogging.CreateEntry "Assigned drive letter " & oDiskPart.Drive & " to the boot volume.", LogTypeInfo
|
|
bUnMapDrive = true
|
|
End if
|
|
End if
|
|
|
|
|
|
' Make sure we have a state path
|
|
|
|
bValidStatePath = true
|
|
sStatePath = DetermineOSDStateStorePath("RESTORE")
|
|
|
|
If sStatePath = "" or not oFSO.FolderExists(sStatePath) then
|
|
|
|
oLogging.CreateEntry "No user state to restore, exiting.", LogTypeInfo
|
|
bValidStatePath = False
|
|
|
|
ElseIf sUSMTVersion = "USMT3" and not oFSO.FolderExists(sStatePath & "\USMT3") then
|
|
|
|
oLogging.CreateEntry "No user state folder (USMT3) found at " & oEnvironment.Item("StatePath") & ", unable to restore user state.", LogTypeWarning
|
|
bvalidStatePath = False
|
|
|
|
ElseIf Right(sUSMTVersion,1) >= "4" and Not oFSO.FolderExists(sStatePath & "\USMT") then
|
|
|
|
oLogging.CreateEntry "No user state folder (USMT) compatible with " & sUSMTVersion & " found at " & sStatePath & ", unable to restore user state.", LogTypeWarning
|
|
oLogging.CreateEntry "Please check if you are using a version of USMT that is compatible with this Operating System. USMT3 cannot be restored to Windows 7 or greater.", LogTypeWarning
|
|
bValidStatePath = False
|
|
|
|
End if
|
|
|
|
|
|
' Do the restore if we found a valid state store path
|
|
|
|
If bValidStatePath then
|
|
|
|
iRetVal = DoStateRestore(sStatePath, sBaseArgs)
|
|
|
|
oLogging.CreateEntry oUtility.ScriptName & " COMPLETED. Return Value = " & iRetVal, LogTypeInfo
|
|
End if
|
|
|
|
|
|
' Remove the boot drive letter if necessary
|
|
|
|
If bUnMapDrive then
|
|
oLogging.CreateEntry "Removing drive letter " & oDiskPart.Drive & " from the boot drive.", LogTypeInfo
|
|
RunDiskPartSilent array("list volume", "Select volume " & left(oDiskPart.Drive,2), "REMOVE LETTER=" & left(oDiskPart.Drive,2) & " NOERR", "exit" )
|
|
End if
|
|
|
|
|
|
ElseIf oUtility.Arguments.Exists("capture") then
|
|
|
|
' If running offline, find the existing OS
|
|
|
|
If oEnv("SystemDrive") = "X:" then ' We're in PE
|
|
|
|
' Find the local Windows installation (if not already determined)
|
|
|
|
If VerifyWinPEWinDir <> Success Then
|
|
DetermineWinPEWinDir
|
|
End If
|
|
|
|
If oEnvironment.Item("OSDWinPEWinDir") = "" Then
|
|
oLogging.ReportFailure "The offline Windows directory could not be determined, offline USMT migration cannot be performed", 9706
|
|
End If
|
|
End if
|
|
|
|
|
|
' Get the state path
|
|
|
|
sStatePath = DetermineOSDStateStorePath("CAPTURE")
|
|
|
|
If sUSMTVersion = "USMT5" then
|
|
oUtility.GetMajorMinorVersion oEnvironment.Item("ImageBuild")
|
|
If oUtility.VersionMajor = 6 and oUtility.VersionMinor = 1 then
|
|
oLogging.CreateEntry "Special Case for USMT 5.0 scanstate for Windows 7 Target. /targetWindows7", LogTypeInfo
|
|
sBaseArgs = sBaseArgs & " /targetWindows7"
|
|
End if
|
|
End if
|
|
|
|
|
|
' Now perform the capture
|
|
|
|
iRetVal = DoStateCapture(sStatePath, false, sBaseArgs)
|
|
If iRetVal <> Success then
|
|
oLogging.ReportFailure "Non-zero return code from USMT capture, rc = " & iRetVal, 9707
|
|
End if
|
|
|
|
Else
|
|
|
|
oLogging.ReportFailure "No valid command line option was specified", 9708
|
|
|
|
End if
|
|
|
|
|
|
Main = iRetval
|
|
|
|
' All done
|
|
|
|
End Function
|
|
|
|
|
|
'//---------------------------------------------------------------------------
|
|
'// Function: DoStateCapture
|
|
'// Purpose: Perform a state capture or estimate as requested
|
|
'//---------------------------------------------------------------------------
|
|
Function DoStateCapture(sStatePath, bEstimate, sBaseArgs)
|
|
|
|
Dim iRetVal, sScanStateArgs, sElement
|
|
Dim sLog, sProgressFileName
|
|
Dim sCmd, arrTmp, iType, iEstimate, iActual
|
|
|
|
iRetVal = Failure
|
|
|
|
|
|
If bEstimate then
|
|
oLogging.CreateEntry "------ Performing State Capture Estimate ------", LogTypeInfo
|
|
Else
|
|
oLogging.CreateEntry "------ Performing State Capture ------", LogTypeInfo
|
|
End if
|
|
|
|
|
|
' Figure out what to call the logs
|
|
|
|
If bEstimate then
|
|
sLog = oUtility.LogPath & "\USMTestimate.log"
|
|
sProgressFileName = oUtility.LogPath & "\USMTestimate.prg"
|
|
On Error Resume Next
|
|
oFSO.DeleteFile oUtility.LogPath & "\USMTestimate.*"
|
|
On Error Goto 0
|
|
Else
|
|
sLog = oUtility.LogPath & "\USMTcapture.log"
|
|
sProgressFileName = oUtility.LogPath & "\USMTcapture.prg"
|
|
On Error Resume Next
|
|
oFSO.DeleteFile oUtility.LogPath & "\USMTcapture.*"
|
|
On Error Goto 0
|
|
End if
|
|
oLogging.CreateEntry "USMT log file: " & sLog, LogTypeInfo
|
|
oLogging.CreateEntry "USMT progress file: " & sProgressFileName, LogTypeInfo
|
|
|
|
|
|
' Add the appropriate options
|
|
|
|
If bEstimate then
|
|
sScanStateArgs = """" & oUtility.LogPath & "\usmtestimate"" " & sBaseArgs & " /p /nocompress /l:" & sLog & " /progress:" & sProgressFileName
|
|
Else
|
|
oUtility.VerifyPathExists sStatePath
|
|
sScanStateArgs = QuotedPath(sStatePath) & " " & AdjustArgs("CAPTURE", sStatePath, sBaseArgs) & " /l:" & sLog & " /progress:" & sProgressFileName
|
|
End if
|
|
|
|
|
|
' If running offline, add the necessary parameters
|
|
|
|
If oEnv("SystemDrive") = "X:" then ' We're in PE
|
|
|
|
' Set USMT working directory environment variable to a writable path
|
|
|
|
oEnv("USMT_WORKING_DIR") = oEnvironment.Item("OSDWinPEWindir") & "\temp"
|
|
oEnv("TEMP") = oEnvironment.Item("OSDWinPEWindir") & "\temp"
|
|
|
|
|
|
' Set the platform of the old OS (32 or 64)
|
|
|
|
Select Case oEnvironment.Item("OriginalArchitecture")
|
|
|
|
Case "X64"
|
|
oEnv("MIG_OFFLINE_PLATFORM_ARCH") = "64"
|
|
Case "X86"
|
|
oEnv("MIG_OFFLINE_PLATFORM_ARCH") = "32"
|
|
Case Else
|
|
oLogging.ReportFailure "Architecture of the original Operating System could not be determined", 9709
|
|
End Select
|
|
|
|
|
|
' Add the needed offline switches
|
|
|
|
If Instr(1, oEnvironment.Item("OSDWinPEWindir"), ".old", vbTextCompare) > 0 then
|
|
sScanStateArgs = sScanStateArgs & " /offlineWinOld:""" & oEnvironment.Item("OSDWinPEWindir")
|
|
Else
|
|
sScanStateArgs = sScanStateArgs & " /offlineWinDir:" & oEnvironment.Item("OSDWinPEWindir")
|
|
End if
|
|
|
|
|
|
' Make sure restore uses hardlinks too (for ConfigMgr) if capture is using them
|
|
|
|
If oEnvironment.Item("DeploymentMethod") = "SCCM" and Instr(sScanStateArgs, "/hardlink") > 0 then
|
|
If oEnvironment.Item("OSDMigrateAdditionalRestoreOptions") = "" Then
|
|
oEnvironment.Item("OSDMigrateAdditionalRestoreOptions") = "/hardlink /nocompress"
|
|
Else
|
|
If Instr(oEnvironment.Item("OSDMigrateAdditionalRestoreOptions"), "/hardlink") = 0 Then
|
|
oEnvironment.Item("OSDMigrateAdditionalRestoreOptions") = oEnvironment.Item("OSDMigrateAdditionalRestoreOptions") & " /hardlink /nocompress"
|
|
End If
|
|
End If
|
|
End if
|
|
|
|
End if
|
|
|
|
|
|
' Substitute the appropriate custom key values
|
|
|
|
sScanStateArgs = oEnvironment.Substitute(sScanStateArgs)
|
|
oLogging.CreateEntry "ScanState arguments: " & sScanStateArgs, LogTypeInfo
|
|
|
|
|
|
' Execute the Scanstate command
|
|
|
|
If bEstimate then
|
|
iType = LogTypeWarning
|
|
oLogging.CreateEvent 41004, LogTypeInfo, "ZTI Beginning user state estimate", Array(bEstimate)
|
|
Else
|
|
iType = LogTypeError
|
|
oLogging.CreateEvent 41004, LogTypeInfo, "ZTI Beginning user state capture", Array(bEstimate)
|
|
End if
|
|
|
|
On Error Resume Next
|
|
oShell.CurrentDirectory = sUSMTPath
|
|
sCmd = "cmd /c """"" & sUSMTPath & "\scanstate.exe"" " & sScanStateArgs & " > nul 2>&1"""
|
|
iRetVal = RunWithProgress(sCmd, sProgressFileName)
|
|
If Err then
|
|
iRetVal = Err.Number
|
|
oLogging.CreateEvent 41006, iType, "ZTI ERROR - Unable to run Scanstate, Error = " & Err.Description & " (" & Err.Number & ")", Array(bEstimate, Err.Number)
|
|
ElseIf iRetVal <> 0 and iRetVal <> 3 then
|
|
oLogging.CreateEvent 41006, iType, "ZTI ERROR - Non-zero return code from Scanstate, RC = " & iRetVal, Array(bEstimate, iRetVal)
|
|
If bEstimate then
|
|
ScanProgress sProgressFileName, "ESTIMATE"
|
|
Else
|
|
ScanProgress sProgressFileName, "SCANSTATE"
|
|
End if
|
|
Else
|
|
|
|
If iRetVal = 3 then
|
|
oLogging.CreateEntry "USMT_WOULD_HAVE_FAILED - At least one error was skipped as a result of /c.", LogTypeWarning
|
|
oLogging.CreateEvent 41005, LogTypeInfo, "ZTI Scanstate estimate was successful with Warning (USMT_WOULD_HAVE_FAILED).", Array(bEstimate)
|
|
iRetVal = 0
|
|
End if
|
|
|
|
If bEstimate then
|
|
oLogging.CreateEvent 41005, LogTypeInfo, "ZTI Scanstate estimate was successful.", Array(bEstimate)
|
|
ScanProgress sProgressFileName, "ESTIMATE"
|
|
Else
|
|
|
|
' Get the estimated size
|
|
|
|
If oEnvironment.Item("USMTEstimate") <> "" then
|
|
iEstimate = oEnvironment.Item("USMTEstimate")
|
|
Else
|
|
iEstimate = 0
|
|
End if
|
|
|
|
|
|
' Get the actual size
|
|
|
|
iActual = Int(oFSO.GetFolder(sStatePath).Size / 1048576)
|
|
|
|
|
|
' Report an event with both
|
|
|
|
' oLogging.CreateEvent 41005, LogTypeInfo, "ZTI Scanstate was successful. Estimated size: " & CStr(iEstimate) & "MB. Actual size: " & iActual & "MB.", Array(bEstimate, iEstimate, iActual)
|
|
ScanProgress sProgressFileName, "SCANSTATE"
|
|
|
|
End if
|
|
iRetVal = Success
|
|
End if
|
|
On Error Goto 0
|
|
|
|
DoStateCapture = iRetVal
|
|
|
|
End Function
|
|
|
|
|
|
'//---------------------------------------------------------------------------
|
|
'// Function: DoStateRestore
|
|
'// Purpose: Determine if user state can be stored locally or not
|
|
'//---------------------------------------------------------------------------
|
|
Function DoStateRestore(sStatePath, sBaseArgs)
|
|
|
|
Dim iRetVal, sLoadStateArgs
|
|
Dim sLog, sProgressFileName
|
|
Dim sCmd, re
|
|
Dim arrTmp, sElement
|
|
|
|
|
|
iRetVal = Failure
|
|
|
|
oLogging.CreateEntry "------ Performing State Restore ------", LogTypeInfo
|
|
|
|
|
|
' Assign the log file names
|
|
|
|
sLog = oUtility.LogPath & "\USMTrestore.log"
|
|
sProgressFileName = oUtility.LogPath & "\USMTrestore.prg"
|
|
On Error Resume Next
|
|
oFSO.DeleteFile oUtility.LogPath & "\USMTrestore.*"
|
|
On Error Goto 0
|
|
|
|
|
|
' Add the appropriate options
|
|
|
|
sLoadStateArgs = QuotedPath(sStatePath) & " " & AdjustArgs("RESTORE", sStatePath, sBaseArgs) & " /l:" & sLog & " " & " /progress:" & sProgressFileName
|
|
|
|
|
|
' Substitute the appropriate custom key values
|
|
|
|
sLoadStateArgs = oEnvironment.Substitute(sLoadStateArgs)
|
|
|
|
|
|
' Execute the Loadstate command
|
|
|
|
oLogging.CreateEvent 41007, LogTypeInfo, "ZTI Beginning user state restore.", Array()
|
|
|
|
On Error Resume Next
|
|
oShell.CurrentDirectory = sUSMTPath
|
|
sCmd = "cmd /c """"" & sUSMTPath & "\loadstate.exe"" " & sLoadStateArgs & " > nul 2>&1"""
|
|
iRetVal = RunWithProgress(sCmd, sProgressFileName)
|
|
If Err then
|
|
iRetVal = Err.Number
|
|
oLogging.CreateEvent 41007, LogTypeError, "ZTI ERROR - Unable to run Loadstate, Error = " & Err.Description & " (" & Err.Number & ")", Array(Err.Number)
|
|
' Move the state store if it is local so OSD doesn't delete it
|
|
If ucase(Left(sStatePath, 2)) = ucase(sDestinationLogicalDrive) then
|
|
oFSO.MoveFolder sStatePath, Substitute("%WINDIR%\StateStore")
|
|
oLogging.CreateEntry "Moving state store directory " & sStatePath & " to %WINDIR%\StateStore so that it does not get deleted.", LogTypeWarning
|
|
End if
|
|
ElseIf iRetVal <> Success and iRetVal <> 3 then
|
|
oLogging.CreateEvent 41007, LogTypeError, "ZTI ERROR - Non-zero return code from Loadstate, RC = " & iRetVal, Array(iRetVal)
|
|
' Move the state store if it is local so OSD doesn't delete it
|
|
If ucase(Left(sStatePath, 2)) = ucase(sDestinationLogicalDrive) then
|
|
oFSO.MoveFolder sStatePath, Substitute("%WINDIR%\StateStore")
|
|
oLogging.CreateEntry "Moving state store directory " & sStatePath & " to %WINDIR%\StateStore so that it does not get deleted.", LogTypeWarning
|
|
End if
|
|
ScanProgress sProgressFileName, "LOADSTATE"
|
|
Else
|
|
If iRetVal = 3 then
|
|
oLogging.CreateEntry "USMT_WOULD_HAVE_FAILED - At least one error was skipped as a result of /c.", LogTypeWarning
|
|
oLogging.CreateEvent 41008, LogTypeInfo, "ZTI User state restored successfully with Warning (USMT_WOULD_HAVE_FAILED).", Array()
|
|
End if
|
|
iRetVal = Success
|
|
oLogging.CreateEvent 41008, LogTypeInfo, "ZTI User state restored successfully.", Array()
|
|
ScanProgress sProgressFileName, "LOADSTATE"
|
|
End if
|
|
On Error Goto 0
|
|
|
|
DoStateRestore = iRetVal
|
|
|
|
End Function
|
|
|
|
|
|
'//---------------------------------------------------------------------------
|
|
'// Function: QuotedPath
|
|
'// Purpose: Uses appropriate rules needed for USMT when specify store path
|
|
'//---------------------------------------------------------------------------
|
|
Function QuotedPath(sStatePath)
|
|
|
|
If mid(sStatePath,2,1) = ":" and len(sStatePath) < 3 then
|
|
QuotedPath = sStatePath
|
|
ElseIf mid(sStatePath,2,2) = ":\" and len(sStatePath) < 4 then
|
|
QuotedPath = sStatePath
|
|
ElseIf Right(sStatePath,1) = "\" then
|
|
QuotedPath = """" & Left(sStatePath, Len(sStatePath) - 1) & """"
|
|
Else
|
|
QuotedPath= """" & sStatePath & """"
|
|
End if
|
|
|
|
End Function
|
|
|
|
'//---------------------------------------------------------------------------
|
|
'// Function: AdjustArgs
|
|
'// Purpose: Specify hardlink properties when appropriate
|
|
'//---------------------------------------------------------------------------
|
|
Function AdjustArgs(sMode, sStatePath, sBaseArgs)
|
|
|
|
' Default is to use arguments untouched
|
|
|
|
AdjustArgs = sBaseArgs
|
|
If (not bUseHardlinks) or oEnvironment.Item("DeploymentType") = "REPLACE" then
|
|
|
|
' Add an encryption key if specified
|
|
|
|
If oEnvironment.Item("ForceEncryption") = "YES" and oEnvironment.Item("UserDataKey") <> "" then
|
|
AdjustArgs = AdjustArgs & " /encrypt /key:" & oEnvironment.Item("UserDataKey")
|
|
End if
|
|
|
|
|
|
' Only use hardlinks for non-replace scenarios
|
|
|
|
Exit Function
|
|
|
|
ElseIf Left(sStatePath, 2) = "\\" then
|
|
|
|
' Add an encryption key if specified
|
|
|
|
If oEnvironment.Item("ForceEncryption") = "YES" and oEnvironment.Item("UserDataKey") <> "" then
|
|
AdjustArgs = AdjustArgs & " /encrypt /key:" & oEnvironment.Item("UserDataKey")
|
|
End if
|
|
|
|
|
|
' Only use hardlinks for local paths
|
|
|
|
Exit Function
|
|
|
|
Else
|
|
|
|
' Hardlinks are allowed and using a local path, so add the parameters
|
|
' if they aren't already present
|
|
If Instr(1, sBaseArgs, "/hardlink") = 0 then
|
|
If sMode = "CAPTURE" then
|
|
AdjustArgs = AdjustArgs & " /hardlink /efs:hardlink"
|
|
ElseIf oFSO.FileExists(sStatePath & "\USMT\hardlink.main") then
|
|
AdjustArgs = AdjustArgs & " /hardlink"
|
|
End if
|
|
End if
|
|
If Instr(1, AdjustArgs, "/hardlink") <> 0 and Instr(1, sBaseArgs, "/nocompress") = 0 then
|
|
AdjustArgs = AdjustArgs & " /nocompress"
|
|
End if
|
|
End if
|
|
|
|
End Function
|
|
|
|
'//---------------------------------------------------------------------------
|
|
'// Function: RunWithProgress
|
|
'// Purpose: Runs the specified command and displays progress
|
|
'//---------------------------------------------------------------------------
|
|
Function RunWithProgress(sCmd, sProgressFileName)
|
|
|
|
Dim oExec
|
|
Dim lastHeartbeat
|
|
Dim lastStart
|
|
Dim iHeartbeat
|
|
Dim iMinutes
|
|
Dim iOffset
|
|
Dim oProgressFile
|
|
Dim sNewText
|
|
Dim arrNewLines
|
|
Dim i
|
|
Dim arrValues
|
|
|
|
|
|
' Initialize the last heartbeat time (start the timer) and interval
|
|
|
|
lastHeartbeat = Now
|
|
iHeartbeat = 5
|
|
|
|
|
|
' Start the command
|
|
|
|
oLogging.CreateEntry "About to run command: " & sCmd, LogTypeInfo
|
|
lastStart = Now
|
|
Set oExec = oShell.Exec(sCmd)
|
|
Do While oExec.Status = 0
|
|
|
|
' Sleep 2 seconds
|
|
|
|
WScript.Sleep 2000
|
|
|
|
|
|
' See if it is time for a heartbeat
|
|
|
|
If iHeartbeat > 0 and DateDiff("n", lastHeartbeat, Now) > iHeartbeat then
|
|
iMinutes = DateDiff("n", lastStart, Now)
|
|
oLogging.CreateEvent 41003, LogTypeInfo, "ZTI Heartbeat: command has been running for " & CStr(iMinutes) & " minutes (process ID " & oExec.ProcessID & ")", Array(iMinutes)
|
|
lastHeartbeat = Now
|
|
End if
|
|
|
|
|
|
' Open the progress file and get the new lines
|
|
|
|
If oFSO.FileExists(sProgressFileName) then
|
|
|
|
On Error Resume Next
|
|
|
|
If oFSO.GetFile(sProgressFileName).Size > iOffset then
|
|
|
|
' Read the new lines
|
|
|
|
Set oProgressFile = oFSO.OpenTextFile(sProgressFileName, ForReading)
|
|
oProgressFile.Skip iOffset
|
|
sNewText = oProgressFile.ReadAll
|
|
iOffset = iOffset + Len(sNewText)
|
|
oProgressFile.Close
|
|
|
|
|
|
' Process the lines
|
|
|
|
On Error Goto 0
|
|
arrNewLines = Split(sNewText, vbCrLf)
|
|
For i = UBound(arrNewLines) - 1 to 0 Step -1
|
|
|
|
' Find the first total percentage completed message, then throw away the rest
|
|
|
|
arrValues = Split(arrNewLines(i), ",", 5)
|
|
If UBound(arrValues) >= 4 and Instr(arrValues(3), "totalPercentageCompleted") > 0 then
|
|
oLogging.ReportProgress "User state is being processed", CInt(arrValues(4))
|
|
Exit For
|
|
End if
|
|
|
|
Next
|
|
|
|
End if
|
|
|
|
On Error Goto 0
|
|
|
|
End if
|
|
|
|
Loop
|
|
|
|
|
|
' Make sure we hit 100%
|
|
|
|
oLogging.ReportProgress "USMT execution completed", 100
|
|
|
|
|
|
' Return the exit code to the caller
|
|
|
|
oLogging.CreateEntry "Return code from command = " & oExec.ExitCode, LogTypeInfo
|
|
RunWithProgress = oExec.ExitCode
|
|
|
|
End Function
|
|
|
|
|
|
'//---------------------------------------------------------------------------
|
|
'// Function: UseLocal
|
|
'// Purpose: Determine if user state can be stored locally or not
|
|
'//---------------------------------------------------------------------------
|
|
Function UseLocal()
|
|
|
|
Dim iRetVal
|
|
Dim oSizeFile, sLine, arrColumns, iStateSize, iAvailableSize, iImageSize, iNeededSize, iTotalSize, dMultiplier, sDrive
|
|
Dim bStoreLocal
|
|
Dim sSizeFile
|
|
|
|
|
|
bStoreLocal = false
|
|
|
|
|
|
' See if an estimate file can be located.
|
|
|
|
sSizeFile = ""
|
|
If oFSO.FileExists(oUtility.LogPath & "\usmtestimate\usmt3\usmtsize.txt") then ' USMT 3.0 name
|
|
sSizeFile = oUtility.LogPath & "\usmtestimate\usmt3\usmtsize.txt"
|
|
ElseIf oFSO.FileExists(oUtility.LogPath & "\usmtestimate\usmtsize.txt") then ' USMT 2.6 name
|
|
sSizeFile = oUtility.LogPath & "\usmtestimate\usmtsize.txt"
|
|
ElseIf oFSO.FileExists(oUtility.LogPath & "\usmtestimate\USMT\usmtsize.txt") then ' USMT 4.0 name
|
|
sSizeFile = oUtility.LogPath & "\usmtestimate\usmt\usmtsize.txt"
|
|
Else
|
|
sSizeFile = ""
|
|
End if
|
|
|
|
|
|
if sSizeFile <> "" then
|
|
|
|
' Found the file, retrieve the size from the first line, second value.
|
|
|
|
Set oSizeFile = oFSO.OpenTextFile(sSizeFile, ForReading, True)
|
|
sLine = oSizeFile.ReadLine
|
|
arrColumns = Split(sLine, vbTab)
|
|
iStateSize = Int(arrColumns(1) / 1048576) + 1 ' Round up
|
|
oLogging.CreateEntry "Estimated size of user state data (in MB) = " & iStateSize, LogTypeInfo
|
|
oEnvironment.Item("USMTEstimate") = iStateSize
|
|
|
|
|
|
' Get the available space on drive C:
|
|
|
|
sDrive = Left(oUtility.LogPath,2)
|
|
iAvailableSize = Int(oFSO.GetDrive(sDrive).FreeSpace / 1048576) - 1 ' Round down
|
|
oLogging.CreateEntry "Available space on drive C: for user state capture, Windows PE, and logs = " & iAvailableSize, LogTypeInfo
|
|
|
|
|
|
' Make sure there is currently enough space for the user state (with 10% fudge factor), including 150MB for WinPE and logs. If there is, then do more calculations.
|
|
|
|
If iAvailableSize > (iStateSize * 1.1 + 150) then
|
|
|
|
oLogging.CreateEntry "There is enough space to do a local user state capture, making sure the image will still fit.", LogTypeInfo
|
|
|
|
|
|
' Determine the multiplier
|
|
|
|
If oEnvironment.Item("ImageSizeMultiplier") <> "" then
|
|
dMultiplier = CSng(oEnvironment.Item("ImageSizeMultiplier"))
|
|
oLogging.CreateEntry "Using specified ImageSizeMultiplier = " & dMultiplier, LogTypeInfo
|
|
Else
|
|
dMultiplier = 2.5
|
|
oLogging.CreateEntry "Using default ImageSizeMultiplier = " & dMultiplier, LogTypeInfo
|
|
End if
|
|
|
|
|
|
' Guess at the expanded image size using the WIM file size
|
|
|
|
If oEnvironment.Item("ImageSize") <> "" then
|
|
iImageSize = CInt(oEnvironment.Item("ImageSize"))
|
|
oLogging.CreateEntry "Using specified image size = " & iImageSize, LogTypeInfo
|
|
ElseIf oFSO.FileExists(oEnvironment.Item("InstallFromPath")) then
|
|
oLogging.CreateEntry "Checking size of " & oEnvironment.Item("InstallFromPath"), LogTypeInfo
|
|
iImageSize = (Int(oFSO.GetFile(oEnvironment.Item("InstallFromPath")).Size / 1048576) + 1) * dMultiplier ' Assume 2.5X compression
|
|
oLogging.CreateEntry "Estimated size of image (assuming " & dMultiplier & "X compression) = " & iImageSize, LogTypeInfo
|
|
ElseIf oFSO.FileExists(oEnvironment.Item("OSDPACKAGEPATH") & "\OS.WIM") then
|
|
oLogging.CreateEntry "Checking size of " & oEnvironment.Item("OSDPACKAGEPATH") & "\OS.WIM", LogTypeInfo
|
|
iImageSize = (Int(oFSO.GetFile(oEnvironment.Item("OSDPACKAGEPATH") & "\OS.WIM").Size / 1048576) + 1) * dMultiplier ' Assume 2.5X compression
|
|
oLogging.CreateEntry "Estimated size of image (assuming " & dMultiplier & "X compression) = " & iImageSize, LogTypeInfo
|
|
Else
|
|
iImageSize = 7168 ' Throw a SWAG guess of 7GB
|
|
oLogging.CreateEntry "WARNING - Cannot determine image size, guessing 7GB", LogTypeWarning
|
|
End if
|
|
|
|
|
|
' Calc total required space, allowing a 10% fudge factor for state storage and 150MB for WinPE, logs, etc.
|
|
|
|
iNeededSize = (iStateSize * 1.1) + iImageSize + 150
|
|
oLogging.CreateEntry "Estimated total space needed on C: partion = " & iNeededSize, LogTypeInfo
|
|
|
|
|
|
' Get the size of C:
|
|
|
|
iTotalSize = Int(oFSO.GetDrive(sDrive).TotalSize / 1048576) - 1 ' Round down
|
|
oLogging.CreateEntry "Total space available on " & sDrive & " drive = " & iTotalSize, LogTypeInfo
|
|
|
|
|
|
' Enough space?
|
|
|
|
If iTotalSize > iNeededSize then
|
|
bStoreLocal = true
|
|
oLogging.CreateEntry "User state can be backed up locally.", LogTypeInfo
|
|
Else
|
|
oLogging.CreateEntry "Backing up user state locally would not leave enough space for the OS image. State will be written to a network share.", LogTypeInfo
|
|
End if
|
|
|
|
Else
|
|
|
|
oLogging.CreateEntry "Unable to store user state information locally, not enough available disk space.", LogTypeInfo
|
|
|
|
End if
|
|
|
|
else
|
|
|
|
oLogging.CreateEntry "Unable to determine if user state can be stored locally, assuming it must be remote.", LogTypeWarning
|
|
|
|
end if
|
|
|
|
|
|
UseLocal = bStoreLocal
|
|
|
|
End Function
|
|
|
|
|
|
'//---------------------------------------------------------------------------
|
|
'// Function: ScanProgress
|
|
'// Purpose: Scan the USMT progress file to find the errors and warnings.
|
|
'//---------------------------------------------------------------------------
|
|
Function ScanProgress(sProgressFile, sType)
|
|
|
|
Dim oProgressFile, sLine, arrParts
|
|
Dim iErrors, iWarnings
|
|
Dim iType
|
|
|
|
|
|
' Determine how many warnings and errors were logged.
|
|
|
|
iErrors = 0
|
|
iWarnings = 0
|
|
|
|
Set oProgressFile = oFSO.OpenTextFile(sProgressFile)
|
|
While not oProgressFile.AtEndOfStream
|
|
sLine = oProgressFile.ReadLine
|
|
arrParts = Split(sLine, ",", 5)
|
|
If Trim(arrParts(3)) = "error" then
|
|
If Instr(sLine, "action, Abort") > 0 then
|
|
iErrors = iErrors + 1
|
|
oLogging.CreateEntry "Fatal " & arrParts(4) & " error reported by USMT: " & sLine, LogTypeError
|
|
Else
|
|
iWarnings = iWarnings + 1
|
|
If iWarnings < 100 then
|
|
oLogging.CreateEntry "Non-fatal error " & arrParts(4) & " reported by USMT: " & sLine, LogTypeWarning
|
|
ElseIf iWarnings = 100 then
|
|
oLogging.CreateEntry "Too many USMT errors found, suppressing remaining messages.", LogTypeWarning
|
|
End if
|
|
End if
|
|
End if
|
|
WEnd
|
|
|
|
|
|
' Report how many errors and warnings were found.
|
|
|
|
If iErrors > 0 then
|
|
iType = LogTypeError
|
|
ElseIf iWarnings > 0 then
|
|
iType = LogTypeWarning
|
|
Else
|
|
iType = LogTypeInfo
|
|
End if
|
|
oLogging.CreateEvent 41010, iType, "ZTI USMT " & sType & " reported " & iErrors & " errors and " & iWarnings & " warnings.", Array(sType, iErrors, iWarnings)
|
|
|
|
End Function
|
|
|
|
|
|
|
|
'//---------------------------------------------------------------------------
|
|
'// Function: InstallUSMT
|
|
'// Purpose: Copy the USMT files locally
|
|
'//---------------------------------------------------------------------------
|
|
Function InstallUSMT
|
|
Dim sDestPath
|
|
|
|
|
|
' Copy the files locally
|
|
|
|
sDestPath = oUtility.LocalRootPath & "\USMT\" & oEnvironment.Item("Architecture")
|
|
oUtility.VerifyPathExists sDestPath
|
|
oUtility.RunWithHeartbeat "cmd.exe /c xcopy /iesryhd """ & sUSMTPath & """ """ & sDestPath & """ 1>> " & oLogging.LogPath & "\ZTIUserState_xcopy.log 2>>&1 "
|
|
|
|
InstallUSMT = sDestPath
|
|
|
|
End Function
|
|
|
|
|
|
|
|
'//---------------------------------------------------------------------------
|
|
'// Function: DetermineOSDStateStorePath
|
|
'// Purpose: Determine the path to be used for state capture or restore
|
|
'//---------------------------------------------------------------------------
|
|
Function DetermineOSDStateStorePath(sPhase)
|
|
|
|
Dim bUseNetwork
|
|
|
|
|
|
' If this is an estimate, just return blank
|
|
|
|
If sPhase = "ESTIMATE" then
|
|
DetermineOSDStateStorePath = ""
|
|
Exit Function
|
|
End if
|
|
|
|
|
|
' If OSDStateStorePath is already set, return that (fixing local paths if necessary)
|
|
|
|
If oEnvironment.Item("OSDStateStorePath") <> "" then
|
|
If Mid(oEnvironment.Item("OSDStateStorePath"), 2, 1) = ":" and sPhase = "RESTORE" then
|
|
oEnvironment.Item("OSDStateStorePath") = GetUserStateDrive(oEnvironment.Item("OSDStateStorePath"))
|
|
End if
|
|
DetermineOSDStateStorePath = oEnvironment.Item("OSDStateStorePath")
|
|
Exit Function
|
|
End if
|
|
|
|
|
|
' If StatePath was set by the Lite Touch wizard, use that path
|
|
|
|
If oEnvironment.Item("StatePath") <> "" then
|
|
oEnvironment.Item("OSDStateStorePath") = oEnvironment.Item("StatePath")
|
|
|
|
' If UserDataLocation is set to a UNC path (typically via CustomSettings.ini, use that path
|
|
|
|
ElseIf Left(oEnvironment.Item("UserDataLocation"), 2) = "\\" then
|
|
oEnvironment.Item("OSDStateStorePath") = oEnvironment.Substitute(oEnvironment.Item("UserDataLocation"))
|
|
|
|
' If UserDataLocation points to a local path, use it
|
|
|
|
ElseIf Mid(oEnvironment.Item("UserDataLocation"), 2, 1) = ":" then
|
|
oEnvironment.Item("OSDStateStorePath") = oEnvironment.Item("UserDataLocation")
|
|
|
|
' Still no path? Check based on the UserDataLocation specified
|
|
|
|
Else
|
|
|
|
' Figure out if we should use the network
|
|
|
|
If UCase(oEnvironment.Item("DeploymentType")) = "REPLACE" then
|
|
bUseNetwork = true
|
|
ElseIf sUSMTVersion <> "USMT3" then
|
|
' Hardlinks supported, always use local
|
|
bUseNetwork = false
|
|
Else
|
|
' Perform an estimate
|
|
iRetVal = DoStateCapture(oUtility.LogPath & "\usmtestimate", true, sBaseArgs)
|
|
If iRetVal = 0 then
|
|
If UseLocal then
|
|
bUseNetwork = false
|
|
Else
|
|
bUseNetwork = true
|
|
End if
|
|
Else
|
|
oLogging.CreateEntry "Unable to determine user state size, assuming it will fit locally.", LogTypeWarning
|
|
bUseNetwork = false
|
|
End if
|
|
End if
|
|
|
|
|
|
' Check specified user data location
|
|
|
|
Select Case UCase(oEnvironment.Item("UserDataLocation"))
|
|
Case "NETWORK"
|
|
If oEnvironment.Item("UDShare") <> "" then
|
|
If oEnvironment.Item("UDDir") = "" then
|
|
oEnvironment.Item("OSDStateStorePath") = oEnvironment.Substitute(oEnvironment.Item("UDShare") & "\" & oUtility.ComputerName)
|
|
Else
|
|
oEnvironment.Item("OSDStateStorePath") = oEnvironment.Substitute(oEnvironment.Item("UDShare") & "\" & oEnvironment.Item("UDDir"))
|
|
End if
|
|
Else
|
|
oLogging.CreateEntry "User state capture not possible, no network path (UDShare, UDDir) specified.", LogTypeError
|
|
DetermineOSDStateStorePath = ""
|
|
Exit function
|
|
End if
|
|
Case Else
|
|
If bUseNetwork then
|
|
If oEnvironment.Item("UDShare") <> "" then
|
|
If oEnvironment.Item("UDDir") = "" then
|
|
oEnvironment.Item("OSDStateStorePath") = oEnvironment.Substitute(oEnvironment.Item("UDShare") & "\" & oUtility.ComputerName)
|
|
Else
|
|
oEnvironment.Item("OSDStateStorePath") = oEnvironment.Substitute(oEnvironment.Item("UDShare") & "\" & oEnvironment.Item("UDDir"))
|
|
End if
|
|
Else
|
|
oLogging.CreateEntry "User state capture not possible, no network path (UDShare, UDDir) specified.", LogTypeError
|
|
DetermineOSDStateStorePath = ""
|
|
Exit function
|
|
End if
|
|
Else
|
|
oEnvironment.Item("OSDStateStorePath") = oUtility.StatePath
|
|
End if
|
|
End Select
|
|
|
|
End if
|
|
|
|
|
|
' If a network path is specified, try to connect to it
|
|
|
|
If Left(oEnvironment.Item("OSDStateStorePath"), 2) = "\\" then
|
|
oUtility.ValidateConnection oEnvironment.Item("OSDStateStorePath")
|
|
End if
|
|
|
|
|
|
' If a local path is specified, make sure we still have the right drive letter
|
|
|
|
If Mid(oEnvironment.Item("OSDStateStorePath"), 2, 1) = ":" and sPhase = "RESTORE" then
|
|
oEnvironment.Item("OSDStateStorePath") = GetUserStateDrive(oEnvironment.Item("OSDStateStorePath"))
|
|
End if
|
|
|
|
|
|
' Return the path
|
|
|
|
DetermineOSDStateStorePath = oEnvironment.Item("OSDStateStorePath")
|
|
|
|
End Function
|
|
|
|
|
|
'//---------------------------------------------------------------------------
|
|
'// Function: GetUserStateDrive
|
|
'// Purpose: Perform a state capture or estimate as requested
|
|
'//---------------------------------------------------------------------------
|
|
Function GetUserStateDrive(sStatePath)
|
|
Dim sNewStatePath
|
|
Dim iRetVal, oDrive, oDrives
|
|
|
|
|
|
oLogging.CreateEntry "Starting search for user state at path " & Mid(sStatePath, 3), LogTypeInfo
|
|
|
|
For each oDrive in oFSO.Drives
|
|
|
|
oLogging.CreateEntry "Checking for " & oDrive.Path & Mid(sStatePath, 3), LogTypeInfo
|
|
|
|
If oDrive.IsReady then
|
|
If oFSO.FolderExists(oDrive.Path & Mid(sStatePath, 3)) then
|
|
|
|
' If we can find a hardlink state store that is tagged as the "main" store, we want to use that.
|
|
|
|
If oFSO.FileExists(oDrive.Path & Mid(sStatePath, 3) & "\USMT\hardlink.main") then
|
|
oLogging.CreateEntry "Found primary hardlink folder at " & oDrive.Path & Mid(sStatePath, 3), LogTypeInfo
|
|
GetUserStateDrive = oDrive.Path & Mid(sStatePath, 3)
|
|
Exit Function
|
|
ElseIf oFSO.FolderExists(oDrive.Path & Mid(sStatePath, 3) & "\USMT") or oFSO.FolderExists(oDrive.Path & Mid(sStatePath, 3) & "\USMT3") then
|
|
oLogging.CreateEntry "Found user state at " & oDrive.Path & Mid(sStatePath, 3), LogTypeInfo
|
|
If sNewStatePath = "" then
|
|
sNewStatePath = oDrive.Path & Mid(sStatePath, 3)
|
|
End if
|
|
End if
|
|
|
|
End if
|
|
End if
|
|
|
|
Next
|
|
|
|
|
|
' If no store found, log an error.
|
|
|
|
If sNewStatePath = "" then
|
|
oLogging.CreateEntry "The search for a user state drive failed", LogTypeInfo
|
|
GetUserStateDrive = sStatePath
|
|
Exit Function
|
|
End if
|
|
|
|
|
|
' Return the first state store found
|
|
|
|
oLogging.CreateEntry "State store at " & sNewStatePath & " will be used.", LogTypeInfo
|
|
GetUserStateDrive = sNewStatePath
|
|
|
|
End Function
|
|
|
|
|
|
'//---------------------------------------------------------------------------
|
|
'// Function: VerifyWinPEWinDir
|
|
'// Purpose: Check to see if the OSDWinPEWINDIR path is valid
|
|
'//---------------------------------------------------------------------------
|
|
Function VerifyWinPEWinDir
|
|
|
|
Dim iRetVal
|
|
|
|
If oEnvironment.Item("OSDWinPEWINDIR") = "" Then
|
|
VerifyWinPEWINDIR = Failure
|
|
Exit Function
|
|
Else
|
|
If oFSO.FolderExists(oEnvironment.Item("OSDWinPEWINDIR")) Then
|
|
VerifyWinPEWinDir = Success
|
|
Else
|
|
VerifyWinPEWinDir = Failure
|
|
Exit Function
|
|
End If
|
|
End If
|
|
|
|
End Function
|
|
|
|
|
|
'//---------------------------------------------------------------------------
|
|
'// Function: DetermineWinPEWinDir
|
|
'// Purpose: Find the Windows installation path to be used for offline capture
|
|
'//---------------------------------------------------------------------------
|
|
Function DetermineWinPEWinDir
|
|
Dim iRetVal, oDrive, oDiskPartition, oAssocs, Assoc, winDrive
|
|
|
|
|
|
' If the original disk index and partition were already determined, used those
|
|
' as long as they can be validated.
|
|
|
|
If oEnvironment.Item("OriginalPartitionIdentifier") <> "" then
|
|
|
|
oLogging.CreateEntry "Determining the path to Windows based on Original Disk and Partition configuration.",LogTypeInfo
|
|
|
|
winDrive = oUtility.GetDriveFromTag ( "OriginalPartitionIdentifier" )
|
|
|
|
If winDrive = "" or isEmpty(winDrive) then
|
|
oLogging.CreateEntry "OriginalPartitionIdentifier did not return any drives...", LogTypeInfo
|
|
ElseIf oFSO.FolderExists(WinDrive & "\Windows") Then
|
|
oEnvironment.Item("OSDWinPEWinDir") = winDrive & "\Windows"
|
|
ElseIf oFSO.FolderExists(WinDrive & "\Winnt") Then
|
|
oEnvironment.Item("OSDWinPEWinDir") = winDrive & "\Winnt"
|
|
End If
|
|
|
|
Else ' original drive and partition not set, need to figure the path
|
|
|
|
For each oDrive in oFSO.Drives
|
|
If oDrive.DriveType = 2 then
|
|
If oDrive.IsReady Then
|
|
|
|
' Find the Windows folder
|
|
|
|
If oFSO.FolderExists(oDrive.Path & "\Windows") Then
|
|
oEnvironment.Item("OSDWinPEWinDir") = oDrive.Path & "\Windows"
|
|
Exit For
|
|
ElseIf oFSO.FolderExists(WinDrive & "\Winnt") Then
|
|
oEnvironment.Item("OSDWinPEWinDir") = oDrive.Path & "\Winnt"
|
|
Exit For
|
|
End If
|
|
|
|
End If
|
|
End if
|
|
Next
|
|
|
|
|
|
End if
|
|
|
|
If oEnvironment.Item("OSDWinPEWinDIR") = "" Then
|
|
oLogging.CreateEntry "Unable to locate a Windows installation for offline state capture", LogTypeInfo
|
|
DetermineWinPEWinDIR = Failure
|
|
Else
|
|
oLogging.CreateEntry "User state will be captured from drive " & Left(oEnvironment.Item("OSDWinPEWinDIR"),1), LogTypeInfo
|
|
DetermineWinPEWiNDIR = Success
|
|
End If
|
|
|
|
End Function
|
|
|
|
End Class
|
|
</script>
|
|
</job>
|