Several years ago I wrote a script to help select which disk to deploy Windows to during your MDT LiteTouch or ZeroTouch task sequence.
https://keithga.wordpress.com/2013/09/18/ztiselectbootdisk-wsf/
Well, based on a request from my latest client, I have created a similar script that support BusType.
BackGround
My client is trying to install Windows Server 2016 on a Server with a SAN. When the machine boots to WinPE, one of the SAN drives appears *first* as Disk 0 (Zero). By default MDT Task Sequences will deploy to Disk Zero! My ZTISelectBootDisk.wsf already shows how to override. All we need to do is to find a way to tell MDT which disk to choose based on the correct WMI query.
Turns out it was harder than I thought.
What we wanted was the BusType that appears in the “Type” field when you type “Select Disk X” and then “detail disk” in Diskpart.exe. When we ran “Detail Disk” in DIskpart.exe we could see the bus type: Fibre as compared to regular disks like SCSI or SAS.
The challenge was that the regular Win32_diskDrive WMI query wasn’t returning the BusType value, and we couldn’t figure out how to get that data through other queries.
I tried running some PowerShell queries like ‘Get-Disk’ and noticed that the output type was MSFT_Disk, from a weird WMI Namespace: root\microsoft\windows\storage. But adding that query to the script works! Yea!!!
BusType
What kind of BusTypes are there?
Name | Value | Meaning |
Unknown | 0 | The bus type is unknown. |
SCSI | 1 | SCSI |
ATAPI | 2 | ATAPI |
ATA | 3 | ATA |
1394 | 4 | IEEE 1394 |
SSA | 5 | SSA |
Fibre Channel | 6 | Fibre Channel |
USB | 7 | USB |
RAID | 8 | RAID |
iSCSI | 9 | iSCSI |
SAS | 10 | Serial Attached SCSI (SAS) |
SATA | 11 | Serial ATA (SATA) |
SD | 12 | Secure Digital (SD) |
MMC | 13 | Multimedia Card (MMC) |
Virtual | 14 | This value is reserved for system use. |
File Backed Virtual | 15 | File-Backed Virtual |
Storage Spaces | 16 | Storage spaces |
NVMe | 17 | NVMe |
For this script we are *excluding* the following devices:
Name | Value | Meaning |
Fibre Channel | 6 | Fibre Channel |
iSCSI | 9 | iSCSI |
Storage Spaces | 16 | Storage spaces |
NVMe | 17 | NVMe |
Meaning that the *FIRST* fixed device not in this list will become the new *Target* OS Disk. Run this query on your machine to see what disk will become the target:
gwmi -namespace root\microsoft\windows\storage -query 'select Number,Size,BusType,Model from MSFT_Disk where BusType <> 6 and BusTy pe <> 9 and BusType <> 16 and BusType <> 17' | Select -first 1
Requirements
Reminder that this script requires MDT (latest), and the script should be placed in the %DeploymentShare%\Scripts folder. Additionally you should install all the Storage packages for WinPE, sorry I don’t recall *which* packages I selected when I did testing.
Script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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" | |
' // | |
' // Currently hard coded to select the *FIRST* drive that is | |
' // Not iSCSI, Fibre Channel, Storage Spaces, nor NVMe. | |
' // | |
' // REQUIRES that you install the correct WinPE Storage Components! | |
' // | |
' // | |
' // 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 | |
' // | |
' // 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 | |
Dim oContext, oLocator, objQuery, objStorageWMI, objStorage | |
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%" | |
' | |
Set oContext = CreateObject("WbemScripting.SWbemNamedValueSet") | |
oContext.Add "__ProviderArchitecture", 64 | |
Set oLocator = CreateObject("Wbemscripting.SWbemLocator") | |
set objStorageWMI = oLocator.ConnectServer("","root\Microsoft\Windows\Storage","","",,,,oContext) | |
set objQuery = objStorageWMI.ExecQuery("select Number,Size,BusType,Model from MSFT_Disk where BusType <> 6 and BusType <> 9 and BusType <> 16 and BusType <> 17") | |
If objQuery.Count = 0 then | |
oLogging.CreateEntry "No Disk Drives Found!?!?! Dude, did you install the right storage drivers into WinPE 0x7b.",LogTypeError | |
exit function | |
End if | |
For each objStorage in objQuery | |
oLogging.CreateEntry "Found Device: N:" & ObjStorage.Number & " S:" & ObjStorage.Size & " M:" & ObjStorage.Model & " T:" & ObjStorage.BusType & " " , LogTypeInfo | |
oEnvironment.Item("OSDDiskIndex") = ObjStorage.Number | |
bFound = SUCCESS | |
exit for | |
Next | |
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' | |
' | |
' 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> |
-k