ZTISelectBootDisk.wsf

Thought I would post this script, I get asked about it now and then.

MDT works pretty well when there is only one hard disk on the client machine, and it will also work fine if we can assume that the first disk (disk 0) is the correct target for the OS.

The default Task Sequence is setup by default to target the first disk (Disk 0). Administrators can change the Task Sequence to target other disks, say the second disk (Disk 1), but then *all* installations for that task sequence will target the second disk.

The Problem

This model breaks down in more complex environments where there is more than one disk *and* the order of the disk is non-deterministic. Say for example you have a server, one disk is 300GB and is connected to the SATA bus, and another 8 disks are 1TB drives connected to a RAID SAS controller.

The order of the disks (disk 0 through disk 8) could change depending on the load order of the storage drivers. Depending on the timing, Disk 0 could come up first, or could come up last, there is no way to force the disk ordering. So if we want to target a specific disk we need to change MDT.

OSDDiskIndex

The trick to targeting a different disk is within the Default Task Sequence, the “Format and Partition Disk” step is applied against a specific target disk, in this case it targets the first disk (disk 0) by default. The variable used to specify the target disk for the ZTIDiskPart.wsf script is OSDDiskIndex. This variable is stored within the ts.xml Task Sequence configuration file and is only visible to that step in the task sequence. If we were to define OSDDiskIndex as a global variable in a script, it would override the local task sequence definition.

Getting the Disk Index

The trick is to find a consistent and repeatable way to find our target disk, in the server example above we could search for all drives that are less than 500GB. In my environment at home, I use Intel SSD Drives as the OS Drives, and spinning disks for storage, so I could do a search for “Intel SSD*”.

In VBScript we perform a search for disks using a WMI Query. Use Powershell to help prototype your query to get it just right. For example here is a search for all disks whose model starts with “Intel SSD”:

C:\>powershell
Windows PowerShell
Copyright (C) 2012 Microsoft Corporation. All rights reserved.

PS C:\> gwmi win32_diskdrive -filter 'model like "INTEL SSD%"'

Partitions : 3
DeviceID   : \\.\PHYSICALDRIVE0
Model      : INTEL SSDSC2BW240A3L
Size       : 240054796800
Caption    : INTEL SSDSC2BW240A3L

Sample

Here is a sample I wrote to set the OSDDDiskIndex variable based on a WMI Query. You must set the OSBootDiskOverrideWQL variable somewhere, perhaps in the customsettings.ini file.

<job id="ZTISelectBootDisk">
	<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:      ZTISelectBootDisk.wsf
' // 
' // Version:   <VERSION>
' // 
' // Purpose:   Given a collection of Storage Devices on a machine,
' //            this program will assist in finding the correct 
' //            device to be processed by "ZTIDiskPart.wsf"
' // 
' // WARNING:   If there are any *other* disks that need to be Cleaned
' //            and formatted, they should be processed first. 
' //            And this the global Variable OSDDiskIndex should be
' //            set to <blank> when done being processed by ZTIDiskPart.wsf.
' // 
' // Variables: 
' //   OSDDiskIndex           [ Output ]  - Disk Index
' //   OSBootDiskOverrideWQL              - WMI Query Language for Win32_DiskPartition  
' //      " WHERE Size < 100000000000 and InterfaceType = "IDE"  and MediaType != "Removable Media""
' //      " WHERE Model like "INTEL SSD%""
' //
' // Usage: 
' //   cscript.exe [//nologo] ZTISelectBootDisk.wsf [/debug:true]
' //   cscript.exe [//nologo] ZTIDiskPart.wsf [/debug:true]
' //   cscript.exe [//nologo] ZTISetVariable.wsf [/debug:true] /OSDDiskIndex:""
' //
' // ***************************************************************************

Option Explicit
RunNewInstance

'//----------------------------------------------------------------------------
'//  Main Class
'//----------------------------------------------------------------------------

Class ZTISelectBootDisk

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

	Function Main

		Dim oWMIDisk
		Dim bFound
		Dim oDiskPartBoot

		oLogging.CreateEntry "---------------- Initialization ----------------", LogTypeInfo

		IF oEnvironment.Item("DEPLOYMENTTYPE") <> "NEWCOMPUTER" Then
			oLogging.ReportFailure "Not a new computer scenario, exiting Select Boot Disk.", 7700
		End If

		bFound = FAILURE

		''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
		'
		'  1st Pass - Find any disk that matches the Query  "Select * From Win32_diskPartition %OSBootDiskOverrideWQL%"
		'

		If oEnvironment.Item("OSBootDiskOverrideWQL") <> "" then
			oLogging.CreateEntry "Search for: [ " & oEnvironment.Item("OSBootDiskOverrideWQL") & " ] ", LogTypeInfo
			For each oWMIDisk in AllDiskDrivesEx(oEnvironment.Item("OSBootDiskOverrideWQL"))

				'
				' Custom Processing here, add code to help select the OS Boot Disk.
				'    oWMIDisk is Win32_DiskDrive class.
				'

				If bFound = SUCCESS then
					oLogging.CreateEntry "Pass1: There is another disk that meets the Boot Criteria: " & oWMIDisk.Path_, LogTypeInfo
				Else
					oEnvironment.Item("OSDDiskIndex") = oWMIDisk.Index
					bFound = SUCCESS
				End if

			next
		End if

		''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
		'
		'  2nd pass - Use the 1st Partition larger than 15GB on the first disk with a bootable partition.
		'

		If bFound = FAILURE then
			oLogging.CreateEntry "No drive was found using search parameters, Use the 1st \Windows Partition found.", LogTypeInfo

			set oDiskPartBoot = GetBootDriveEx( false, oEnvironment.Item("ImageBuild"), false )
			If not oDiskPartBoot is nothing then 
				oEnvironment.Item("OSDDiskIndex") = oDiskPartBoot.Disk
				bFound = SUCCESS
			End if
		End if

		TestAndLog bFound = SUCCESS, "Verify OSDDiskIndex was found and set: " & oEnvironment.Item("OSDDiskIndex")
		Main = bFound

	End Function

End class

	</script>
</job>
Advertisements

27 thoughts on “ZTISelectBootDisk.wsf

  1. Keith,
    Is there a way to check to see if a disk has data, such as a D: drive? If it does, then do not format it. I (obviously) will always format the C: drive on a new build.

    I’m using it only for server builds, so I don’t have a Refresh TS or anything of the like.

    • That depends…

      If the drives are *encrypted* with bitlocker, then that is not easy to do. Otherwise, sure just enumerate through all the volumes, searching for :\windows\system32\ntoskrnl.exe, 95% of the time that will be your C: drive.

      • I may not have been clear. I think this is a good idea, but here is my problem. We do not have bitlocker on the servers, so that is no issue.
        1. I have a server where the OS has been corrupted, or I need to re-build it to Server 2012 R2, whatever my App team needs.
        2. They want to keep the data on D:, E:, etc.

        When I’m in WinPE, from what I can tell, the disks are offline. I’ve looked at Win32_LogicalDisk (only shows disks with a drive letter), Win32_DiskDrive (only shows total size, not space used), and Win32_DiskPartition (not helpful).

        My only other though would be to put in a TSVariable called DiskFormat, or something to that effect, and add a selection in my custom Task Sequence pane to ask if you want to preserve secondary drives, etc. What are your thoughts?

  2. Pingback: ZTISetTargetDiskFromHint.wsf – Example script to find Disk Target. | Keith's Consulting Blog

  3. Keith,

    I was hoping you could clear things up for me a bit more. I basically want to install the image on the largest drive if there are multiple drives.

    1.The sample that you posted here, I created a new file and pasted that information.

    If oEnvironment.Item(“OSBootDiskOverrideWQL”) “” then
    oLogging.CreateEntry “Search for: [ ” & oEnvironment.Item(“OSBootDiskOverrideWQL”) & ” ] “, LogTypeInfo
    For each oWMIDisk in AllDiskDrivesEx(oEnvironment.Item(“OSBootDiskOverrideWQL”))

    ” WHERE Size > 100000000000″
    ‘ Custom Processing here, add code to help select the OS Boot Disk.

    That was the only section I modified, I’m not sure if thats right.

    2. Where would this new script go in the task sequence.

    3. Under the customsettings.ini I placed the OSBootDiskOverrideWQL=where size> 100000000000 again I’m unsure if this is correct.

    I’m new to this.. hope you can help me out.

    Thanks!

  4. Keith,
    I recently ran into a very similar problem, we use an external USB hard drive with offline media to deploy our image when network connectivity is either to slow or unavailable. Everything worked well until we purchased some new external hard drives and started running into a problem where the OS would get installed on the local hard drive, but the boot files were getting written to the external USB drive, upon reboot the deployment would fail as it was unable to find an operating system to boot to on the local disk. Eventually we discovered that the new disks were being identified as SCSI rather than USB, which MDT interpreted as a valid disk to write too. We resolved the problem by modifying the ZTIDiskUtility script, the isOSReady function.
    isOSReady = g_Win32_DiskDrive.InterfaceType “USB” and g_Win32_DiskDrive.InterfaceType “1394” and g_Win32_DiskDrive.InterfaceType “SCSI”
    This fixed our problem, accept in the case of a device with a SCSI type local drive. Is there a better work around for this that you can suggest?

    • Hi Erik,

      There are a number of different win32_DiskDrive members that you could use to take the removable drive out of the selection process.
      I personally use: g_Win32_DiskDrive.SerialNumber

  5. Please I would like help with your script, but based on the disk model

    i.e. I have a server with 10 disks, currently the FC disks have a lower order (although are technically OFF-LINE at the time of the build), and the HP raid array sits at disk 8
    in the custom.ini under custome properties I have

    OSBootDiskOverrideWQL=” WHERE Model like “HP LOGICAL VOLUME SCSI Disk Device””

    under the tasksequence I have disabled the default format step
    then added the step run script cscript.exe “%scriptroot%\ZTISelectBootDisk.wsf”

    Is there something else i’m missing? or doing wrong

    • I have never seen a disk query like that. ARe you sure that the Win32_diskDrive will return a volume like HP Logical Volume SCSI Disk Device”? Have you tried running this query outside of MDT to verify that it returns a value on these machines?

      • Sure when it’s a raid array, the array manager sets that as the model it’s no different from ” WHERE Model like “INTEL SSD%”” really

        it issue I have is where and how do I call your script inside mdt?
        Do I create a new step? or edit an existing step/script somehow

  6. My client pc have tow drives.One is ssd about 128GB,another is HDD about 1T.
    And the OS should be installed on ssd.
    Is there anythine shoule be rewrote in the ZTISelectBootDisk.wsf
    What should be set to OSBOOTDISKOVERRIDEWQL in the customsettings.ini file.
    Thank you a lot

  7. weill,I fixed the failure problem,because I wrote the command line “ZTISelectBootDisk.vbs” instead of “ZTISelectBootDisk.wsf”.
    Now,there is no failure warning.
    But there is no difference from before when no such a command line :cscript.exe “%scriptroot%\ZTISelectBootDisk.wsf”
    Here is my rules,please check it out:
    [Settings]
    Priority=Default
    Properties=MyCustomProperty

    [Default]
    OSInstall=YES
    SkipAdminPassword=YES
    SkipApplications=YES
    SkipAppsOnUpgrade=YES
    SkipBDDWelcome=YES
    SkipBitLocker=YES
    SkipCapture=YES
    SkipComputerName=YES
    JoinWorkgroup=Tencent
    SkipDomainMembership=YES
    SkipComputerBackup=YES
    SkipDeploymentType=YES
    DeploymentType=NEWCOMPUTER
    SkipFinalSummary=YES
    SkipLocaleSelection=YES
    KeyboardLocale=zh-cn
    UserLocale=zh-cn
    UILanguage=zh-cn
    SkipPackageDisplay=YES
    SkipProductKey=YES
    SkipSummary=YES
    SkipTaskSequence=NO
    SkipTimeZone=YES
    TimeZoneName=China Standard Time
    SkipUserData=Yes
    OSBootDiskOverrideWQL=WHERE Model like %128%
    FinishAction=REBOOT
    ApplyGPOPack=NO

    • When you add a new property to customsettings.ini, don’t forget to add the name to properties:

      [Settings]
      Priority=Default
      Properties=OSBootDiskOverrideWQL

      [Default]
      ...
      OSBootDiskOverrideWQL=WHERE size > 1234567890

      • I did as your said. But the OS still has been installed on HDD(disk0),it’s not on SDD(disk1)
        But there is a difference from before when no selectboot script.The SSD is formatted.And I can see the number 0 changed into 1 during the format proccessing.(Display on screen:Preparing disk 1 partition)

  8. So I assume we just create ZTISelectBootDisk.wsf and drop it in the Scripts folder, but then do you have to create a step in the Task Sequence to run it? And do we need to do anything to the “Format and Partition Disk” task sequence step?

    Thanks

    • RIght copy ZTISelectBootDisk.wsf to the %ScriptRoot% folder (\\server\deploymentshare$\scripts )
      and add a step just *before* the Format ant partition disk step in the “new Computer section of Pre-install.

      • Thankyou keith, I put this in place and it now installs windows to the SSD regardless of whether it is Disk 0 or not. However when the SSD is not Disk 0 it will install Windows to it without the separate 500 MB system partition, it’s just one big partition that is the C drive. Normally MDT partitions the drive with 500 MB for System and then the rest as the C drive.

        Do you know why it might be doing this?

  9. So this is what I have in the custom processing section :

    ‘ 1st Pass – Find any disk that matches the Query “Select * From Win32_diskPartition %OSBootDiskOverrideWQL%”

    If oEnvironment.Item(“OSBootDiskOverrideWQL”) “” then
    oLogging.CreateEntry “Search for: [ ” & oEnvironment.Item(“OSBootDiskOverrideWQL”) & ” ] “, LogTypeInfo
    For each oWMIDisk in AllDiskDrivesEx(oEnvironment.Item(“OSBootDiskOverrideWQL”))

    “”WHERE Model like “HP LOGICAL VOLUME SCSI Disk Device””

    And in my .ini file I have the following;

    Properties=OSBootDiskOverrideWQL

    …………
    …………
    …………
    …………
    OSBootDiskOverrideWQL=””WHERE Model like “HP LOGICAL VOLUME SCSI Disk Device””

    I then set up a new task right before the format but it is still failing. What am I doing wrong or am I missing something? Any help would be great.

Leave a Reply

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

WordPress.com Logo

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

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s