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

Independent Consultant | Contractor | Microsoft & Citrix Specialist | Desktop Virtualization Specialist at J House Consulting
Jeremy is a highly respected, IT Professional, with over 30 years’ experience in the industry. He is an independent IT consultant providing expertise to enterprise, corporate, higher education and government clients. His skill set, high ethical standards, integrity, morals and attention to detail, coupled with his friendly nature and exceptional design and problem solving skills, makes him one of the most highly respected and sought after Microsoft and Citrix technical resources in Australia. His alignment with industry and vendor best practices puts him amongst the leaders of his field.
Jeremy Saunders
Jeremy Saunders
Jeremy Saunders
  • jamal

    Hi Jeremy,

    Brilliant script, its worked very well. Would it be possible if you could add removable drives section as well please? Sorry I don’t have any scripting skills.

    Thanks
    Jamal

    • Jeremy

      Hi Jamal,

      Thanks for your comments. The original script I’d posted is a bit old and outdated. I’ve updated it now, which give you what you need.

      Cheers,
      Jeremy.

  • Hi Jeremy,

    Thanks for the update.
    I have updated the script for my needs (c:, d:, e: applied to cd-rom and r:, s:, t:, u:, v: applied to removable drive).

    It is successful on Windows XP but doesn’t work on windows 7 machines.

    Can you advice please?

    Cheers
    Jamal

  • I have manually changed drive letters which out my list and run the script and its worked (Windows 7).
    It may need some reset or what don’t know.

    Cheers
    Jamal

Previous post:

Next post: