Using PowerShell V2 to gather info on free space on the volumes of your remote file server
Overview
In a previous blog post, I have examined some of PowerShell’s control structures and included an example gathered some information from web. If you haven’t seen it, you can check it at https://blogs.technet.com/josebda/archive/2010/04/04/experimenting-with-powershell-v2-scripting-variables-and-control-structures.aspx. At the end of that post, I suggested a project to check free spaces on file servers. That’s what this post tackles
The goal here is to remotely check a file server, enumerate the shares on it and check the free space on the volume behind that share. For these tasks, we’ll use the WMI providers for file shares (Win32_Share), logical disks (Win32_LogicalDisk) and volumes (Win32_Volume).
Looking at your shares
To start, let’s do something simple. We’ll use the Get-WMIObject (GWMI for short) and the Win32_Share WMI class to obtain a list of all the shares in a server. The –ComputerName parameter is used to specify the remote server.
Get-WmiObject Win32_Share -ComputerName josebda-s1 | Select Name, Path, Type | FT
Name Path Type
---- ---- ----
ADMIN$ C:Windows 2147483648
C$ C: 2147483648
E$ E: 2147483648
IPC$ 2147483651
josebda-dfs C:DFSRootsjosebda-dfs 0
Proposals2009 C:ProposalsProposals2009 0
Proposals2010 C:ProposalsProposals2010 0
Software C:Software 0
StorageReports C:StorageReports 0
Looking at your logical disks
Next, we’ll look at the logical disks at that same server. The command is similar, obviously with a different WMI Class.
Get-WmiObject Win32_LogicalDisk -ComputerName josebda-s1 | Select Name, Size, FreeSpace
Name Size FreeSpace
---- ---- ---------
C: 239213735936 211060846592
D: 2996799488 0
Script to cross-reference shares and drives
Now that we know where to find all the information, we can put together a script to put it all together. For this one, we’ll skip the hidden administrative shares (filtering for shares with type 0, which are regular file shares). I’ll also use a little trick to create a new object with a custom set of properties, that mix share and logical disk information. Here’s that script:
$Shares = Get-WmiObject Win32_Share -ComputerName josebda-s1
Foreach ($Share in $Shares)
{
If ($Share.Type -eq 0)
{
$Result = "" | Select Server, Share, Path, Drive, Used, Free, Total
$Result.Server=$Share.__SERVER
$Result.Share=$Share.Name
$Result.Path=$Share.Path
$Result.Drive=$Result.Path[0];$Filter = "Name ='"+$Result.Drive+":'"
$Drive = Get-WMIObject Win32_LogicalDisk -Filter $Filter -ComputerName $Result.Server
$Result.Total=$Drive.Size
$Result.Free=$Drive.FreeSpace
$Result.Used=$Drive.Size - $Drive.FreeSpace
$Result
}
}
The script has a couple of issues. First, we assume that the first letter of the path behind a share can be used to find the matching logical disk. Also, if we have several shares on the same drive, we query WMI multiple times to get the drive information. But we'll overlook that for now.
Output of the script
Once you run it (the easiest way is to use the “Windows PowerShell ISE”), you will get the following output:
Server : JOSEBDA-S1
Share : josebda-dfs
Path : C:DFSRootsjosebda-dfs
Drive : C
Used : 28151881728
Free : 211061854208
Total : 239213735936Server : JOSEBDA-S1
Share : Proposals2009
Path : C:ProposalsProposals2009
Drive : C
Used : 28151881728
Free : 211061854208
Total : 239213735936Server : JOSEBDA-S1
Share : Proposals2010
Path : C:ProposalsProposals2010
Drive : C
Used : 28151881728
Free : 211061854208
Total : 239213735936Server : JOSEBDA-S1
Share : Software
Path : C:Software
Drive : C
Used : 28151881728
Free : 211061854208
Total : 239213735936Server : JOSEBDA-S1
Share : StorageReports
Path : C:StorageReports
Drive : C
Used : 28151881728
Free : 211061854208
Total : 239213735936
Turning it into a one-liner
Note that, because we’re sending an object to the pipeline, you can do interesting things with it like SELECT just a few of the properties, find just share WHERE a certain condition is met, GROUP results or FORMAT the results as a TABLE. Another fun thing you can do is write this whole thing as a single command line (some people are into that :-). In that case, you might want to use shorter variable names and use abbreviated aliases for cmdlets and parameters. Here’s an example (that does exactly the same as the script above) that you can just cut and paste into a command line:
GWMI Win32_Share -CN josebda-s1 | % { If ($_.Type -eq 0) { $R="" | Select Server, Share, Path, Drive, Used, Free, Total; $R.Server=$_.__SERVER; $R.Share=$_.Name; $R.Path=$_.Path; $R.Drive=$R.Path[0]; $F="Name ='"+$R.Drive+":'"; $D=GWMI Win32_LogicalDisk -Filter $F -CN $R.Server; $R.Total=$D.Size; $R.Free=$D.FreeSpace; $R.Used=$D.Size-$D.FreeSpace; $R} } | FT
Server Share Path Drive Used Free Total
------ ----- ---- ----- ---- ---- -----
JOSEBDA-S1 josebda-dfs C:DFSRootsj... C 28152012800 211061723136 239213735936
JOSEBDA-S1 Proposals2009 C:Proposals... C 28152012800 211061723136 239213735936
JOSEBDA-S1 Proposals2010 C:Proposals... C 28152012800 211061723136 239213735936
JOSEBDA-S1 Software C:Software C 28152012800 211061723136 239213735936
JOSEBDA-S1 StorageReports C:StorageRep... C 28152012800 211061723136 239213735936
Looking at volumes and mount points
In the previous scripts, there was at least one thing that we did not take into consideration. The folders used by the share could be under a mount point. You see, Windows allows you to mount a volume under an empty folder of an existing volume. To check this out, you could use the Win32_Volume WMI class:
Get-WmiObject Win32_Volume -ComputerName josebda-s1 | Select Name, Capacity, FreeSpace, BootVolume, SystemVolume, FileSystem | FT
Name Capacity FreeSpace BootVolume SystemVolume FileSystem
---- -------- --------- ---------- ------------ ----------
\?Volume{3d9fc... 104853504 75362304 False True NTFS
C:Proposals 6441398272 6379917312 False False NTFS
C: 239213735936 211063054336 True False NTFS
\?Volume{150c7... 4294963200 4244283392 False False NTFS
D: 2996799488 0 False False UDF
Along with a few volumes that are not mounted (first and fourth on the list), you can see that the “C:Proposals” folder is actually a mount point. That is important because it won’t use space from the “C:” volume.
Script to cross-reference shares and mount points
We now need to change our script to account for that. It’s a bit tricky because mount points look exactly like folders and could happen anywhere in the main volume hierarchy. One way to do it is taking one pass at the list of volumes for each share we process, verifying what the the longest path in the volumes list that matches the beginning of the file share path.
$Shares = Get-WMIObject Win32_Share -ComputerName Josebda-s1
$Volumes = Get-WMIObject Win32_Volume -ComputerName Josebda-s1
Foreach ($Share in $Shares)
{
If ($Share.Type -eq 0)
{
$Result = "" | Select Server, Share, Path, Volume, Used, Free, Total
$Result.Server=$Share.__SERVER
$Result.Share=$Share.Name
$Result.Path=$Share.Path
$ShareVolume="" | Select Name
Foreach ($Volume in $Volumes)
{
If ( ($Result.Path.Length -ge $Volume.Name.Length) -and ($Volume.Name.Length -gt $ShareVolume.Name.Length) )
{
If ($Result.Path.Substring(0,$Volume.Name.Length) -eq $Volume.Name) { $ShareVolume = $Volume }
}
}
$Result.Volume=$ShareVolume.Name;
$Result.Total=$ShareVolume.Capacity
$Result.Free=$ShareVolume.FreeSpace
$Result.Used=$ShareVolume.Capacity - $ShareVolume.FreeSpace
$Result
}
}
Output of the updated script
Here’s what the output looks now:
Server : JOSEBDA-S1
Share : josebda-dfs
Path : C:DFSRootsjosebda-dfs
Volume : C:
Used : 28152209408
Free : 211061526528
Total : 239213735936Server : JOSEBDA-S1
Share : Proposals2009
Path : C:ProposalsProposals2009
Volume : C:Proposals
Used : 61480960
Free : 6379917312
Total : 6441398272Server : JOSEBDA-S1
Share : Proposals2010
Path : C:ProposalsProposals2010
Volume : C:Proposals
Used : 61480960
Free : 6379917312
Total : 6441398272Server : JOSEBDA-S1
Share : Software
Path : C:Software
Volume : C:
Used : 28152209408
Free : 211061526528
Total : 239213735936Server : JOSEBDA-S1
Share : StorageReports
Path : C:StorageReports
Volume : C:
Used : 28152209408
Free : 211061526528
Total : 239213735936
Another one-liner, now with mount points
As you can see, we now correctly account for the free space for the shares under a mount point. Once again, you can try the “one liner” version below, with a format-table at the end.
$VV=GWMI Win32_Volume -CN Josebda-s1; GWMI Win32_Share -CN Josebda-s1 | % { If ($_.Type -eq 0) { $R=""|Select Server, Share, Path, Volume, Used, Free, Total; $R.Server=$_.__SERVER; $R.Share=$_.Name; $R.Path=$_.Path; $SV=""|Select Name; foreach($V in $VV) { If (($R.Path.Length -ge $V.Name.Length) -and ($V.Name.Length -gt $SV.Name.Length) ) { If ($R.Path.Substring(0,$V.Name.Length) -eq $V.Name) {$SV=$V} } } $R.Volume=$SV.Name; $R.Total=$SV.Capacity; $R.Free=$SV.FreeSpace; $R.Used=$SV.Capacity-$SV.FreeSpace; $R } } | FT
Server Share Path Volume Used Free Total
------ ----- ---- ------ ---- ---- -----
JOSEBDA-S1 josebda-dfs C:DFSRootsj... C: 28152344576 211061391360 239213735936
JOSEBDA-S1 Proposals2009 C:Proposals... C:Proposals 61480960 6379917312 6441398272
JOSEBDA-S1 Proposals2010 C:Proposals... C:Proposals 61480960 6379917312 6441398272
JOSEBDA-S1 Software C:Software C: 28152344576 211061391360 239213735936
JOSEBDA-S1 StorageReports C:StorageRep... C: 28152344576 211061391360 239213735936
Conclusion
If you are an IT Administrator with limited development experience, the scripts in this post might look a bit intimidating at first, but please refer to my previous post to learn a bit about control structures and also how to save them as PS1 script files so you can re-use them. If you are a developer, I encourage you to investigate the many, many other WMI classes and start putting all that information to good use. Get started at https://msdn.microsoft.com/en-us/library/aa394084(v=VS.85).aspx
Comments
- Anonymous
January 01, 2003
Great idea! I have updated the scripts to use Type -eq 0... - Anonymous
January 01, 2003
If you want to skip the hidden administrative shares ONLY (and not all of the hidden shares), I would suggest to change($Share.Name[$Share.Name.Length-1] -ne "$")to($_.type -eq 0)Type 0 (0x0) is Disk Drive.