Powershell

Custom types with Powershell (part 2)

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 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.