Issue with pipeline support in function

Tagged: 

This topic contains 3 replies, has 2 voices, and was last updated by Profile photo of David Muegge David Muegge 1 year, 9 months ago.

  • Author
    Posts
  • #29772
    Profile photo of David Muegge
    David Muegge
    Participant

    Hello, I am creating a module that wraps a REST API and having an issue with pipeline support.

    I have a get function like so...

    function Get-Volume{
    [CmdletBinding(DefaultParameterSetName='AllVolumes')]
    param ( [Parameter(Mandatory=$true, 
                        ValueFromPipeline=$true,
                        Position=0,
                        ParameterSetName='VolByName')]
            [Alias('n')] 
            [string]$Name,
            [Parameter(Mandatory=$true,
                        ParameterSetName='VolByIndex')]
            [Alias('i')] 
            [string]$ID
    )
        Begin{
            $UriObject = 'volumes'
        }
        Process{
            # Return details of volume names passed by parameter or pipeline
            if($Name){
                $UriString = ($UriObject + '/?name=' + $Name)
                Invoke-RestMethod -Method Get -Uri ($Global:APIBaseUri + $UriString) -Headers $Global:APIHeaders).content
            }
        }
        End{
            # Return detail of specific volume by ID
            if($ID){
                $UriString = ($UriObject + '/' + $ID)
            }
            # No parameters passed return details of all volumes
            if($PSCmdlet.ParameterSetName -eq 'AllVolumes'){
                $UriString = ($UriObject + '/')
                (Get-StorageItem -UriString $UriObject).$UriObject | ForEach-Object{(Invoke-RestMethod -Method Get -Uri ($Global:APIBaseUri + $UriString + '?name=' + ($_.Name)) -Headers $Global:APIHeaders).Content}    
            }
        }
    } # Get-Volume
    

    And a set finction like so...

    function Set-Volume{
    [CmdletBinding()]
    param ( [Parameter(Mandatory=$true,ParameterSetName='VolUpdateByName',
                        ValueFromPipeline=$true, 
                        ValueFromPipelineByPropertyName=$true)]
            [Parameter(ParameterSetName='VolNameUpdate')]
            [Parameter(ParameterSetName='VolSizeUpdate')]
            [string]$Name,
            [Parameter(Mandatory=$true,ParameterSetName='VolUpdateByIndex')]
            [Parameter(ParameterSetName='VolNameUpdate')]
            [Parameter(ParameterSetName='VolSizeUpdate')]
            [string]$ID,
            [Parameter(Mandatory=$false,ParameterSetName='VolNameUpdate')]
            [ValidateNotNullOrEmpty()]
            [string]$NewVolName,
            [Parameter(Mandatory=$false,ParameterSetName='VolSizeUpdate')]
            [ValidateNotNullOrEmpty()]
            [string]$NewVolSize
    )
        Begin{
            $UriObject = 'volumes'
        }
        Process{
            if($Name){
                $UriString = ($UriObject + '/?name=' + $Name)
                $JSoNBody = New-Object -TypeName psobject
    
                # Optional Parameters
                if($NewVolName){$JSoNBody | Add-Member -MemberType NoteProperty -Name vol-name -Value $NewVolName}
                if($NewVolSize){$JSoNBody | Add-Member -MemberType NoteProperty -Name vol-size -Value $NewVolSize}
                Invoke-RestMethod -Method Put -Uri ($Global:APIBaseUri + $UriString) -Headers $Global:APIHeaders -Body ($JSoNBody | ConvertTo-Json)
            }
        }     
        End{
            if($ID){
                $UriString = ($UriObject + '/' + $ID)
                $JSoNBody = New-Object -TypeName psobject
    
                # Optional Parameters
                if($NewVolName){$JSoNBody | Add-Member -MemberType NoteProperty -Name vol-name -Value $NewVolName}
                if($NewVolSize){$JSoNBody | Add-Member -MemberType NoteProperty -Name vol-size -Value $NewVolSize}
                Invoke-RestMethod -Method Put -Uri ($Global:APIBaseUri + $UriString) -Headers $Global:APIHeaders -Body ($JSoNBody | ConvertTo-Json)
            }
        }
    } # Set-Volume
    

    When I do a get the following object is returned.

    vol-id                   : {f4babab7df3d426cb56fc0fdf89a1ef7, DAM01, 64}
    obj-severity             : information
    guid                     : f4babab7df3d426cb56fc0fdf89a1ef7
    index                    : 64
    naa-name                 : 
    creation-time            : 2015-09-15 01:06:41
    vol-size                 : 10485760
    name                     : DAM01
    

    I can use a the pipeline on both of the functions in the following manner

    @('DAM01','DAM02') | Get-Volume
    @('DAM01','DAM02') | Set-Volume -NewVolName 'DAM03'
    

    When I try to use the functions together like so...

    Get-Volume -Name 'DAM01' | Set-Volume -NewVolName 'DAM03'
    

    The set function does not seem to be able to pick the name property from the output of the get.
    It is trying to use the entire object as parameter input.
    Here is a snippet of output from a trace of the command above.

    ParameterBinding Information: 0 : BIND PIPELINE object to parameters: [Set-XIOVolume]
    ParameterBinding Information: 0 :     PIPELINE object TYPE = [System.Management.Automation.PSCustomObject]
    ParameterBinding Information: 0 :     RESTORING pipeline parameter's original values
    ParameterBinding Information: 0 : BIND PIPELINE object to parameters: [Out-Default]
    ParameterBinding Information: 0 :     PIPELINE object TYPE = [System.Management.Automation.ErrorRecord]
    ParameterBinding Information: 0 :     RESTORING pipeline parameter's original values
    ParameterBinding Information: 0 :     Parameter [InputObject] PIPELINE INPUT ValueFromPipeline NO COERCION
    ParameterBinding Information: 0 :     BIND arg [The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.] to parameter [InputObject]
    ParameterBinding Information: 0 :         BIND arg [The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.] to param [InputObject] SUCCESSFUL
    ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [Out-Default]
    

    What am I missing here and any suggestions on how to correct it so the set can properly identify the name property for parameter input?

    Thanks,

    Dave Muegge

  • #29776
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    ValueByPropertyName is expecting a PSObject, so what is this returning:

    (Get-StorageItem -UriString $UriObject).$UriObject | ForEach-Object{(Invoke-RestMethod -Method Get -Uri ($Global:APIBaseUri + $UriString + '?name=' + ($_.Name)) -Headers $Global:APIHeaders).Content}    
    

    Here is an example passing the values through the pipeline:

    function Get-Volume {
        [CmdletBinding(DefaultParameterSetName = 'AllVolumes')]
        param (
            [Parameter(Mandatory = $true,
                       ValueFromPipeline = $true,
                       Position = 0,
                       ParameterSetName = 'VolByName')]
            [Alias('n')]
            [string]$Name
        )
        begin { }
        process {
            New-Object -TypeName PSObject -Property @{
                Name = $Name;
            }
        }
        end { }
    }
    
    function Set-Volume {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory = $true, ParameterSetName = 'VolUpdateByName',
                       ValueFromPipelineByPropertyName = $true)]
            [Parameter(ParameterSetName = 'VolNameUpdate')]
            [Parameter(ParameterSetName = 'VolSizeUpdate')]
            [string]$Name
        )
        begin { }
        process { Write-Verbose ("Processing {0}..." -f $Name) }
        end { }
    }
    
    @('DAM01', 'DAM02') | Get-Volume | Set-Volume -Verbose
    

    Also, I would recommend naming these that is not already an existing cmdlet. If you worked for Tech Corp, maybe Get-TCVolume or something like that. If that script runs and your function Get-Volume is in memory and you wanted to call the cmdlet Get-Volume it be confusing.

  • #29781
    Profile photo of David Muegge
    David Muegge
    Participant

    Rob, Thanks for the reply

    Thanks for the advice about the function naming and I actually do have prefixes, which I removed to make the post more generic.

    The following line does return a PSCustomObject which by my understanding is the same as a PSObject just a newer version.

    Invoke-RestMethod -Method Put -Uri ($Global:APIBaseUri + $UriString) -Headers $Global:APIHeaders -Body ($JSoNBody | ConvertTo-Json)
    

    Here is an example of the type output of the function.

    (Get-XIOVolume -Name 'DAM01').GetType()
    
    IsPublic IsSerial Name                                     BaseType                                                                                                                                                                                                                                                                                   
    -------- -------- ----                                     --------                                                                                                                                                                                                                                                                                   
    True     False    PSCustomObject                           System.Object
    

    I actuall did try capturing the output and splatting to an object like so

    $ReturnObj = Invoke-RestMethod -Method Put -Uri ($Global:APIBaseUri + $UriString) -Headers $Global:APIHeaders -Body ($JSoNBody | ConvertTo-Json)
    New-Object -TypeName psobject -Property @ReturnObj
    

    This caused a conversion error and did not solve the issue.

    Any other ideas or suggestions are appreciated.

    Dave

  • #30382
    Profile photo of David Muegge
    David Muegge
    Participant

    I thought I would update this as I found my issue. The pipeline problem was actually a side affect of having a poor design of Parameter Sets.

    Tip if you are using a fairly complex setup of multiple parameter sets and having trouble with getting input from the pipeline. The issue just may be the parameter set design.

    Thanks,

    Dave

You must be logged in to reply to this topic.