Get-CommandPlugin

Get-CommandPlugin

  • Comments 1

One of the nifty new CTP3 features is command and parameter meta data on functions. In V1, you had to parse a function yourself to extract the parameters. In CTP2, you could use the tokenizer API to parse the function, but extracting parameters this was is error prone. In CTP3, and in V2, you can actually get a dictionary of parameters on the FunctionInfo object.

Check it out:

Get-Command -type Function | Foreach-Object { $_.Parameters }

 

This opens up a lot of possibilities. One of the possibilities I really like is that you can now use the parameter metadata to determine if two functions fit together. This means that I can use a number of different functions as plugins for another function.

One example of how you might want to use this is making a central script that runs all of your diagnostic scripts.

Suppose I had several functions that worked against a remote machine:
Get-InformationAboutProcessor -computer
Get-InformationAboutFreeDiskSpace -computer
Get-InformationAboutLocalUsers -computer
etc etc etc

If I had a core function, Get-Information, which had a parameter -computer, then I can consider any command named Get-InformationAbout* with a parameter name of computer a potential plugin to Get-Information. I could have downloaded the scripts from several locations, and put them in many different modules, and, as long as they were there when I ran Get-Information, the same command would collect the data. Get-Information could then do the summarization of the information and send me an email with the information.

Thanks to splatting (which gives you a dictionary of all of the parameters to the current function ($psBoundParameters) and allows you to "splat" any dictionary to the dictionary provides the arguments to another command), writing a command like Get-Information is trivial. In fact, it is only one pipeline:

    function Get-Information($computer) {
        $myInvocation.MyCommand |
            Get-CommandPlugin -preposition About -parameter Computer |
            Foreach-Object { & $_ @psBoundParameters }
    }

 

For more developer-oriented types, this should remind you of an interface, but it's actually quite a bit cooler in my opinion. First and foremost, you don't have to go out of your way to use a command signature as an interface, you just simply write the command. You also don't have to compile the interface, which saves time and code space. Another perk is that while an interface states that a property is of a certain type, this technique allows PowerShell to coerce the data to a different type in each plugin command. But the really interesting possibility is what the plugins can do with the bound parameters. If the command plugin is a simple function, or has a parameter that takes ValueFromPipelineByRemainingArgs, then the function will be able to get all of the parameters passed to the parent function. I find that this is a good way to write functions you expect others to extend.

This final possibility is both empowering and risky. The empowering example is that you could write the plugin function to use more of the main functions parameters, which does something interfaces never could do cleanly: provide additional information. The risky example is that if the main function has a parameter including sensitive information (let's say credentials), then the underlying functions would have this information as well. As such, I would recommend only using Get-CommandPlugin with plugins you know, trust, and have inspected, or with parameters that do not contain sensitive information.

I'm reminded of a quote from a Marvel Movie: "With Great Power Comes Great Responsibility." Get-CommandPlugin has great power. Please script responsibly.


Synopsis:

Finds all of the commands that have a signature similar to command

 


Detailed Description:

Searches all of the commands for a similar signature to a base command
The signature of a command will be considered to be the command name + a keyword + additional text +
correct parameter names
For example, if you have a function, Get-Code -URL, then the plugins to Get-Code -preposition From would be:
Get-CodeFromSourceA -url
Get-CodeFromSourceB -url
etc
By using this command, it is possible to essential use a script and it's parameters as an ad-hoc interface.
This means you can write a command which does some operation, and then searches the currently loaded commands
for any plugins.

 


Here's Get-CommandPlugin:

function Get-CommandPlugin {
           
        <#
        .Synopsis
            Finds all of the commands that have a signature similar to command
        .Description
            Searches all of the commands for a similar signature to a base command
            The signature of a command will be considered to be the command name + a keyword + additional text + 
            correct parameter names
            For example, if you have a function, Get-Code -URL, then the plugins to Get-Code -preposition From would be:
            Get-CodeFromSourceA -url
            Get-CodeFromSourceB -url
            etc
            By using this command, it is possible to essential use a script and it's parameters as an ad-hoc interface.
            This means you can write a command which does some operation, and then searches the currently loaded commands
            for any plugins.
        .Example
            # Declares a Get-Code function, which finds all plugins and passes its parameters down to those commands
            # also declares Get-CodeFromA, which will print "hi" and the $url, and Get-CodeFromB, which will prnt "bye" and the $url
             function Get-Code($url) { 
                $myInvocation.MyCommand |
                    Get-CommandPlugin -preposition "From" |
                    Foreach-Object {
                        & $_ @psBoundParameters
                    }
            }

            function Get-CodeFromA($url) {
                "hi"
                $url
            }
            function Get-CodeFromB($url) {
                "bye"
                $url
            }           
        .Link
            Get-Command
                    
        #>
        param(
        [Parameter(ValueFromPipeline=$true,
            Position=0)]
        $command,        
        [Parameter(Position=1)]
        [Alias("Keyword")]
        [string]
        $preposition,
        
        [Parameter(Position=2)]
        [string[]]
        $parameters
        )
process {

            if ($command -isnot [Management.Automation.CommandInfo]) {
                $realC = Get-Command $command | Select-Object -First 1
            } else {
                $realC = $command
            }
            if (-not $parameters) {
                $parameters = $realC.Parameters.Keys
            }
            Get-Command "$realC$preposition*" | Where-Object {
                $potentialPlugin = $_
                $isPlugin = $false
                foreach ($p in $parameters) {
                    if ($potentialPlugin.Parameters.Keys -notcontains $p) {
                        return $false
                    }
                }
                return $true
            }
        
}

}
    

 


Automatically generated with Write-CommandBlogPost

Leave a Comment
  • Please add 2 and 6 and type the answer here:
  • Post
  • On our internal discussion list, someone just asked about how to pass switch parameters from one function

Page 1 of 1 (1 items)