Link to home
Start Free TrialLog in
Avatar of ryanmaves
ryanmaves

asked on

Help with PowerShell $PSBoundParameters

My script below works and that should satisfy it's purpose, however, I am trying to learn more about what is happening.

In doing so I have put Write-Verbose within an If/Then process so that if the computer name is passed as a parameter it should Write-Verbose that it is passed as a parameter. However, if passed as a script, it should Write-Verbose that it is passed through pipeline.

Why in every example does the computer name(s) pass through as parameter??
Get-Content .\list.txt | Start-ReportPSCompliant -Verbose

Open in new window


That should pass the computer names through the cmdlet as pipeline and the Write-Verbose "$_ is going through pipeline" should be printed, but no matter how I use the cmdlet it always passes through as parameter.

Why is it not using the pipeline Else{} script??

function Start-ReportPSCompliant {
<#
.Synopsis
   This reports on computers as to whether or not they have policy ready for PSRemoting by invoking a command to return the environment computer name. If it doesn't return then PSRemoting is not enabled or the device is unavailable.
.DESCRIPTION
   Enter in a single computer name or multiple computer names. Value from pipeline is also
   acceptable.
.EXAMPLE
   Start-ReportPSCompliant Server1

.EXAMPLE
   Start-ReportPSCompliant -Computer 'computer1','computer2'

.EXAMPLE
   Get-Content C:\Scripts\list.txt | Start-ReportPSCompliant 

.EXAMPLE
   Start-ReportPSCompliant (Get-Content C:\Scripts\list.txt)
#>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [String[]]$Computer = 'localhost'
    )
    Begin {
        Write-Verbose 'Looking for existing reports and removing files if found'
        $Pa = Test-Path C:\Scripts\temp\compliant.txt

            if ($Pa) {Remove-Item C:\Scripts\temp\compliant.txt; Write-Verbose 'compliant.txt list was removed'}
            else {Write-Verbose 'compliant.txt list does not exist'}

        $Pa = Test-Path C:\Scripts\temp\noncompliant.txt

            if ($Pa) {Remove-Item C:\Scripts\temp\noncompliant.txt; Write-Verbose 'noncompliant.txt list was removed'}
            else {Write-Verbose 'noncompliant.txt list does not exist'}

    }
    Process {
        if ($PSBoundParameters.ContainsKey('computer')) {
             # we have parameter input
        Write-Verbose "$Computer is going through as parameter input"
             foreach ($c in $Computer) {
                CompliantWorker $c
             }
        }else{
             # we have pipeline input
        Write-Verbose "$_ is going through pipeline"
             CompliantWorker $_
        }
    }
    End {
        Write-Verbose 'The operation is complete'
        $Pa = Test-Path C:\Scripts\temp\compliant.txt

            if ($Pa) {notepad C:\Scripts\temp\compliant.txt}
            else {Write-Warning 'No computers in list were compliant!'}

        $Pa = Test-Path C:\Scripts\temp\noncompliant.txt
        
            if ($Pa) {notepad C:\Scripts\temp\noncompliant.txt}
            else {Write-Verbose 'All computers in list were compliant!'}
    }
}
function CompliantWorker {
    param (
        $Computer
    )
        Write-Verbose "Attempting to send a remote command to $Computer"
        Invoke-Command $Computer {$env:COMPUTERNAME} -ErrorAction SilentlyContinue -ErrorVariable err
        
            if($err.count -gt 0) {
                Write-Output $Computer | Out-File C:\Scripts\temp\noncompliant.txt -Append
            }
            else {
               $Computer | Out-File C:\Scripts\temp\compliant.txt -Append
            }
}

Open in new window

Avatar of footech
footech
Flag of United States of America image

The parameter is defined to take pipeline input, so it is being used either way.  If you want you can examine $MyInvocation (or $PsCmdlet.MyInvocation) and look at the PipelineLength property.  If it's greater than 1 then you're using the pipeline.  This is true of you're using something like
Get-Content .\list.txt | Start-ReportPSCompliant -Verbose
but not something like
"server1","server2" | Start-ReportPSCompliant -Verbose

You've written your script to handle pipeline input.  It shouldn't matter whether you're actually using the pipeline or not.
Avatar of ryanmaves
ryanmaves

ASKER

Thanks for the response footech. You're right, it shouldn't matter, but I don't understand why I'm not seeing my Verbose message that says my input is going through pipeline as opposed to parameter when I do in fact use the line

Get-Content .\list.txt | Start-ReportPSCompliant -Verbose

Should I not expect to see the Verbose message from the Else{} block?

if ($PSBoundParameters.ContainsKey('computer')) {
             # we have parameter input
        Write-Verbose "$Computer is going through as parameter input"
             foreach ($c in $Computer) {
                CompliantWorker $c
             }
        }else{
             # we have pipeline input
        Write-Verbose "$_ is going through pipeline"
             CompliantWorker $_
        }

Open in new window

Nope.  As I mentioned, the parameter is being used either way.  So whether you feed it values via the command line directly or the pipeline, $Computer is the receptacle.
Okay, I see what you are saying. My question then would be, what about my script has caused the parameter to handle pipeline input as well?

FYI. I got this script trick of $PSBoundParameters from Don Jones at 3:19:00 time frame in this youtube video below:
https://www.youtube.com/watch?v=7fFEV8xawx0

I guess I am trying to understand, do I have unnecessary lines in my script. I can't wrap my head around why the correct Verbose message isn't being used here. Unless, that means it's unnecessary script that can be removed, but why did Don Jones use it?

Thanks for entertaining my curiosity.
ASKER CERTIFIED SOLUTION
Avatar of footech
footech
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks for being thorough footech. You are right, turns out I didn't even need the If/Then statement in my code. The process simply is a foreach and sends it to my worker process. Everything still works the same.

Thanks for the lesson.

    Process {
        foreach ($c in $Computer) { CompliantWorker $c }
        }

Open in new window

Note, without the foreach statement having it send to my worker, there does rise a problem when calling computernames into the parameter such as it doesn't process them individually, but tries to do it all at once. This causes all the computernames to be added to my non-compliant list because they are all considered to have an ($err.count -gt 0).

Thanks again!