Using PowerShell V2 to gather info on free space on the volumes of your remote file server

Using PowerShell V2 to gather info on free space on the volumes of your remote file server

  • Comments 2
  • Likes

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 http://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:\DFSRoots\josebda-dfs                           0
Proposals2009              C:\Proposals\Proposals2009                        0
Proposals2010              C:\Proposals\Proposals2010                        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:\DFSRoots\josebda-dfs
Drive  : C
Used   : 28151881728
Free   : 211061854208
Total  : 239213735936

Server : JOSEBDA-S1
Share  : Proposals2009
Path   : C:\Proposals\Proposals2009
Drive  : C
Used   : 28151881728
Free   : 211061854208
Total  : 239213735936

Server : JOSEBDA-S1
Share  : Proposals2010
Path   : C:\Proposals\Proposals2010
Drive  : C
Used   : 28151881728
Free   : 211061854208
Total  : 239213735936

Server : JOSEBDA-S1
Share  : Software
Path   : C:\Software
Drive  : C
Used   : 28151881728
Free   : 211061854208
Total  : 239213735936

Server : 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:\DFSRoots\j...                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:\DFSRoots\josebda-dfs
Volume : C:\
Used   : 28152209408
Free   : 211061526528
Total  : 239213735936

Server : JOSEBDA-S1
Share  : Proposals2009
Path   : C:\Proposals\Proposals2009
Volume : C:\Proposals\
Used   : 61480960
Free   : 6379917312
Total  : 6441398272

Server : JOSEBDA-S1
Share  : Proposals2010
Path   : C:\Proposals\Proposals2010
Volume : C:\Proposals\
Used   : 61480960
Free   : 6379917312
Total  : 6441398272

Server : JOSEBDA-S1
Share  : Software
Path   : C:\Software
Volume : C:\
Used   : 28152209408
Free   : 211061526528
Total  : 239213735936

Server : 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:\DFSRoots\j... 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 http://msdn.microsoft.com/en-us/library/aa394084(v=VS.85).aspx

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • 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.

  • Great idea! I have updated the scripts to use Type -eq 0...