Time to read: ~ 4 minutes
tl;dr: Save your command as a [scriptblock] variable and then use .InvokeReturnAsIs method
Update: 2018-04-24 Added examples of using simple functions as well.
I am starting to believe that I’ve gone through 3 phases of returning information from the PowerShell console:
Write-Host to return information
which, while returning what you want, just returns them to the screen. Not much formatting, can’t be used in a pipeline, etc, etc.
Write-Information to return information
Now, while this is better, it still suffers from the same problem as before with the results being individual lines and not really connecting with each other. So we move on to the 3rd option…
(3). Using [PSCustomObject] to return information
Finally we have output that is an object! So it is connected, and can be used later on in the pipeline, etc.
Making It Flat
That was instrumental in moving from
PSCustomObject as it helped to “flatten” my code.
And, if we look at our PSCustomObject code, it’s not exactly flat is it?
I can let it go once but the script that I was working on called the PSCustomObject in 5 or 6 different places. That’s too much to leave it as it is. It needs to be flattened.
So what can we do with this?…
I mean, it runs fine; nothing wrong there:
Let us try and do exactly what we just said. Take our command out as a string and try to run it later on.
Luckily, we can take advantage of the
Invoke-Expression cmdlet on our string to run it exactly how it is! (
Get-Help Invoke-Expression -Full)
Now, our [PSCustomObject] is only 5 lines….
But I don’t really like this.
If there is a PowerShell version of SQL Injection, this feels like it would be rife with problems. Is there anything else that we can do?
How about we don’t store it as a string, but instead store it as the type that it should be; a scriptblock.
Put our code in curly brackets, define the type and we’re good to go! (
Get-Help about_Script_Blocks -Full)
IT WORKS!…and yet…
Invoke-Command -Script $Script is awfully long. Isn’t there a better way? Well there was in the help file, using the ampersand (
This…also…works… but dammit, who’s going to know what that means? I want this to be readable and easy to troubleshoot!
Hmmm, reading from the help file on
For more information about the call operator, see about_Operators
Okay so, let’s
Get-Help about_Operators -Full then!
Runs a command, script, or script block. The call operator, also known as
the “invocation operator,” lets you run commands that are stored in
variables and represented by strings. Because the call operator does not
parse the command, it cannot interpret command parameters.
Well that wasn’t much help.
It’s also called an “invocation operator” huh? If only there was something like an
.Invoke() method for this guy, then I could do something like the following:
That works??!! How does that work, I made it up?!
Well, it slightly works… Why are there curly brackets around my results? Why does that even work at all? Why can’t I just return the results as is?
Ah damn, I’ve committed one of the cardinal sins of PowerShell. I haven’t run
$Script | Get-Member)
Seriously? It’s actually called
InvokeReturnAsIs? Let’s try it out
Perfect! That’s exactly what I wanted! Clear, concise, readable, easy to troubleshoot, and flat!
After publishing this, I got informed of another way that I thought was a great addition; functions (thank you Mr purplemonkeymad, whoever you are)
With the added benefit that I can include comment based help on the function!
In the end
This was a strange day figuring this out but now that I know I can invoke commands in PSCustomObject objects, I feel like I’ll be able to do a lot more with my PowerShell.
I’m self-documenting this here for later! 🙂