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>
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?
Pingback: ZTISetTargetDiskFromHint.wsf – Example script to find Disk Target. | Keith's Consulting Blog
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!
Yea, If you are looking for the “largest” disk, then we are probably talking about a new script to perform the search. The current script won’t handle that.
In the VBScript what do I place in the custom Code setting of the script? Can you give me and example of what it should look like?
Thanks, Examples are listed in the comments section at the top of the script.
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
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
You add the step just before the ZTIDiskPart.wsf “Format and Partition” step under “Pre-install->NewComputer”
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
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)
which one should I choose in the Installation Operating system step
I run a test.
The OS still installed on hdd.
I’m confused
I choose “Specific disk and partition(disk 0,partition 1)” as the way of installion OS.
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?
Will this still work in MDT 2013 Update 2?
Yes.
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.