I’ve been doing some more experiments with custom types after my previous post on this topic and when working with classes there is an additional consideration to keep in mind: depending on where the class is declared, the object type (actually, the type name) changes which in turn will affect how the custom format file needs to be crafted.
For example, let’s consider an example where the type declaration is outside a function declaration:
class AzSqlDatabaseSize { [string]$ServerName [string]$DatabaseName [string]$Sku [string]$CurrentSizeGb [string]$MaxSizeGb [string]$PercentageUsed AzSqlDatabaseSize($ServerName, $DatabaseName, $Sku, $CurrentSizeGb, $MaxSizeGb, $PercentageUsed) { $this.ServerName = $ServerName; $this.DatabaseName = $DatabaseName; $this.Sku = $Sku; $this.CurrentSizeGb = $CurrentSizeGb; $this.MaxSizeGb = $MaxSizeGb; $this.PercentageUsed = $PercentageUsed } } function Get-AzSqlDatabaseSize { [CmdletBinding()] [OutputType('AzSqlDatabaseSize')] param ( [parameter(Mandatory, Position = 1, ValueFromPipeline, ValueFromPipelineByPropertyName)] [string]$ServerName, [parameter(Mandatory, Position = 2, ValueFromPipelineByPropertyName)] [string]$ResourceGroupName, [parameter(Mandatory, Position = 3, ValueFromPipelineByPropertyName)] [string]$DatabaseName ) process { $database = Get-AzSqlDatabase -ServerName $ServerName -ResourceGroupName $ResourceGroupName -DatabaseName $DatabaseName $dbSize = $null $dbSize = $database | Get-AzMetric -MetricName 'storage' -WarningAction 'SilentlyContinue' $ServerName = $resourceIdTokens[8] $DatabaseName = $resourceIdTokens[10] $Sku = $database.CurrentServiceOBjectiveName $CurrentSizeGb = ($dbSize.Data[0].Maximum / 1gb).ToString("0.00") $MaxSizeGb = $database.MaxSizeBytes / 1gb $PercentageUsed = (($dbSize.Data[0].Maximum) / ($database.MaxSizeBytes) * 100).ToString("0.00") $outObj = [AzSqlDatabaseSize]::new($ServerName, $DatabaseName, $Sku, $CurrentSizeGb, $MaxSizeGb, $PercentageUsed) $outObj } }
If we execute this function and check the output with Get-Member
, the type name matches exactly the class name as expected:
λ 32 carlo@CARLOCXPS 14:12:47 carlo >_ Get-AzSqlServer | Get-AzSqlDatabase | Get-AzSqlDatabaseSize | gm TypeName: AzSqlDatabaseSize Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() ToString Method string ToString() CurrentSizeGb Property string CurrentSizeGb {get;set;} DatabaseName Property string DatabaseName {get;set;} MaxSizeGb Property string MaxSizeGb {get;set;} PercentageUsed Property string PercentageUsed {get;set;} ServerName Property string ServerName {get;set;} Sku Property string Sku {get;set;}
If, on the other hand I define the class as part of the function, things change:
function Get-AzSqlDatabaseSize { [CmdletBinding()] [OutputType('AzSqlDatabaseSize')] param ( [parameter(Mandatory, Position = 1, ValueFromPipeline, ValueFromPipelineByPropertyName)] [string]$ServerName, [parameter(Mandatory, Position = 2, ValueFromPipelineByPropertyName)] [string]$ResourceGroupName, [parameter(Mandatory, Position = 3, ValueFromPipelineByPropertyName)] [string]$DatabaseName ) begin { class AzSqlDatabaseSize { [string]$ServerName [string]$DatabaseName [string]$Sku [string]$CurrentSizeGb [string]$MaxSizeGb [string]$PercentageUsed AzSqlDatabaseSize($ServerName, $DatabaseName, $Sku, $CurrentSizeGb, $MaxSizeGb, $PercentageUsed) { $this.ServerName = $ServerName; $this.DatabaseName = $DatabaseName; $this.Sku = $Sku; $this.CurrentSizeGb = $CurrentSizeGb; $this.MaxSizeGb = $MaxSizeGb; $this.PercentageUsed = $PercentageUsed } } } process { $database = Get-AzSqlDatabase -ServerName $ServerName -ResourceGroupName $ResourceGroupName -DatabaseName $DatabaseName $dbSize = $null $dbSize = $database | Get-AzMetric -MetricName 'storage' -WarningAction 'SilentlyContinue' $resourceIdTokens = $dbSize.Id -split '/' $ServerName = $resourceIdTokens[8] $DatabaseName = $resourceIdTokens[10] $Sku = $database.CurrentServiceOBjectiveName $CurrentSizeGb = ($dbSize.Data[0].Maximum / 1gb).ToString("0.00") $MaxSizeGb = $database.MaxSizeBytes / 1gb $PercentageUsed = (($dbSize.Data[0].Maximum) / ($database.MaxSizeBytes) * 100).ToString("0.00") $outObj = [AzSqlDatabaseSize]::new($ServerName, $DatabaseName, $Sku, $CurrentSizeGb, $MaxSizeGb, $PercentageUsed) $outObj } }
Now if I execute this function again and pipe the output to Get-Member
, the type name returned uses the function name as a namespace declaration; this is important and must be taken into account and used in the view name if we want it to match and properly format the output for this custom type:
λ 31 carlo@CARLOCXPS 14:11:01 carlo >_ Get-AzSqlServer | Get-AzSqlDatabase | Get-AzSqlDatabaseSize | gm TypeName: Get-AzSqlDatabaseSize.AzSqlDatabaseSize Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() ToString Method string ToString() CurrentSizeGb Property string CurrentSizeGb {get;set;} DatabaseName Property string DatabaseName {get;set;} MaxSizeGb Property string MaxSizeGb {get;set;} PercentageUsed Property string PercentageUsed {get;set;} ServerName Property string ServerName {get;set;} Sku Property string Sku {get;set;}
One final note about OutputType
:
The OutputType attribute value is only a documentation note. It is not derived from the function code or compared to the actual function output. As such, the value might be inaccurate.
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_outputtypeattribute?view=powershell-6
From sublime to ridiculousness there is only one step. – Napoleon