Intelligently changing the drive letter assignment for multiple CD/DVD ROM Drives

Diskpart may fail when creating more than one partition when executing the “assign letter=D” command. This is because Windows, WinPE included, by default assigns the first CD-ROM “type” drive to the first available letter after C:. So if you are partitioning a new server, or repartitioning a server that contains only 1 partition (C:), the CD-ROM drive will be assigned to D:. Therefore, if you were creating more than one partition and wanted to assign letter D: to the second partition, it would fail.

The other challenge is that sometimes you may see more than one CD-ROM “type” drive, as booting up using an ISO image is considered to be one, plus the server may actually have a physical one too, which would be assigned to letter E:.

The script below searches all logical disks, locates the CD-ROM “type” drives, and changes their drive letters starting from Z: and working backwards. This ensure that they do not interfere with any further Diskpart operations.

I have used two arrays:

  1. arrReservedDrives – used to reserve drive letters D:, E: and F:. If a CD-ROM “type” drive matches these letters, its drive letter is changed. You can certainly add letters to, or remove letters from this array.
  2. arrReservedLetters – used to reserve any other drive letters, such as network drives, etc. I have reserved I: and S: because those are network drives used by my automated build process.

You could easily extend this script to also manage the drive letters assigned to removable drives, etc.

Enjoy!!!

' This script will change the drive letter of any CD/DVD ROM drive(s) attached to a drive letter that is
' not in use starting from the Z: drive.
' It will also change the letter of any Removable Disks. This is to manage the Virtual Floppy device
' presented from the Dell Remote Access Controller (DRAC), which by default is connected starting from
' the letter C, and will break the Disk partitioning process.

' Release 1.3 Modified by Jeremy@jhouseconsulting.com on 14th June 2010
' Written by Jeremy@jhouseconsulting.com on 30th September 2008.

Option Explicit

Dim blnDebug, arrReservedDrives, strComputer, objWMIService, colDisks, objDisk, strNewDriveLetter, x

blnDebug = False

' Drive C: is always reserved. This array is setup to ensure that we reserve the first 6 drive letters for
' Fixed Disk partitions/volumes and H is typically used as a network home drive.
arrReservedDrives = Array ("C:","D:","E:","F:","G:","H:")

  strComputer = "."
  Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
  Set colDisks = objWMIService.ExecQuery("Select * from Win32_LogicalDisk")
  For Each objDisk in colDisks
    Select Case objDisk.DriveType
      Case 0
        If blnDebug Then wscript.echo objDisk.DeviceID & " is an Unknown device."
      Case 1
        If blnDebug Then wscript.echo objDisk.DeviceID & " has No Root Directory."
      Case 2
        If blnDebug Then wscript.echo objDisk.DeviceID & " is a Removable Disk."
        If instr(1,objDisk.Description,"Removable Disk",1) > 0 Then
          For x=0 to Ubound(arrReservedDrives)
            If StrComp(arrReservedDrives(x),objDisk.DeviceID) = 0 Then
              wscript.echo "There is a " & objDisk.Description & " connected as drive " & objDisk.DeviceID
              strNewDriveLetter=FindAvailableDriveLetter("Descending")
              Call ChangeDriveLetter(objDisk.DeviceID,strNewDriveLetter)
              wscript.echo "Changed the " & objDisk.Description & " from " & objDisk.DeviceID & " to " & strNewDriveLetter
            End If
          Next
        End If
      Case 3
        If blnDebug Then wscript.echo objDisk.DeviceID & " is a Local (Fixed) Disk."
      Case 4
        If blnDebug Then wscript.echo objDisk.DeviceID & " is a Network Drive."
      Case 5
        If blnDebug Then wscript.echo objDisk.DeviceID & " is a CD-ROM or DVD-ROM Drive."
        For x=0 to Ubound(arrReservedDrives)
          If StrComp(arrReservedDrives(x),objDisk.DeviceID) = 0 Then
            wscript.echo "There is a " & objDisk.Description & " connected as drive " & objDisk.DeviceID
            strNewDriveLetter=FindAvailableDriveLetter("Descending")
            Call ChangeDriveLetter(objDisk.DeviceID,strNewDriveLetter)
            wscript.echo "Changed the " & objDisk.Description & " from " & objDisk.DeviceID & " to " & strNewDriveLetter
          End If
        Next
      Case 6
        If blnDebug Then wscript.echo objDisk.DeviceID & " is a RAM Disk."
      Case Else
        If blnDebug Then wscript.echo objDisk.DeviceID & " is an undocumented device type."
    End Select
  Next

Set objWMIService = Nothing
Set colDisks = Nothing

wscript.quit(0)

Sub ChangeDriveLetter(strCurrentLetter,strNewLetter)
  Dim arrDefaultString, WshShell, objFSO, strDiskPartFile, objOutFile, strCommandLine
  Const FOR_WRITING = 2
  arrDefaultString = Array ("select volume ", "remove noerr", "assign letter ", "exit")
  set WshShell = WScript.CreateObject("WScript.Shell")
  Set objFSO = CreateObject("Scripting.FileSystemObject")
  strDiskPartFile = WshShell.ExpandEnvironmentStrings("%TEMP%\Diskpart.txt")
  Set objOutFile = objfso.CreateTextFile(strDiskPartFile, True)
  objOutFile.WriteLine arrDefaultString(0) & strCurrentLetter
  objOutFile.WriteLine arrDefaultString(1)
  objOutFile.WriteLine arrDefaultString(2) & strNewLetter
  objOutFile.WriteLine arrDefaultString(3)
  objOutFile.close
  strCommandLine = "diskpart.exe /S " & chr(34) & strDiskPartFile & chr(34)
  WshShell.Run strCommandLine, 2, true
  set WshShell = Nothing
  Set objFSO = Nothing
  Set objOutFile = Nothing
End Sub

Function FindAvailableDriveLetter(strDirection)

' The purpose of this function is to take the current drive letters, plus any reservations as per arrReservedLetters,
' and return the next available drive letter from either direction as specified. This script is great for changing the
' drive letter of CD/DVD ROM drives, etc.
' Based on an article by the Scripting Guy titled "How Can I Determine the Next Available Drive Letter on a Computer?"
' found: http://www.microsoft.com/technet/scriptcenter/resources/qanda/jan05/hey0122.mspx

' Call this function by either of the two following methods.
' strNewDriveLetter=FindAvailableDriveLetter("Descending")
' strNewDriveLetter=FindAvailableDriveLetter("Ascending")

  Dim arrReservedLetters, objDictionary, strComputer, objWMIService, colDisks, objDisk
  Dim i, strDrive, intStart, intFinish, intStep

  arrReservedLetters = Array("X:","I:","S:")

  Set objDictionary = CreateObject("Scripting.Dictionary")

  strComputer = "."
  Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
  Set colDisks = objWMIService.ExecQuery("Select * from Win32_LogicalDisk")
  For Each objDisk in colDisks
    objDictionary.Add objDisk.DeviceID, objDisk.DeviceID
  Next

' If the reserved drive letter(s) are already in-use, such as network drives already mapped when this script runs, this
' process will fail with the error "This key is already associated with an element of this collection", which is error
' 457, so we need to use an "On Error Resume Next" to tell it to continue on errors.
  For i = 0 to UBound(arrReservedLetters)
    On Error Resume Next
    wscript.echo "Reserving drive letter " & arrReservedLetters(i)
    objDictionary.Add arrReservedLetters(i), arrReservedLetters(i)
    If Err.Number = 457 Then
      wscript.echo "Drive letter " & arrReservedLetters(i) & " has already been reserved because it's already in use."
    ElseIf Err.Number <> 0 Then
      wscript.echo Err.Description
    End If
    On Error Goto 0
  Next

' Note to understand this process 71 ASCII = CHAR G and 90 ASCII = CHAR Z
  Select Case lcase(strDirection)
    Case "descending"
      intStart = 90
      intFinish = 71
      intStep = -1
    Case "ascending"
      intStart = 71
      intFinish = 90
      intStep = 1
  End Select

  For i = intStart to intFinish step intStep
    strDrive = Chr(i) & ":"
    If objDictionary.Exists(strDrive) Then
    Else
'      Wscript.Echo strDrive & " is the next available drive letter."
      FindAvailableDriveLetter = strDrive
      Set objDictionary = Nothing
      Set objWMIService = Nothing
      Set colDisks = Nothing
      Exit Function
    End If
  Next

  Wscript.Echo "There are no available drive letters on this computer."
  Set objDictionary = Nothing
  Set objWMIService = Nothing
  Set colDisks = Nothing
  wscript.quit(0)

End Function
Jeremy Saunders

Jeremy Saunders

Delivering customer success through tech: IT Infrastructure | Citrix | End User Computing | Platform Engineering | DevOps | Full Stack Developer | Technical Architect | Improvisor | Aspiring Comedian | Midlife Adventurer at J House Consulting
Jeremy Saunders is the Problem Terminator; the MacGyver of IT. Views and Intellectual Property (IP) published on this site belong to Jeremy. Please refer to the About page for more information about Jeremy.