Processing parameters in a function

This topic contains 4 replies, has 3 voices, and was last updated by Profile photo of Mark Brown Mark Brown 1 year, 8 months ago.

  • Author
    Posts
  • #31912
    Profile photo of Mark Brown
    Mark Brown
    Participant

    I have written a simple function. It takes a computer name, and then in the Process {} block I use Invoke-Command to run an action on each machine. My parameter is created like this:
    Param(
    [Parameter(valuefrompipeline=$true,Position=0)]
    [string[]]$ComputerName=$ThisServer,

    So I can supply a single name, a list of names or get a list from the pipeline.
    However, it appears that things get processed in different ways when they get to the Process{} block. A single name is OK, as is input from the pipeline. But a list of machine names fails. Is this because the list is an array, and this is handled differently? How do get round this in a simple way?

    Thanks

  • #31913
    Profile photo of Ed Grant
    Ed Grant
    Participant

    Can you post the rest of your code? Just be sure to sanitize it so it doesn't contain any private information. It would be useful to see what the Function actually does instead of just seeing the Parameter.

  • #31914
    Profile photo of Mark Brown
    Mark Brown
    Participant

    Here you go Ed, nothing special.....

    Function Stop-GEOService
    {

    [CmdletBinding()]
    Param(
    [Parameter(valuefrompipeline=$true)]
    [string[]]$ComputerName=($servername.keys | %{if($servername.$_ -eq $env:computername){$_}})
    )
    Begin
    {
    "Preparing to stop service..."
    }
    Process
    {
    $geoServer= $($serverName[$($ComputerName)])
    "Processing: $ComputerName System Name: $geoServer"
    try
    {
    Invoke-Command -ComputerName $geoServer { Stop-Service -DisplayName "GEO*" -Confirm} -ErrorAction stop
    Add-VSToolsLog -LogFile VSTools.log -LogText " services were stopped on $ComputerName"
    }
    Catch
    {
    Write-Host "An error occured"
    $errorMessage = $_.Exception.Message
    Write-Host $errorMessage
    Add-VSToolsLog -LogFile VSTools.log -LogText "An error occurred trying to Stop GEO services for $ComputerName, the error was $errorMessage"
    }
    }
    End
    {
    }
    }

    Thanks for your help.

  • #31918
    Profile photo of Curtis Smith
    Curtis Smith
    Participant

    SideNote: If you use the "pre" and "/pre" html tags around your code, it will put it in a pretty format like below, making it much easier t read in the post.

    Here is an example of your issue.

    Function Test {
        [CmdLetBinding()]
        Param(
            [Parameter(valuefrompipeline=$true)]
            [String[]]$ComputerName
        )
        Begin{}
        Process {
            Write-Host $ComputerName
        }
        End {}
    }
    
    $objects = @(
        [PSCustomObject]@{ComputerName = "Test1"; Date = (Get-Date).AddDays(-1)},
        [PSCustomObject]@{ComputerName = "Test2"; Date = (Get-Date).AddDays(-10)},
        [PSCustomObject]@{ComputerName = "Test3"; Date = (Get-Date).AddDays(-20)}
    )
    
    $objects | test
    write-host "------------------------------------"
    test -ComputerName $objects
    

    Results:

    @{ComputerName=Test1; Date=11/12/2015 08:21:25}
    @{ComputerName=Test2; Date=11/03/2015 08:21:25}
    @{ComputerName=Test3; Date=10/24/2015 08:21:25}
    ------------------------------------
    @{ComputerName=Test1; Date=11/12/2015 08:21:25} @{ComputerName=Test2; Date=11/03/2015 08:21:25} @{ComputerName=Test3; Date=10/24/2015 08:21:25}
    

    The reason for this difference is that when you are using the Pipeline, the Process{} script block acts like a foreach loop for the pipeline input, so each input object in processed one at a time. The Process{} block only does this for pipeline input. So when you pass the objects as a parameter, there is nothing looping through the objects individually to process them one at a time. Instead they are being processed as one single array of objects. Thus the result is different.

    In order to handle multiple object as input by a parameter instead of via the pipeline, you need to add a foreach loop to the code like this.

    Function Test {
        [CmdLetBinding()]
        Param(
            [Parameter(valuefrompipeline=$true)]
            [String[]]$ComputerName
        )
        Begin{}
        Process {
            ForEach ($Computer in $ComputerName) {
                Write-Host $Computer
            }
        }
        End {}
    }
    
    $objects = @(
        [PSCustomObject]@{ComputerName = "Test1"; Date = (Get-Date).AddDays(-1)},
        [PSCustomObject]@{ComputerName = "Test2"; Date = (Get-Date).AddDays(-10)},
        [PSCustomObject]@{ComputerName = "Test3"; Date = (Get-Date).AddDays(-20)}
    )
    
    $objects | test
    write-host "------------------------------------"
    test -ComputerName $objects
    

    Results:
    @{ComputerName=Test1; Date=11/12/2015 08:28:32}
    @{ComputerName=Test2; Date=11/03/2015 08:28:32}
    @{ComputerName=Test3; Date=10/24/2015 08:28:32}
    ————————————
    @{ComputerName=Test1; Date=11/12/2015 08:28:32}
    @{ComputerName=Test2; Date=11/03/2015 08:28:32}
    @{ComputerName=Test3; Date=10/24/2015 08:28:32}

  • #31927
    Profile photo of Mark Brown
    Mark Brown
    Participant

    Great, thanks for the help.

You must be logged in to reply to this topic.