loading...

Powershell & The Pipeline

amacc profile image Amacc ・3 min read

So in my experience in working in a complex it environment is that it doesn't
take that long before you see a number of scripts like this floating around.

$computers = import-csv somecomputerlist.csv

foreach ($computer in $computers) { 
    if(Test-Connection $computer.ComputerName){
        # Do something with the computer here
    }
}

Then I might be asked to give some guidance on where
Then someone tasked with a cleanup effort might be pointed to me for guidance
on the effort. So I guess its definitely a good first start, they are at
least pulling data from a file instead of hardcoding it into the script.

Most of the time my suggestion is to move it to a function so that we can get
some reuse out of this bad boy. Then a few days a request comes in to
review some changes.

Function Process-Computers {
    param ([string[]]$computers)
    foreach ($computer in $computers) { 
        if(Test-Connection $computer.ComputerName){
            # Do something with the computer here
        }
    }
}

So I guess this is a good start, we can dot source the file then do something
like the following.

Process-Computers ( Get-Content $SomeFilePath )

Get-ChildItem $computerReportsDirectory | 
    %{ Process-Computers (Get-Content $_ }

But we could do better. So where do we go from here?

The Pipeline

Powershell functions are a little unique when
compared with other languages, they have the ability to declare a proces loop.
This process loop is integrated into the parameters block to pull pieces of
data off of the incoming items. So how would we use this?

Function Process-Computers{
    param(
        [Parameter(ValueFromPipelineByPropertyName)]
        [string]$ComputerName
    )
    process{ 
        if(Test-Connection $ComputerName){
            # Do something with the computer here
        }
    }
}

Wait a minute where did the loop go? So to explain that first we need to
talk about the pipeline. So we can think of the pipeline operator as a device
that lets us redirect the output of one command into the input of another.
So with a command like

gci \\some\share | test-path

we would think of it as taking the output of listing a remote directory and
running the test-path command on them.

This is integrated into the way that
functions work in powershell in 2 ways, the parameters and the process block.

Parameters

The pipeline integration with the parameters comes in the form of the Parameter
variable decorator options. There are 2 options that we have available
ValueFromPipelineByPropertyName and ValueFromPipeline. The first will take the
object and assign it to that variable and the latter will set itself to a
reference of the inputs parameter.

Function SomeFunction{
    param(
        [Parameter(ValueFromPipeline)]
        $Input,
        [Parameter(ValueFromPipelineByPropertyName)
        [string]$Name
    )
    process{
        $Name -eq $Input.Name
    }
}

Process Block

Then in the above statement the process block will act similar to the body of
a loop, placing an array of items one by one through the process block. Take
note that in the parameter we still can type the variable, so the name above
would be of type string when used in the process block. This does mean that
if the name field actually was an object it would be cast to a string using.

This then allows for a wide array of use cases.

Import-CSV serverlist |
    Process-Computers

invoke-restmethod http://cmdb.link/servers | # Query for the results
    select -expandproperty results |         # Expand the results array
    Process-Computers

Posted on by:

amacc profile

Amacc

@amacc

DevOps Practitioner with a love for software development.

Discussion

markdown guide