Skip to content
Menu
The Lonely Administrator
  • PowerShell Tips & Tricks
  • Books & Training
  • Essential PowerShell Learning Resources
  • Privacy Policy
  • About Me
The Lonely Administrator

Friday Fun: Get Next Available Drive Letter

Posted on April 6, 2012April 4, 2012

A few days ago I saw on question, I think on Facebook, about using PowerShell to find the next available drive letter that could be used for mapping a network drive. Before I show you my approach, let me state that if you need to map a drive in PowerShell for use only within your PowerShell solution, you don't need a drive letter. You can name the drive anything you want when using New-PSDrive.

Manage and Report Active Directory, Exchange and Microsoft 365 with
ManageEngine ADManager Plus - Download Free Trial

Exclusive offer on ADManager Plus for US and UK regions. Claim now!


new-psdrive Backup -PSProvider Filesystem -Root "\\NAS01\backup"

But of course that won't work if you plan on using the NET USE command. So let's figure out how to determine the next available drive letter. First, let's build an array of possible drive letters. I'm too lazy to type the letters C-Z (skipping A and B for the sake of nostalgia). So I'll "create" them like this:


$letters=[char[]](67..90)

This creates an array of letters C-Z, albeit technically [CHAR] objects, but we're good. Next we need a list of currently used drive letters which we can retrieve from WMI.


$devices=get-wmiobject win32_logicaldisk | select -expand DeviceID

I'm expanding the DeviceID property so $devices is a simple array and not a collection of objects with a DeviceID property. I did that so that I could use the -Contains operator.


$letters | where {$devices -notcontains "$($_):"} | Select -first 1

With this simple command I'm piping the collection of characters to Where-Object, testing if each letter (with the appended 🙂 is NOT found in the array $devices. The letters are already sorted so all I need to do is select the first 1 and that will be the next available drive letter in my current PowerShell session.

Before I let you go today, let me point out one thing: I could combine these two lines into a single PowerShell one-liner.


[char[]](67..90) | Where {(get-wmiobject win32_logicaldisk | select -expand DeviceID) -notcontains "$($_):"} | Select -first 1

I'll get the same result...BUT...just because you can do something in PowerShell doesn't mean you should. This one liner takes almost 2400MS to execute. But when I break it into two lines, as I showed you, I get the end result in 110MS which is a dramatic and noticeable difference. You can test for yourself using Measure-Command. The take-away is to not be afraid to use multiple commands, especially in a script. It may be faster and it will certainly be easier to understand.


Behind the PowerShell Pipeline

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Pocket (Opens in new window) Pocket
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to print (Opens in new window) Print
  • Click to email a link to a friend (Opens in new window) Email

Like this:

Like Loading...

Related

6 thoughts on “Friday Fun: Get Next Available Drive Letter”

  1. walid toumi says:
    April 6, 2012 at 12:23 pm

    Where-Object, testing if each letter

    you can break pipeline like this:


    filter BreakWhere {
    param(
    [ScriptBlock]$FilterScript
    )
    if(&$FilterScript) {
    $_
    } else {
    break
    }
    }

    and this un test:

    PS> 1..10000000 | BreakWhere -FilterScript { $_ -le 5 }
    PS> 1..10000000 | Where -FilterScript { $_ -le 5 }

  2. walid toumi says:
    April 6, 2012 at 1:10 pm

    also you can create a proxy function


    function Select-Object {

    [CmdletBinding(DefaultParameterSetName='DefaultParameter')]
    param(
    [Parameter(ValueFromPipeline=$true)]
    [System.Management.Automation.PSObject]
    ${InputObject},

    [Parameter(ParameterSetName='DefaultParameter', Position=0)]
    [System.Object[]]
    ${Property},

    [Parameter(ParameterSetName='DefaultParameter')]
    [System.String[]]
    ${ExcludeProperty},

    [Parameter(ParameterSetName='DefaultParameter')]
    [System.String]
    ${ExpandProperty},

    [Switch]
    ${Unique},

    [Parameter(ParameterSetName='DefaultParameter')]
    [ValidateRange(0, 2147483647)]
    [System.Int32]
    ${Last},

    [Parameter(ParameterSetName='DefaultParameter')]
    [ValidateRange(0, 2147483647)]
    [System.Int32]
    ${First},

    [Parameter(ParameterSetName='DefaultParameter')]
    [ValidateRange(0, 2147483647)]
    [System.Int32]
    ${Skip},

    [Parameter(ParameterSetName='IndexParameter')]
    [ValidateRange(0, 2147483647)]
    [System.Int32[]]
    ${Index})

    begin
    {
    try {
    $outBuffer = $null
    if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
    {
    $PSBoundParameters['OutBuffer'] = 1
    }
    $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Select-Object', [System.Management.Automation.CommandTypes]::Cmdlet)
    $scriptCmd = {& $wrappedCmd @PSBoundParameters }
    $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
    $steppablePipeline.Begin($PSCmdlet)
    } catch {
    throw
    }
    }

    process
    {
    try {
    if($First -and $_ -le $First) {
    $steppablePipeline.Process($_)
    } else {
    break
    }
    } catch {
    throw
    }
    }

    end
    {
    try {
    $steppablePipeline.End()
    } catch {
    throw
    }
    }
    <# .ForwardHelpTargetName Select-Object .ForwardHelpCategory Cmdlet #>

    }

    test:


    1..1mb | Select-Object -First 1
    1..1mb | Microsoft.PowerShell.Utility\Select-Object -First 1

  3. Chris Savard says:
    April 6, 2012 at 2:05 pm

    Even slower one-liner, but with mapped drives too…

    [char[]](65..90) | % { If ((gwmi Win32_LogicalDisk).DeviceID -contains “${_}:” -or (gwmi Win32_MappedLogicalDisk).DeviceID -contains “${_}:”) { “${_}: YUP!” } Else { “${_}: NOPE!” } }

    1. Jeffery Hicks says:
      April 6, 2012 at 3:19 pm

      Mapped drives will also show with win32_logicaldisk.

    2. Jeffery Hicks says:
      April 6, 2012 at 3:30 pm

      Now that I go back and run the code it won’t work because WMI is going to return a collection of objects. Even on Windows 7 I get all “Nope”.

  4. walid toumi says:
    April 6, 2012 at 3:11 pm

    @Chris Savard,

    don’t work in my XP:

    A: NOPE!
    B: NOPE!
    C: NOPE!
    D: NOPE!
    E: NOPE!
    F: NOPE!
    G: NOPE!
    H: NOPE!
    I: NOPE!
    J: NOPE!
    K: NOPE!
    L: NOPE!
    M: NOPE!
    N: NOPE!
    O: NOPE!
    P: NOPE!
    Q: NOPE!
    R: NOPE!
    S: NOPE!
    T: NOPE!
    U: NOPE!
    V: NOPE!
    W: NOPE!
    X: NOPE!
    Y: NOPE!
    Z: NOPE!

Comments are closed.

reports

Powered by Buttondown.

Join me on Mastodon

The PowerShell Practice Primer
Learn PowerShell in a Month of Lunches Fourth edition


Get More PowerShell Books

Other Online Content

github



PluralSightAuthor

Active Directory ADSI Automation Backup Books CIM CLI conferences console Friday Fun FridayFun Function functions Get-WMIObject GitHub hashtable HTML Hyper-V Iron Scripter ISE Measure-Object module modules MrRoboto new-object objects Out-Gridview Pipeline PowerShell PowerShell ISE Profile prompt Registry Regular Expressions remoting SAPIEN ScriptBlock Scripting Techmentor Training VBScript WMI WPF Write-Host xml

©2025 The Lonely Administrator | Powered by SuperbThemes!
%d