Files

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>