Sort Drives by Free Space in Powershell - powershell

I'm trying to sort my drives using Powershell by free space. The purpose of this utility is to re-distribute one disk that is almost full across all the disks that are not as full.
Currently, my powershell script looks like this:
# Retrieves drives from My Computer
$Drives = gdr -PSProvider FileSystem
# Iterates over all drives found
foreach($Drive in $Drives) {
$Drive | Sort-Object -Property Free
}
The output is this:
Name Used (GB) Free (GB) Provider Root CurrentLocation
---- --------- --------- -------- ---- ---------------
C 151.09 688.31 FileSystem C:\
D 0.05 15.67 FileSystem D:\
F 0.09 2.96 FileSystem F:\
G 0.05 16.43 FileSystem G:\
H 0.03 9.73 FileSystem H:\
I 2.42 65.94 FileSystem I:\
But I'm expecting something like the Free (GB) Column to be sorted ASC
Any clues?

The problem was that I sorting the individual drive and not the collection as pointed out by #johnrsharpe and #Lee_Dailey
This ended up solving my problem:
# Iterates over all drives found
$SortedDrives = $Drives | Sort-Object -Property Used -Descending
foreach ($Drive in $SortedDrives) {
Write-Output $Drive.Name
}

Related

How to remove mapped network drive when created with a random letter

I am creating a mapped network drive with a random letter like this:
$drive = Get-ChildItem function:[f-z]: -n | Where { !(Test-Path $_) } | select -First 1
$PsDrive = New-PSDrive -Name ($drive)[0] -PSProvider "FileSystem" -Root $somePath
This code works fine. I have a problem when want to remove the newly created mapped drive at the end of my script. How I do it:
$someEnv = Remove-PSDrive -Name $drive
The problem here is that $drive adds the colon after the name ':'. For example if $drive is called M, it will be "M:" and Remove-PsDrive fails.
Probably it will be removed automatically when the session is over, but I want to remove it explicitly.
Do you guys have an idea how I can remove this drive?
Thanks
You can remove the ':' from the $drive variable using Replace
$drive.Replace(':','')
So your code to remove the drive looks like this
$someEnv = Remove-PSDrive -Name $drive.Replace(':','')
here is another, somewhat different way to do it that uses just powershell. [grin]
what it does ...
creates a list of possible drive letters
in this case, it is just F thru Z.
== uses 'F'[0] to convert the letter string to a character
== uses the range operator to coerce the chars to ascii code numbers
== generates a range from those code numbers
== coerces the code numbers back to characters & stuffs them into an array
in PoSh7, you can make that array with 'F'..'Z'. [grin]
grabs the in-use drive letters
filters the candidate letters against the in-use letters
grabs the 1st remaining letter
sets a target root
in this case it is the temp dir.
creates a new PSDrive with that letter
shows the drive letter in use by that new PSDrive
gets a dir list of the 1st three items in that PSDrive
removes the PSDrive
shows the current filesystem PSDrives
the code ...
$CandidateDriveLetters = [char[]]('F'[0]..'Z'[0])
$InUseDriveLetters = (Get-PSDrive -PSProvider FileSystem).Name
$1stAvailableDL = ($CandidateDriveLetters |
Where-Object {
$_ -notin $InUseDriveLetters
})[0]
$TargetDriveRoot = $env:TEMP
$NewDrive = New-PSDrive -Name $1stAvailableDL -PSProvider FileSystem -Root $TargetDriveRoot
$NewDrive.Name
'=' * 30
Get-ChildItem -Path ('{0}:' -f $NewDrive.Name) |
Select-Object -First 3
'=' * 30
Remove-PSDrive -Name $NewDrive.Name
(Get-PSDrive -PSProvider FileSystem).Name
output ...
H
==============================
Directory: C:\Temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2020-04-21 6:39 PM 2ccdytd4
d----- 2020-04-21 6:39 PM 30u23uyw
d----- 2020-04-21 6:39 PM 55zoq3fj
==============================
C
D
E
F
G
R
S
Z

Powershell Where-Object Match

I want to use the Where-Object to limit the Output of Get-PSDrive to only network shares.
Get-PSDrive shows me the following:
Name Used (GB) Free (GB) Provider Root CurrentLocation
---- --------- --------- -------- ---- ---------------
A FileSystem A:\
Alias Alias
C 16.19 43.47 FileSystem C:\ Users\HansB\Documents
Cert Certificate \
D FileSystem D:\
Env Environment
Function Function
HKCU Registry HKEY_CURRENT_USER
HKLM Registry HKEY_LOCAL_MACHINE
V 451.39 159.76 FileSystem \\192.168.71.31\fs_log_target
Variable Variable
W 197.72 FileSystem \\192.168.71.32\perf200
WSMan WSMan
X 197.72 FileSystem \\192.168.71.32\perf100
Y 271.52 34.33 FileSystem \\192.168.71.30\group200
Z 271.52 34.33 FileSystem \\192.168.71.30\group100
Then I want to get the \\192.168.71.30\group100 Network Share:
Get-PSDrive | Where-Object { $_.Root -match "\\\\192.168.71.30\\group100" }
But I get nothing, why does -match not work?
Use DisplayRoot instead of Root property
Get-PSDrive | Where-Object { $_.DisplayRoot -match "\\\\192.168.71.30\\group100" }
try to run
Get-PSDrive | Where-Object { $_.DisplayRoot -match "\\\\192.168.71.30\\group100" } | select *
Root will be your mapped drive letter, and DisplayRoot your UNC Path
EDIT: as a side note. For escaping regex use [regex]::Escape() method.
PS > [regex]::Escape("\\192.168.71.30\group100")
\\\\192\.168\.71\.30\\group100
Try using -eq instead:
Get-PSDrive | Where-Object {$_.Root -eq "\\192.168.71.30\group100"}
If yoy type Get-PSDrive | select root you will notice that for network drives it will print nothing. So there is something strange with network share roots.
Get-PSDrive | select root | %{write-host $_.Root.GetType()} will show that it's a system string, so not quite sure why it seems to be empty for network drives.

How to Get Actual Hard Disk Memory of A VM?

Is it possible to get the actual hard disk usage of a virtual machine?
Measure-VM -Name * | select-object -property TotalDiskAllocation
The TotalDiskAllocation property gets the total disk space assigned to the VM and even though this is helpful I also need to know how much is really being used.
For example, if a VM has 150 GB of allocated memory and it only uses 50 GB, is there any Powershell command that will be able to extract the 50 GB? If yes, how will I be able to incorporate that script to the script above?
Based on your attempt to use Measure-VM, I assumed you are using Hyper-V. I use something similar to this in one of my Hyper-V scripts:
(Get-VM dechiro1).HardDrives | ForEach {
$GetVhd = Get-VHD -Path $_.Path
[pscustomobject]#{
Name = $_.Name
Type = $GetVhd.VhdType
ProvisionedGB = ($GetVhd.Size / 1GB)
CommittedGB = ($GetVhd.FileSize / 1GB)
}
}
Basically, for each of the virtual machine's hard drives, use Get-VHD to get the VHD details which includes the full size and what I refer to as the committed size (actual space on disk).
Example output:
Name Type ProvisionedGB CommittedGB
---- ---- ------------- -----------
Hard Drive on IDE controll... Dynamic 20 0.00390625
Hard Drive on IDE controll... Dynamic 40 0.00390625
Edit:
If you wanted to pull from every VM and include the VM name with the returned object and you prefer to use the pipeline form, this will work:
Get-VM | ForEach { $Vm = $_; $_.HardDrives } | ForEach {
$GetVhd = Get-VHD -Path $_.Path
[pscustomobject]#{
Vm = $Vm.Name
Name = $_.Name
Type = $GetVhd.VhdType
ProvisionedGB = ($GetVhd.Size / 1GB)
CommittedGB = ($GetVhd.FileSize / 1GB)
}
}

Incorrect total size result from powershell

I'm facing a problem which the result of getdrive is not accurate. Clueless.
As seen in the screenshots below, 10.1.105.203 has a total space of 7.81TB, but from what powershell gives me, it is only roughly 4TB. Pretty confusing here.
You can also use Get-Psdrive to get the size of a drive.
PS C:\> Get-PSDrive -PSProvider filesystem
Name Used (GB) Free (GB) Provider Root
---- --------- --------- -------- ----
C 48.92 195.13 FileSystem C:\
D 30.82 69.18 FileSystem D:\
E .12 121.49 FileSystem E:\
You can always do a little manipulation to achieve multiple things with this, e.g,
PS C:\> Get-PSDrive -PSProvider filesystem | select Name, #{n= 'Used(GB)' ; e = {"{0:N2}" -f ($_.used/1GB)}}, #{n= 'Free
(GB)' ; e = {"{0:N2}" -f ($_.Free/1GB)}}, #{n= 'Total(GB)' ; e = {"{0:N2}" -f (($_.used + $_.Free)/1GB)}} | ft -auto
Name Used(GB) Free(GB) Total(GB)
---- -------- -------- ---------
C 48.92 195.13 244.04
D 30.82 69.18 100.00
E 0.12 121.49 121.62
You're not really using PowerShell. You're using the Windows Scripting Host's object model, the precursor to PowerShell, from PowerShell. Try using either:
C:\PS> Get-Volume
DriveLetter FileSystemLabel FileSystem DriveType HealthStatus SizeRemaining Size
----------- --------------- ---------- --------- ------------ ------------- ----
C NTFS Fixed Healthy 94.34 GB 237.96 GB
Recovery NTFS Fixed Healthy 10.19 MB 300 MB
or
C:\PS> Get-WmiObject Win32_Volume | Format-Table DriveLetter,Capacity,FreeSpace
DriveLetter Capacity FreeSpace
----------- -------- ---------
C: 255505461248 101292863488
314568704 10682368
Full agree with #Keith Hill, with the PowerShell way to get the result. The strange behaviour is that for your drive L:, I can see that the total size is ok for the 10Tb drive, so I would not be surprise if you receive the same results from the new commands.
The next thing, I would check is if quotas are activated on your server.
You can also use :
Get-WmiObject Win32_logicaldisk -filter "driveType=4"

Unable to retrieve physical size of available storage for cluster

I am half way down with my work and now stuck.
I am trying to fetch information about available storage devices for a cluster.
I am able to fetch the list of available storage devices but unable to retrieve the physical disk, available free space, etc of these available storage.
I want like this. Is there any command to fetch physical disk name from Cluster Disk Name or directly can I get the disk details.
For Shared Disk I am able to retrieve the details (Get-ClusterSharedVolume) but not for a non-shared disk.
I want powershell or WMI script for doing so.
You can get this information from WMI, but it takes a couple steps:
$resources = Get-WmiObject -namespace root\MSCluster MSCluster_Resource -filter "Type='Physical Disk'"
$resources | foreach {
$res = $_
$disks = $res.GetRelated("MSCluster_Disk")
$disks | foreach {
$_.GetRelated("MSCluster_DiskPartition") |
select #{N="Name"; E={$res.Name}}, #{N="Status"; E={$res.State}}, Path, VolumeLabel, TotalSize, FreeSpace
}
} | ft
That will give you output like the following:
Name Status Path VolumeLabel TotalSize FreeSpace
---- ------ ---- ----------- --------- ---------
Cluster Disk 2 2 K: New Volume 5220 5163
SQL - FAS3070 SiteB 2 S: MC_SQL 5597 5455
SM Test 2 M: SM Test 1024 992
DTC - FAS3070B 2 F: MC_WITNESS 5346 5289
Cluster Disk Witness 2 E: New Volume 5322 5267
Cluster Disk 1 2 G: MC_DTC 5088 5035
Cluster Disk 3 2 T: SQL 5119 4999
If you don't care about the resource name/status you can skip those steps and jump straight to the partition (and it'll run much quicker):
gwmi -namespace root\MSCluster MSCluster_DiskPartition | ft Path, VolumeLabel, TotalSize, FreeSpace
Edit: Note that the size is in MB and a Status of "2" means that the disk is online.
you can use wmi like this:
Get-WMIObject Win32_LogicalDisk -filter "DriveType=3" | Select DeviceID, FreeSpace
throw in a computername parameter if you wish to do it remotely
HTH,
Matt
PS. for a more readable report you can try this:
Get-WMIObject Win32_LogicalDisk -filter "DriveType=3" |
Select DeviceID, #{Name = "Free Space (%)" ; Expression= {[int] ($_.FreeSpace / $_.Size* 100)}},#{Name = "Free Space (GB)"; Expression = {[int]($_.Freespace / 1GB)}}, #{Name = "Size (GB)"; Expression = {[int]($_.Freespace / 1GB)}}