powershellpowershell-7.0output-formatting

Is There a Way to Cause Powershell to Use a Particular Format for a Function's Output?


I wish to suggest (perhaps enforce, but I am not firm on the semantics yet) a particular format for the output of a PowerShell function.

about_Format.ps1xml (versioned for PowerShell 7.1) says this: 'Beginning in PowerShell 6, the default views are defined in PowerShell source code. The Format.ps1xml files from PowerShell 5.1 and earlier versions don't exist in PowerShell 6 and later versions.'. The article then goes on to explain how Format.ps1xml files can be used to change the display of objects, etc etc. This is not very explicit: 'don't exist' -ne 'cannot exist'...

This begs several questions:

  1. Although they 'don't exist', can Format.ps1xml files be created/used in versions of PowerShell greater than 5.1?
  2. Whether they can or not, is there some better practice for suggesting to PowerShell how a certain function should format returned data? Note that inherent in 'suggest' is that the pipeline nature of PowerShell's output must be preserved: the user must still be able to pipe the output of the function to Format-List or ForEach-Object etc..

For example, the Get-ADUser cmdlet returns objects formatted by Format-List. If I write a function called Search-ADUser that calls Get-ADUser internally and returns some of those objects, the output will also be formatted as a list. Piping the output to Format-Table before returning it does not satisfy my requirements, because the output will then not be treated as separate objects in a pipeline.

Example code:

function Search-ADUser {
  param (
    $Name,
    [ValidateNotNullOrEmpty()][string[]]$Properties = @('Enabled', 'SamAccountName', 'Name', 'emailAddress', 'proxyAddresses')
  )
  return Get-ADUser -Filter ('name -like "*{0}*"' -F $Name) -Properties $Properties | Select-Object $Properties
}

The best answers should address both questions, although the second is more salient.

Unacceptable answers include suggestions that the function should not enforce a format, and/or that the user should pipe the output of the function to their formatter of choice. That is a very subjective stance, and whether it is held by the majority or not is irrelevant to the question.

I searched force function format #powershell-7.0 before posting, but none of the search results appeared to be relevant.


Solution

  • Although they 'don't exist', can Format.ps1xml files be created/used in versions of PowerShell greater than 5.1?

    is there some better practice for suggesting to PowerShell how a certain function should format returned data?

    The lack of an API-based way to define formatting data requires the following approach:

    GitHub proposal #10463 asks for a greatly simplified experience, along the lines of supporting extended [OutputType()] attributes that specify the desired formatting.


    Applied to your sample function:

    function Search-ADUser {
      param (
        $Name,
        [ValidateNotNullOrEmpty()][string[]]$Properties = @('Enabled', 'SamAccountName', 'Name', 'emailAddress', 'proxyAddresses')
      )
    
      # The self-chosen ETS type name.
      $etsTypeName = 'SearchAdUserResult'
    
      # Create the formatting data on demand.
      if (-not (Get-FormatData -ErrorAction Ignore $etsTypeName)) {
    
        # Create a temporary file with formatting definitions to pass to 
        # Update-FormatData below.
        $tempFile = Join-Path ([IO.Path]::GetTempPath()) "$etsTypeName.Format.ps1xml"
    
        # Define a table view with all 5 properties.
        @"
    <Configuration>
    <ViewDefinitions>
        <View>
          <Name>$etsTypeName</Name>
          <ViewSelectedBy>
            <TypeName>$etsTypeName</TypeName>
          </ViewSelectedBy>
          <TableControl>
            <TableRowEntries>
              <TableRowEntry>
                <TableColumnItems>
                  <TableColumnItem>
                    <PropertyName>Enabled</PropertyName>
                  </TableColumnItem>
                  <TableColumnItem>
                    <PropertyName>SamAccountName</PropertyName>
                  </TableColumnItem>
                  <TableColumnItem>
                    <PropertyName>Name</PropertyName>
                  </TableColumnItem>
                  <TableColumnItem>
                    <PropertyName>emailAddress</PropertyName>
                  </TableColumnItem>
                  <TableColumnItem>
                    <PropertyName>proxyAddresses</PropertyName>
                  </TableColumnItem>
                </TableColumnItems>
              </TableRowEntry>
            </TableRowEntries>
          </TableControl>
        </View>
      </ViewDefinitions>
    </Configuration>
    "@ > $tempFile
    
        # Load the formatting data into the current session.
        Update-FormatData -AppendPath $tempFile
    
        # Clean up.
        Remove-Item $tempFile
      }
    
      # Call Get-ADUser and assign the self-chosen ETS type name to the the output.
      # Note: To test this with a custom-object literal, use the following instead of the Get-ADUser call:
      #      [pscustomobject] @{ Enabled = $true; SamAccountName = 'jdoe'; Name = 'Jane Doe'; emailAddress = 'jdoe@example.org'; proxyAddresses = 'janedoe@example.org' }
      Get-ADUser -Filter ('name -like "*{0}*"' -F $Name) -Properties $Properties | Select-Object $Properties | ForEach-Object {
         $_.pstypenames.Insert(0, $etsTypeName); $_
      }
    
    }
    

    You'll then see the desired tabular output based on the format data; e.g.:

    Enabled SamAccountName Name     emailAddress     proxyAddresses
    ------- -------------- ----     ------------     --------------
    True    jdoe           Jane Doe jdoe@example.org janedoe@example.org