Building Class-Based PowerShell Modules
Over the last several weeks, we’ve been exploring how to script with a PowerShell class. The last step is to take everything we’ve covered and wrap it up in a PowerShell module. As you’ll see, there are limitations in using a PowerShell class with a module. But, it is also easier to do more with a class in a module.
Here’s are script files that contains everything we’ve created up to now.
#requires -version 5.1
#requires -RunAsAdministrator
#Get-PSFolderInfo3.ps1
#region enum and class definition
enum FolderInfoTag {
Documents
PowerShell
Media
Temporary
SharedData
Public
Private
}
class psFolderInfo {
#region properties
[String]$Name
[String]$FullName
[DateTime]$LastWriteTime
[TimeSpan]$ModifiedAge
[Int32]$Files
[Double]$AverageSize
[Double]$TotalSize
[Boolean]$IsEmpty
[FolderInfoTag[]]$Tag
hidden [String]$Computername = [System.Environment]::MachineName
hidden [DateTime]$ReportDate = (Get-Date)
hidden [String]$LinkTarget
#endregion
#region methods
hidden [void]Initialize([String]$FullName) {
#move error handling to external code
$folder = Get-Item -Path $FullName
$stat = Get-ChildItem -Path $FullName -File -Recurse | Measure-Object -Property length -Sum -Average
#assign property values to $this
$this.FullName = $FullName
$this.Name = $folder.Name
$this.LastWriteTime = $folder.LastWriteTime
$this.Files = $stat.Count
$this.AverageSize = $stat.Average
$this.TotalSize = $stat.Sum
$this.IsEmpty = $stat.Count -eq 0
#update the hidden property
$this.LinkTarget = $folder.Target
}
[void]Refresh() {
$this.Initialize($this.FullName)
#update the hidden report date property
$this.ReportDate = Get-Date
}
[System.IO.FileInfo]GetNewest+File() {
$r = Get-ChildItem -Path $this.FullName -File -Recurse |
Sort-Object -Property LastWriteTime -Descending |
Select-Object -First 1
#must use return key work
return $r
}
[System.IO.FileInfo]GetLargestFile() {
$r = Get-ChildItem -Path $this.FullName -File -Recurse |
Sort-Object -Property Length -Descending |
Select-Object -First 1
#must use return key work
return $r
}
#endregion
#region constructors
psFolderInfo ([String]$FullName) {
$this.Initialize($FullName)
} #close constructor
#endregion
} #close class
#endregion
#region functions
Function Get-PSFolderInfo {
[cmdletbinding()]
[OutputType('psFolderInfo')]
[alias('gpfi')]
Param(
[Parameter(
Position = 0,
ValueFromPipeline,
HelpMessage = 'Specify a file system folder'
)]
[ValidateNotNullOrEmpty()]
[ValidateScript({
if (Test-Path -Path $_) {
$True
}
Else {
Throw "Failed to verify the path $_"
}
})]
[ValidateScript({
if ((Get-Item $_).GetType().Name -eq 'DirectoryInfo') {
$True
}
else {
Throw 'You must specify a folder path.'
}
})]
[ValidateScript({
if ((Get-Item $_).PSProvider.name -eq 'FileSystem') {
$True
}
else {
Throw 'You must specify a valid file system path.'
}
})]
[alias('FullName')]
[String]$Path = '.',
[ValidateSet("Documents", "PowerShell", "Media", "Temporary", "SharedData", "Public", "Private")]
[FolderInfoTag[]]$Tag
)
Begin {
Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)"
Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)"
} #begin
Process {
#convert path to a full file system path
$cPath = Convert-Path -Path $Path
Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $cPath"
$obj = New-Object -TypeName psFolderInfo -ArgumentList $cPath
if ($Tag) {
Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Adding tag(s) $($tag -join ', ')"
$obj.Tag = $Tag
}
#write result to the pipeline
$obj
} #process
End {
Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)"
} #end
} #close Get-PSFolderInfo
Function Set-PSFolderInfo {
[cmdletbinding()]
[OutputType('psFolderInfo')]
[alias('spfi')]
Param(
[Parameter(
Position = 0,
Mandatory,
ValueFromPipeline,
HelpMessage = "Specify a psFolderInfo object"
)]
[ValidateNotNullOrEmpty()]
[PSFolderInfo]$InputObject,
[ValidateSet("Documents", "PowerShell", "Media", "Temporary", "SharedData", "Public", "Private")]
[FolderInfoTag[]]$Tag
)
Begin {
Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)"
Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)"
} #begin
Process {
Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $($InputObject.FullName)"
if ($Tag) {
Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Adding tag(s) $($tag -join ', ')"
$InputObject.Tag = $Tag
}
#refresh the object
$InputObject.Refresh()
#write result to the pipeline
$InputObject
} #process
End {
Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)"
} #end
} #close Set-PSFolderInfo
#endregion
#region extend the type
#add an alias property
Update-TypeData -TypeName psFolderInfo -MemberType AliasProperty -MemberName Size -Value TotalSize -Force
#move ModifiedAge to an external script property so it is always updated
Update-TypeData -TypeName psFolderInfo -MemberType ScriptProperty -MemberName ModifiedAge -Value {
New-TimeSpan -Start $this.LastWriteTime -End (Get-Date)
} -Force
#endregion
#requires -version 5.1
class psFolderReport {
[string]$Path
[string]$Name
[System.IO.FileInfo]$LargestFile
[System.IO.FileInfo]$OldestFile
[System.IO.FileInfo]$NewestFile
hidden [string]$Computername = [Environment]::MachineName
hidden [DateTime]$ReportDate = (Get-Date)
psFolderReport([psFolderInfo]$PSFolderInfo) {
$this.Path = $PSFolderInfo.FullName
$this.name = $PSFolderInfo.Name
$this.LargestFile = $PSFolderInfo.GetLargestFile()
$this.NewestFile = $PSFolderInfo.GetNewestFile()
$this.OldestFile = $PSFolderInfo.GetOldestFile()
}
}
Function Get-PSFolderReport {
[cmdletbinding()]
[OutputType('psFolderReport')]
[alias('none')]
Param(
[Parameter(
Position = 0,
ValueFromPipeline,
HelpMessage = "Specify a PSFolderInfo object"
)]
[ValidateNotNullOrEmpty()]
[PSFolderInfo]$InputObject
)
Begin {
Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting $($MyInvocation.MyCommand)"
Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running under PowerShell version $($PSVersionTable.PSVersion)"
} #begin
Process {
Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $($InputObject.name)"
#This will fail because of scoping :New-Object -typename PSFolderReport -ArgumentList $InputObject
[PSFolderReport]::new($InputObject)
} #process
End {
Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending $($MyInvocation.MyCommand)"
} #end
} #close Get-PSFolderReport
Want to read the full issue?