An Extended DriveInfo Solution
In this issue:
At the end of last month I began sharing my solution for the October scripting challenge. I had so much to cover that I ran of space. There are size limits for Gmail which sets a threshold I have to respect. So let's pick up where we left off and see how to take the solution even further.
I left you with a PowerShell function that wrapped around the [System.IO.DriveInfo]::GetDrives() static method. The function creates custom object output with the required properties from the challenge.
The function can work locally:
PS C:\> Get-IoDriveInfo
Name : C:\
DriveType : Fixed
DriveFormat : NTFS
AvailableFreeSpace : 264449970176
TotalSize : 509722226688
PctFree : 51.88
VolumeLabel : Windows
Computername : CADENZA
Name : G:\
DriveType : Fixed
DriveFormat : FAT32
AvailableFreeSpace : 9332088832
TotalSize : 16106127360
PctFree : 57.94
VolumeLabel : Google Drive
Computername : CADENZA
Or remotely, and it supports ssh remoting as well.
PS C:\> Get-IoDriveInfo -Hostname fred -user jeff
Name : /
DriveType : Fixed
DriveFormat : btrfs
AvailableFreeSpace : 46003531776
TotalSize : 51982106624
PctFree : 88.5
VolumeLabel : /
Computername : fred
Name : /dev
DriveType : Ram
DriveFormat : udev
AvailableFreeSpace : 4090560512
TotalSize : 4090560512
PctFree : 100
VolumeLabel : /dev
Computername : fred
...
> I omitted the password prompt from the output.
Enhancing the Object Type
At this point, I have a working function that meets the basic requirements. This is the point in the development process where great PowerShell scripters separate themselves from good scripters. This is the point where you think about how to elevate your code. What can you do to make it easier for the user to run or consume the output? I spent several articles recently on this very topic so let's apply those concepts here.
I already have a good set of property names.
PS C:\> $a
PS C:\> $a | Get-Member -MemberType Properties
TypeName: IoDriveInfo
Name MemberType Definition
---- ---------- ----------
AvailableFreeSpace NoteProperty long AvailableFreeSpace=264406237184
Computername NoteProperty string Computername=CADENZA
DriveFormat NoteProperty string DriveFormat=NTFS
DriveType NoteProperty DriveType DriveType=Fixed
Name NoteProperty string Name=C:\
PctFree NoteProperty double PctFree=51.87
TotalSize NoteProperty long TotalSize=509722226688
VolumeLabel NoteProperty string VolumeLabel=Windows
What would make this object easier to use? Here are some ideas.
Adding Aliases
The object is using Name to represent the drive. The user may expect a property called Drive. I can add an alias property for that.
Update-TypeData -TypeName IoDriveInfo -MemberType AliasProperty -MemberName Drive -Value Name -force
Likewise, I may want to make it easier to reference free space and total size.
Update-TypeData -TypeName IoDriveInfo -MemberType AliasProperty -MemberName Freespace -Value AvailableFreeSpace -force
Update-TypeData -TypeName IoDriveInfo -MemberType AliasProperty -MemberName Size -Value TotalSize -force
Adding Script Properties
The object's raw values are in bytes. If the user wants to see sizes in different units, I am forcing them to write an expression like this:
$a | Select Drive,@{Name="SizeGB";Expression={$_.TotalSize/1GB -as [int]}},@{Name="FreeGB";Expression = {[math]::Round($_.AvailableFreespace/1gb,2)}}
Even if you know how to do this, it is tedious to write. A better approach is to add script properties to the object for sizes in MB and GB.
Update-TypeData -TypeName IoDriveInfo -MemberType ScriptProperty -MemberName SizeMB -Value {$this.TotalSize/1mb -as [int]} -force
Update-TypeData -TypeName IoDriveInfo -MemberType ScriptProperty -MemberName SizeGB -Value {$this.TotalSize/1gb -as [int]} -force
Update-TypeData -TypeName IoDriveInfo -MemberType ScriptProperty -MemberName FreeMB -Value {[math]::Round($this.AvailableFreeSpace/1mb,2)} -force
Update-TypeData -TypeName IoDriveInfo -MemberType ScriptProperty -MemberName FreeGB -Value {[math]::Round($this.AvailableFreeSpace/1gb,2)} -force
Now look how easy this becomes:
PS C:\> $a | Select Drive,SizeGB,FreeGB
Drive SizeGB FreeGB
----- ------ ------
C:\ 475 246.25
G:\ 15 8.69
> This is the type of thing you want to include in help examples.
Notice that all of my references use the native object properties. There isn't a hard rule but I try to avoid using type extensions to define other type extensions.
Adding a Property Set
Another thing to consider is how object properties might be used. It isn't difficult to imagine this as a common use case:
PS C:\> $a | Select-Object Name,Size,Freespace,PctFree,Computername
Name : C:\
Size : 509722226688
Freespace : 264406237184
PctFree : 51.87
Computername : CADENZA
Name : G:\
Size : 16106127360
Freespace : 9332088832
PctFree : 57.94
Computername : CADENZA
I can ease the hassle of typing this by creating a property set. If you recall from an earlier article, this requires a .p1xml file. You can create it using New-PSPropertySet from the PSTypeExtensionTools module.
New-PSPropertySet -TypeName IoDriveInfo -FilePath .\IoDriveInfo.types.ps1xml -Name Space -Properties Name,Size,Freespace,PctFree
Once created, I can load the property set using Update-TypeData.
Update-TypeData .\IoDriveInfo.types.ps1xml