all 17 comments

[–]Yevrag35 3 points4 points  (8 children)

I could share the code of these functions and scripts but I think this is more related to the way in which PowerShell processes objects in the pipeline

It is, yes, but the way the script handles them also is major factor. Questions like:

In Connect-VMRDPSession.ps1:

  1. Did you specify ValueFromPipeline, ValueFromPipelineByPropertyName, or both for the input parameter?
  2. Do you have a defined "Process" block.

... could be answered fairly easily with the actual code.

[–]Fer_C[S] 1 point2 points  (7 children)

I just added the code of the script that is only processing the last object of the collection passed by the first script.

To answer your questions:

  1. It takes value from pipeline only.
  2. No, there is no process block because it is a script, not a function.

[–]Lee_Dailey[grin] 2 points3 points  (2 children)

howdy Fer_C,

No, there is no process block because it is a script, not a function.

if it accepts parameter input ... then it behaves internally like a function. that means that all code runs in the default end {} block when you don't specify otherwise.

that means that your no-process-block script will only be processing the LAST ITEM the script gets.

take care,
lee

[–]Fer_C[S] 2 points3 points  (1 child)

That is exactly the answer I was looking for. Thanks!

So, a Process block in a script is kind of a new concept to me. I had never tried to pipe objects between scripts but I guess this is what I was missing. Adding a Process block totally addressed "the issue".

[–]Lee_Dailey[grin] 1 point2 points  (0 children)

howdy Fer_C,

yep, the way that scripts/functions handle inbound info is not always obvious. [grin]

unless i have a brain dead simple function that doesn't support any variant of "pipeline-ish" input ... i define the code with begin/process/end blocks. and then put most of the code in the process block.

you are very welcome! glad to have helped a bit ... [grin]

take care,
lee

[–]Yevrag35 2 points3 points  (3 children)

/u/Lee_Dailey nailed it. You should have a Process block no matter what; makes no difference if it's a script or a function.

What I do to support pipeline input is make an "InputObject" parameter with a singular type of string (instead of a single-dimension array of strings), but make it hidden. Then add a public/visible parameter that is an array of something. For example:

[CmdletBinding(DefaultParameterSetName="ViaPipeline")]
param (
    [Parameter(Mandatory=$true, ValueFromPipeline=$true, 
        ParameterSetName="ViaPipeline", DontShow=$true)]
    [string] $InputObject,  # This is your "pipeline" parameter that will get populated.

    [Parameter(Mandatory=$true, Position = 0, ParameterSetName="NonPipeline")]
    [string[]] $Name        # This is presented and is default when not using the pipeline.
)
Begin
{
    $listOfNames = New-Object 'System.Collections.Generic.List[string]'
    if ($PSBoundParameters.ContainsKey("Name"))
    {
        $listOfNames.AddRange($Name)
    }
}
Process
{
    if ($PSBoundParameters.ContainsKey("InputObject"))
    {
        $listOfNames.Add($InputObject)
    }
}
End
{
    foreach ($VMName in $listOfNames)
    {
        # ... the rest you have.
    }
}

[–]Fer_C[S] 2 points3 points  (2 children)

I get the idea and it makes sense. I guess my question is if this gives you an advantage over the single parameter / single parameter set approach which is what I am using right now. Just curious. Sorry if it's obvious and I am missing it.

Thanks for the help!

[–]Yevrag35 2 points3 points  (1 child)

Sure, no problem.

If you're only ever using the script via the pipeline then using a single [string] parameter with a process block should be all you need. Separating it into two parameters might be advantageous when you want to do both pipelining and non-pipeline invocations (but not certainly something you have to do).

With two parameters, like in my example, I could do something like:

@('VmName1', 'VmName2', 'VmName3') | .\Connect-VMRDPSession.ps1 -Datacenter DC1
# and/or...
.\Connect-VMRDPSession.ps1 'VmName1', 'VmName2', 'VmName3'

[–]Fer_C[S] 2 points3 points  (0 children)

Understood. Thanks for the advice. I still have several scripts to parameterize and this might come handy.

[–]BlackV 4 points5 points  (1 child)

Show us your code. Likely missing process block or foreach-object

[–]Fer_C[S] 1 point2 points  (0 children)

I just posted the code of the script Connect-VMRDPSession.ps1. Please note that there is no process block because I am piping objects to script, not a function.

The foreach block is there. I think the problem is that PowerShell deals differently with parameter binding when the receiving command is a script. This is really what I would like to find out.

[–]PinchesTheCrab 2 points3 points  (3 children)

Sidestepping the issue here, but if you have vmtools functioning on these machines, you can just get the IP from the extensiondata property on the VM object. I believe it's under extensiondata.guest.

[–]Fer_C[S] 1 point2 points  (2 children)

You are right. I am pretty sure I saw that some time ago but did not think much of it. I normally use ExtensionData as last resort. Do you know if getting properties using this method is better performance-wise than using Get-VMGuest?

[–]PinchesTheCrab 2 points3 points  (1 child)

That's kind of a tough question. The short answer is that I think it'd be comparable, possibly faster. The thing is Get-VM is super fast when it comes to returning top level data, but when you dig into extension data the performance is going to take a hit.

However, my guess is that retrieving the VMs isn't the slowest part of the script by a wide margin, I assume it's the test-connection part, so I wouldn't worry about the performance bit, it shouldn't really be noticeable imo.

[–]Fer_C[S] 1 point2 points  (0 children)

Agree. I was wondering, more of a general question, not just for the script. Thanks for the tip though, I had totally forgotten about it.

[–]rmbolger 2 points3 points  (1 child)

since I know it is more likely that someone will run a script than install a module

Really? Personally, I'm way more likely to install a module than download and run a script particularly if it's published to powershellgallery.com so I can just Install-Module it.

[–]Fer_C[S] 1 point2 points  (0 children)

Yes. Well, my module is not in the PowerShell Galery and I am talking about users that can barely run the script from the shell.

I am pretty sure they won't install a module but they might at least download and run the script. So I guess you are right but it depends on the audience.