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

by Jeremy Saunders on October 19, 2008

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

Technical Architect | DevOps Evangelist | Software Developer | Microsoft, NVIDIA, Citrix and Desktop Virtualisation (VDI) Specialist/Expert | Rapper | Improvisor | Comedian | Property Investor | Kayaking enthusiast at J House Consulting
Jeremy Saunders is the Problem Terminator. He is a highly respected IT Professional with over 35 years’ experience in the industry. Using his exceptional design and problem solving skills with precise methodologies applied at both technical and business levels he is always focused on achieving the best business outcomes. He worked as an independent consultant until September 2017, when he took up a full time role at BHP, one of the largest and most innovative global mining companies. With a diverse skill set, high ethical standards, and attention to detail, coupled with a friendly nature and great sense of humour, Jeremy aligns to industry and vendor best practices, which puts him amongst the leaders of his field. He is intensely passionate about solving technology problems for his organisation, their customers and the tech community, to improve the user experience, reliability and operational support. Views and IP shared on this site belong to Jeremy.
Jeremy Saunders
Jeremy Saunders

Previous post:

Next post: