Search code examples
powershelltypename

Custom TypeNames Slow Output


I have a module with a function that collects the firmware versions on HP servers using the HPRESTCmdlets module found on the gallery. The function assigns the object a typename of 'Hardware.Firmware'. I am using a ps1xml for custom viewing.

The function consists of a Begin, Process, and End scriptblock. When running the function against a set of objects (via foreach) the first object is always delayed in the output to the console, it actually is displayed after the End block runs. Every sequential object runs as expected. I also receive the same delay if running against a single object.

If I remove the custom typename, the first object has no delay and is processed correctly. Any ideas on why there is a delay on processing the first object when using a custom type and how can I avoid it?

Here is the code for the function:

function Get-HPFirmware {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, Position = 0)]
        [Alias('Name', 'Server', 'Ip')]
        [string]$iLoName,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullorEmpty()]
        [Alias('User')]
        [System.Management.Automation.PSCredential][System.Management.Automation.Credential()]
        $Credential,

        [switch]$IgnoreCertFailures
    )

    Begin {
        $DefaultVariables = $(Get-Variable).Name
        try {
            Import-Module -Name HPRESTCmdlets -Force -ErrorAction Stop -Verbose:$false
        } catch {
            throw
        }

        Write-Verbose -Message "Splatting parameters for Connect-HPREST"
        $ConnectParams = @{
            'Address'  = $PSBoundParameters['iLoName']
            'Credential' = $PSBoundParameters['Credential']
        }

        if ($PSBoundParameters.ContainsKey('IgnoreCertFailures')) {
            $ConnectParams.DisableCertificateAuthentication = $true
        }
    }
    Process {
        try {
            Write-Verbose -Message "Connecting to $($ConnectParams['Address'])"
            $Session = Connect-HPREST @ConnectParams -ErrorAction Stop
        } catch {
            throw
        }

        try {
            $Systems = Get-HPRESTDataRaw -Href '/rest/v1/Systems' -Session $Session -ErrorAction Stop
            foreach ($Sys in $Systems.links.member.href) {
                $Data = Get-HPRESTDataRaw -Href $Sys -Session $Session -ErrorAction Stop
                $FirmwareUri = ($Data.Oem.Hp.links.PSObject.Members | Where-Object -FilterScript { $_.Name -match 'Firmware' }).Value.href
                Write-Verbose -Message "Firmware Uri ($FirmwareUri) discovered"
                if ($FirmwareUri) {
                    $FirmwareData = Get-HPRESTDataRaw -Href $FirmwareUri -Session $Session -ErrorAction Stop
                    if ($FirmwareData) {
                        $Firmware = $FirmwareData.Current | ForEach-Object -Process {
                            ($_.PSObject.Members | Where-Object -FilterScript { $_.MemberType -eq 'NoteProperty' }).Value
                        }
                        Write-Verbose -Message "$($Firmware.Count) components discovered"
                    } else {
                        Write-Warning -Message "No firmware data available via $FirmwareUri for $($PSBoundParameters['iLoName'])"
                        break
                    }
                } else {
                    Write-Warning -Message "Unable to locate the firmware uri"
                    break
                }

                $PCIDevicesUri = ($Data.Oem.Hp.links.PSObject.Members | Where-Object -FilterScript { $_.Name -match 'PCIDevices' }).Value.href
                Write-Verbose -Message "PCI Device Uri ($PCIDevicesUri) discovered"
                if ($PCIDevicesUri) {
                    $PCIData = Get-HPRESTDataRaw -Href $PCIDevicesUri -Session $Session -ErrorAction Stop
                    if (!$PCIData) {
                        Write-Warning -Message "No PCI device data available via $PCIDevicesUri for $($PSBoundParameters['iLoName'])"
                        break
                    }
                    Write-Verbose -Message "$($PCIData.Items.Count) devices discovered"
                } else {
                    Write-Warning -Message "Unable to locate the PCI device uri"
                    break
                }

                foreach ($i in $Firmware) {
                    if ($i.UEFIDevicePaths) {
                        $Device = $PCIData.Items | Where-Object -FilterScript { $_.UEFIDevicePath -eq $i.UEFIDevicePaths }
                        $Props = @{
                            'ElementName'    = $i.Name
                            'Location'         = $i.Location
                            'VersionString'  = $i.VersionString
                            'FQDD'             = if ($i.Name -match 'FC') { $Device.StructuredName -replace "^\w{3}", "FC" } else { $Device.StructuredName -replace "\s", "" }
                            'DeviceId'         = $Device.DeviceId
                            'SubDeviceId'    = $Device.SubsystemDeviceID
                            'VendorId'         = $Device.VendorID
                            'SubVendorId'    = $Device.SubsystemVendorID
                        }
                    } else {
                        $Props = @{ }
                        switch -wildcard ($i.Name) {
                            '*Power Supply*' {
                                $Props.ElementName = "$($i.Name).$($i.Location)"
                                $Props.Location = $i.Location
                                $Props.VersionString = $i.VersionString
                                $Props.FQDD = "PSU.$($i.Location -replace '\s', '')"
                            }
                            '*iLo*' {
                                $Props.ElementName = "Integrated Lights Out"
                                $Props.Location = $i.Location
                                $Props.VersionString = $i.VersionString.Split(' ')[0]
                                $Props.FQDD = "$($i.Name).$($i.Location -replace '\s', '')"
                            }
                            '*System ROM*' {
                                $Props.ElementName = $i.Name
                                $Props.Location = $i.Location
                                $Props.VersionString = $i.VersionString.Split(' ')[1]
                                $Props.FQDD = "BIOS.$($i.Location -replace '\s', '')"
                            }
                            '*Intelligent*' {
                                $Props.ElementName = $i.Name
                                $Props.Location = $i.Location
                                $Props.VersionString = $i.VersionString
                                $Props.FQDD = "DriverPack.$($i.Location -replace '\s', '')"
                            }
                            '*Power Management*' {
                                $Props.ElementName = $i.Name
                                $Props.Location = $i.Location
                                $Props.VersionString = $i.VersionString
                                $Props.FQDD = "DriverPack.$($i.Location -replace '\s', '')"
                            } '*Server Platform*' {
                                $Props.ElementName = $i.Name
                                $Props.Location = $i.Location
                                $Props.VersionString = $i.VersionString
                                $Props.FQDD = "SPS.$($i.Location -replace '\s', '')"
                            } '*Logic Device*' {
                                $Props.ElementName = $i.Name
                                $Props.Location = $i.Location
                                $Props.VersionString = $i.VersionString.Split(' ')[-1]
                                $Props.FQDD = "SPLD.$($i.Location -replace '\s', '')"
                            }
                            default {
                                $Props.ElementName = $i.Name
                                $Props.Location = $i.Location
                                $Props.VersionString = $i.VersionString
                                $Props.FQDD = "Unknown.$($i.Location -replace '\s', '')"
                            }
                        }
                    }
                    $Object = New-Object -TypeName System.Management.Automation.PSObject -Property $Props
                    $Object.PSObject.TypeNames.Insert(0,'Hardware.Firmware')
                    $Object
                }
            }
            Write-Verbose -Message "Disconnecting from iLo"
            Disconnect-HPREST -Session $Session
        } catch {
            Disconnect-HPREST -Session $Session
            throw
        }
    }
    End {
        Write-Verbose -Message "Cleaning up variables created by cmdlet"
        ((Compare-Object -ReferenceObject (Get-Variable).Name -DifferenceObject $DefaultVariables).InputObject) | ForEach-Object -Process {
            Remove-Variable -Name $_ -Force -ErrorAction Ignore
        }
    }
}

Here is the ps1xml:

<?xml version="1.0" encoding="utf-8" ?>
<Configuration>
    <ViewDefinitions>
        <View>
            <Name>Hardware.Firmware</Name>
            <ViewSelectedBy>
                <TypeName>Hardware.Firmware</TypeName>
            </ViewSelectedBy>
            <TableControl>
                <TableHeaders>
                    <TableColumnHeader>
                        <Label>Name</Label>
                    </TableColumnHeader>
                    <TableColumnHeader>
                        <Label>Version</Label>
                    </TableColumnHeader>
                </TableHeaders>
                <TableRowEntries>
                    <TableRowEntry>
                        <TableColumnItems>
                            <TableColumnItem>
                                <PropertyName>ElementName</PropertyName>
                            </TableColumnItem>
                            <TableColumnItem>
                                <PropertyName>VersionString</PropertyName>
                            </TableColumnItem>
                        </TableColumnItems>
                    </TableRowEntry>
                 </TableRowEntries>
            </TableControl>
        </View>
    </ViewDefinitions>
</Configuration>

Here is the sample Verbose output:

VERBOSE: Splatting parameters for Connect-HPREST
VERBOSE: Connecting to x.x.x.x
VERBOSE: Firmware Uri (/rest/v1/Systems/1/FirmwareInventory) discovered
VERBOSE: 19 components discovered
VERBOSE: PCI Device Uri (/rest/v1/Systems/1/PCIDevices) discovered
VERBOSE: 13 devices discovered

VERBOSE: Disconnecting from iLo
VERBOSE: Cleaning up variables created by cmdlet (This is the END block)
Name                                           Version
-----------                                    -------------
Smart HBA H240ar                               4.52
HP StorageWorks 82Q 8Gb PCI-e Dual Port FC HBA 08.02.00
HP StorageWorks 82Q 8Gb PCI-e Dual Port FC HBA 08.02.00
HP Ethernet 1Gb 4-port 331i Adapter            17.4.41
HP Ethernet 10Gb 2-port 530T Adapter           7.14.79

Solution

  • To the credit of PetSerAl above setting the column width within the ps1xml did resolve the problem. The reason why is, when you have a custom format and you don't specify the width, PowerShell has to wait until it sees every object, so that it can figure out how wide to try and make each column.

    With out the width the command looks like this on the pipeline Get-HPFirmware | Format-Table -Autosize | Out-Default. Autosize blocks the output on the pipeline until it reaches Out-Default.