How to get list of drive letters in Powershell 2.0 - powershell

I'm trying to get a list of drive letters in a drop-down menu. I'm currently using the code below and it works just fine in Windows 10, but doesn't work at all in Windows 7.
$Drive_Letters = Get-WmiObject Win32_LogicalDisk
ForEach ($Drives in $Drive_Letters.DeviceID) { $Dest_Drive_Box.Items.Add($Drives) }
In Win 7 I tried adjusting the code to this...
$Drive_Letters = Get-WmiObject Win32_LogicalDisk | Select-Object DeviceID
ForEach ($Drives in $Drive_Letters) { $Dest_Drive_Box.Items.Add($Drives) }
But now it shows "#DeviceID=C:}", "#DeviceID=D:}", etc. in Win 7 and 10 for each drive letter. I need to just show "C:", "D:", etc.
Thanks!

Get-PSDrive
This will return all drives mapped in the current session. The Name property contains the drive letter.
To capture just drive letters:
(Get-PSDrive).Name -match '^[a-z]$'
Tested working in PSv2:
Get-PSDrive | Select-Object -ExpandProperty 'Name' | Select-String -Pattern '^[a-z]$'

$drives = (Get-PSDrive -PSProvider FileSystem).Root
returns an array for drives with the root path:
C:\
D:\
E:\
You can easily trim the ending if you don't want it.

$Drives = Get-WmiObject Win32_Logicaldisk | % {$_.DeviceId}
$Drives | % {$Dest_Drive_Box.Items.Add($_)}

It would appear that each item $drives is a HashTable with one Key-Value Pair DeviceID = driveletter a quick test shows that using $Drives.DeviceID returns just the value of the key-value pair.
ForEach ($Drives in $Drive_Letters) { $Dest_Drive_Box.Items.Add($Drives.DeviceID) }

Related

PowerShell ForEach-Object within Pipeline

I'm just learning powershell and trying to understand how looping works on ForEach Object. I was able to make this script work that detect USB Storage attached to a device
Get-CimInstance -ClassName Win32_DiskDrive |
where {$_.InterfaceType -eq 'USB'} |
ForEach-Object{"`n`n",$_ } |
Format-list -Property DeviceId,Model,Size
Output:
DeviceId : \\.\PHYSICALDRIVE1Model : WD My Passport 0740 USB DeviceSize : 1000169372160
DeviceId : \\.\PHYSICALDRIVE2Model : TOSHIBA TransMemory USB DeviceSize : 7748213760
However I'm having hardtime targeting the value of each to move it to the next line. the result should be something like this
If I ran the script in Powershell console by using format-list it display perfect however on a webpage it won't display accordingly. How can I use the backtick (`n) so that the result of DeviceID, Model and Size will be on a separate line.
I will appreciate any help. thank you guys
Please use select-object instead of For-each object
Get-CimInstance -ClassName Win32_DiskDrive | where{$.InterfaceType -eq 'USB'} |Select-object -Property DeviceId,Model,Size
#You can filter at CIM level, no need to do it at shell level, also you can specify the list of properties to retrieve
$data = Get-CimInstance -query "Select DeviceId,Model,Size from Win32_DiskDrive where InterfaceType='usb'"
#If you want a string with the format: [PropertyName]:[PropertyValue]`n[PropertyName]:[PropertyValue]...
$stringArray = #(
$data | %{
"DeviceId: $($_.DeviceId)`nModel: $($_.Model)`nSize: $($_.Size)"
}
)
Output ($stringArray):
DeviceId: \\.\PHYSICALDRIVE1
Model: Generic USB Flash Disk USB Device
Size: 15512878080
#Maybe convertto-html is of use for you?
$data | ConvertTo-Html

Get mapped network drives labels

Is there a way to get mapped network drives labels?
I know it's possible to get multiple properties through the
Get-Object Win32_MappedLogicalDisk
But none of them are labels (please do not misunderstand, I do not want Name i.e. K:, I want labels i.e. My Network drive)
You could use the Com Shell.Application object for this:
$shell = New-Object -ComObject Shell.Application
(Get-WmiObject -Class Win32_MappedLogicalDisk).DeviceID |
# or (Get-CimInstance -ClassName Win32_MappedLogicalDisk).DeviceID |
# or ([System.IO.DriveInfo]::GetDrives() | Where-Object { $_.DriveType -eq 'Network' }).Name |
Select-Object #{Name = 'Drive'; Expression = {$_}},
#{Name = 'Label'; Expression = {$shell.NameSpace("$_").Self.Name.Split("(")[0].Trim()}}
# when done, clear the com object from memory
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
Output:
Drive Label
----- -----
X: MyCode
Some explanation for the above:
Using the COM object Shell.Application, you can drill down through its properties and methods.
.NameSpace create and return a Folder object for the specified folder
.Self gets a Read-Only duplicate System.Shell.Folder object
.Name from that we take the Name property like 'MyCode (X:)'
.Split this name we split on the opening bracket '(',
[0] take the first part of the splitted name and
.Trim() get rid of any extraneous whitespace characters
Another way is to go into the registry, but remember that after a mapped network folder is unmapped, the old registry value remains.
This is why below code still uses one of two methods to find active network mappings first:
# the registry key to search in
$regKey = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2'
# list the mapped network drives and loop through
# you can also use Get-CimInstance -ClassName Win32_MappedLogicalDisk
Get-WmiObject -Class Win32_MappedLogicalDisk | ForEach-Object {
# create the full registry key by replacing the backslashes in the network path with hash-symbols
$key = Join-Path -Path $regKey -ChildPath ($_.ProviderName -replace '\\', '#')
# return an object with the drive name (like 'X:') and the Label the user gave it
[PsCustomObject]#{
Drive = $_.DeviceID
Label = Get-ItemPropertyValue -Path $key -Name '_LabelFromReg' -ErrorAction SilentlyContinue
}
}
Output here also:
Drive Label
----- -----
X: MyCode
I am not aware of a cmdlet that will give you that info. I believe you can find it by looking at the registry with gci, but you would need to cleanup the output.
get-childitem "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2"

DISKPART vs PowerShell Get-Volume

In DISKPART.EXE, I get the info when multiple mount points are assigned to a volume. In the picture we see that Drive G: can also be accessed using D:\SQL\MSSQL13.MSSQLSERVER\DATA\ or D:\BlaBla:
But I can't find the same info using PowerShell's Get-Volume or Get-WMIObject -Class Win32_Volume. Does anyone knows how to extract this info using a native PowerShell function?
I thought of extracting the info by calling DISKPART.EXE inside PowerShell but I would prefer an native PowerShell function like Get-Volume.
Perhaps surprisingly, you can look up mount points via the Win32_MountPoint class:
Get-WmiObject Win32_MountPoint | Select-Object Directory, Volume
Further details can be obtained by looking up the references:
Get-WmiObject Win32_MountPoint | ForEach-Object {
$dir = [wmi]$_.Directory | Select-Object -Expand Name
$vol = [wmi]$_.Volume
New-Object -Type PSObject -Property #{
Directory = $dir
Label = $vol.Label
DriveLetter = $vol.DriveLetter
FileSystem = $vol.FileSystem
DeviceId = $vol.DeviceId
}
}

powershell how to remove `{}#` from output. Is there a special command to do it?

I entered gwmi win32_product | select -property name | select -first 1 and output to a file. My result was #{name=Google Talk Plugin}.
How can I get rid of #{}, and name. I only want it to show Google Talk Plugin?
#{} means your exporting an object with properties. Try the -ExpandProperty parameter in Select-Object. You could also combine both select-object commands, like:
gwmi win32_product | select -expandproperty name -first 1
I ran into a problem similar with
$drive = Get-WmiObject Win32_LogicalDisk -ComputerName $servername | Select-Object DeviceID
$drive comes up as #{DeviceID=C:}, #{DeviceID=D:}, ...
Here is my brute force hack at it.
The second Trim statement was because for some reason if I put it in the first Trim it starts to Trim the letters in the Drive =D: becomes :
enter code here
$Asdrive = #() #declared before to get rid of null pointer issue, also to tell PS this is an array not a string
#Write-Host "Trimming for Get-WmiObject"
for($i=0;$i -lt $drive.length; $i++) {
[string]$sdrive = $drive[$i]
[string]$sdrive1 = $sdrive.Trim("#","{","}","D","e","v","i","c","e","I","D")
[string]$sdrive2 = $sdrive1.Trim("=")
$Asdrive += $sdrive2
}
If you're running at least Version 3, you can also use the member enumeration feature and then array slicing to take the first one, instead of using select:
(gwmi win32_product).name[0]
I add some code as I found this question with google.
Frode F. solution is the best one.
If you write out something like:
Get-ADComputer -Filter * -SearchBase $OU | Select-Object Name
you get a proper List of all Computers in an OU. You can also pipe that to a CVS/HTML file and its still nice.
| Export-CSV "mylist.csv"
But if you store it into a variable (array) every name will be wrapped in #{}.
In my case I needed computer names in a variable. Here is the solution thanks to Frodo:
$computerList = Get-ADComputer -Filter * -SearchBase $OU | Select-Object -ExpandProperty Name
Hope it helps someone.
(would add it as comment under the right solution, but I don't have enough reputation to do so)

Getting a free drive letter

I saw the Get-NextFreeDrive function in this answer and I wondered if there was a more efficient way to do this. It appears that the function in the linked answer keeps going through all the letters even if it has already found a free drive letter.
At PowerShell Magazine, we ran a brain teaser contest to find out the shortest answer to your question. Check this:
http://www.powershellmagazine.com/2012/01/12/find-an-unused-drive-letter/
There are several answers but here is my fav one:
ls function:[d-z]: -n | ?{ !(test-path $_) } | random
My two cents:
get-wmiobject win32_logicaldisk | select -expand DeviceID -Last 1 |
% { [char]([int][char]$_[0] + 1) + $_[1] }
Range of valid [CHAR] is 68..90, adding a check if [char]$_[0] -gt 90 avoid unexpected results.
In case some unit is a mapped network drive it return always the major successive, ex.:
c: system drive
d: cd/dvd
r: network mapped drive
the command return s: and not e: as [string]
This give the first free drive letter ( a little ugly.. someone can do it better IMO):
$l = get-wmiobject win32_logicaldisk | select -expand DeviceID | % { $_[0] }
$s = [int][char]$l[0]
foreach ( $let in $l )
{
if ([int][char]$let -ne $s)
{
$ret = [char]$s +":"
break
}
$s+=1
}
$ret
I like this way, for the following reasons:
It doesn't require WMI, just regular powershell cmdlets
It is very clear and easy to read
It easily allows you to exclude specific driveletters
It easily allows you to order the driveletters in any order you would like
It finds the first non used driveletter and maps it, and then it is finished.
$share="\\Server\Share"
$drvlist=(Get-PSDrive -PSProvider filesystem).Name
Foreach ($drvletter in "DEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray()) {
If ($drvlist -notcontains $drvletter) {
$drv=New-PSDrive -PSProvider filesystem -Name $drvletter -Root $share
break
}
}
Here's what I came up with. I need the last available drive letter from A to Z.
$AllLetters = 65..90 | ForEach-Object {[char]$_ + ":"}
$UsedLetters = get-wmiobject win32_logicaldisk | select -expand deviceid
$FreeLetters = $AllLetters | Where-Object {$UsedLetters -notcontains $_}
$FreeLetters | select-object -last 1
This gets an array of letters A..Z
Then gets an array of the letters already in use from WMI
Next produces an array of letters not in use using the comparison operator -notcontains
Finally outputs a single letter.
$taken = Get-WmiObject Win32_LogicalDisk | Select -expand DeviceID
$letter = 65..90 | ForEach-Object{ [char]$_ + ":" }
(Compare-Object -ReferenceObject $letter -DifferenceObject $taken)[1].InputObject
Just for fun to shave an extra line of code (lol). If you wanted to be cloppy as heck you could skip instantiating variables and just pipe those directly into -Ref and -Diff directly, probably ought to be slapped for doing that though. :)
Selects [1] to avoid getting the A: drive just in case that might complicate matters.
I had to Write a function that works with Powershell V2.0.
The following Function will Return the next available letter, it also can get an exclude letter as parameter:
Function AvailableDriveLetter ()
{
param ([char]$ExcludedLetter)
$Letter = [int][char]'C'
$i = #()
#getting all the used Drive letters reported by the Operating System
$(Get-PSDrive -PSProvider filesystem) | %{$i += $_.name}
#Adding the excluded letter
$i+=$ExcludedLetter
while($i -contains $([char]$Letter)){$Letter++}
Return $([char]$Letter)
}
Let's say Your OS reports drive-letters C:,E:,F: and G: as being used.
Running: $First = AvailableDriveLetter ,Will result in $First containing 'D'
Running: $Sec = AvailableDriveLetter -ExcludedLetter $First ,Will result in $Sec containing 'H'
Another way...
$DriveList = Get-PSDrive -PSProvider filesystem | foreach({($_.Root).Replace(":\","")})
$AllDrives = [char[]]([int][char]'E'..[int][char]'Z')
$NextDriveLetter = ($AllDrives | Where-Object { $DriveList -notcontains $_ } | Select-Object -First 1) + ":"
I found to my own cost that the currently accepted answer (ls function:[d-z]: -n | ?{ !(test-path $_) } | random) can indeed return things like CD drives.
I've made this one to exclude any local drives from the array:
"$([char[]]([char]'D'..[char]'Z')|Where-Object {((Get-WmiObject -Class Win32_LogicalDisk).DeviceID).replace(':','') -notcontains $_ }|Select-Object -first 1):"
It will return the first available letter. If you'd prefer the last available letter just change Select-Object -first 1 to Select-Object -last 1
I found out that Test-Path evaluates my empty CD-Drive as False, here is another alternative that will compare every letter in the alphabeth until it finds one that doesn't exist in filesystem, then returns that drive as output.
$DriveLetter = [int][char]'C'
WHILE((Get-PSDrive -PSProvider filesystem).Name -contains [char]$DriveLetter){$DriveLetter++}
Write-Host "$([char]$Driveletter):"
just going to add one that works for remote drive letters
$computer will be the input and $driveletter would contain the next available drive on the remote computer
67..90 | foreach {if(((GWmi win32_logicaldisk -computer $computer -Property DeviceID).deviceID).Substring(0,1) -notcontains [char]$_){$driveLetter = [char]$_; break}}
might be able to shorten that but at that length its clear to see whats going on
This seems a bit like a "me too" answer, but I noticed that all the other answers use -contains or -notcontains and I just didn't like those solutions. So this may not be terribly efficient, but I like it better. The purpose of this code (for me) was to find the first drive that I can use to create a drive mapping.
$FreeDrive=Get-PSDrive -PSProvider FileSystem | Select-Object -ExpandProperty Name | Where-Object { ($_ -ne "A") -and ($_ -ne "B") -and ($_ -ne "C") } | ForEach-Object { [System.Convert]::ToByte([System.Convert]::ToChar($_)) }
$FreeDrive=#($FreeDrive)
if (($FreeDrive.Count -eq 1) -and ($FreeDrive[0] -ne "Z")) { $FreeDrive=[System.Convert]::ToChar($FreeDrive[0]+1) }
$j=0
while ((($FreeDrive[$j]+1) -eq $FreeDrive[$j+1]) -and ($j -lt ($FreeDrive.Count-1))) { $j++ }
$FreeDrive=[System.Convert]::ToChar($FreeDrive[$j]+1)
$FreeDrive