Right after you have been utilizing PowerShell for a while, you’ll commence discovering the want to create your personal tools and commands. Typically, your commands are centered on a PowerShell command that you want to adjust to meet your requirements. Perhaps you want to add a new parameter, modify an existing parameter, or take outcomes from a command and combine them with anything else. This can be a very tedious procedure if you attempt to build your own command from scratch. As you have probably surmised, I’m here to help. Very first, let’s look at two approaches on how to create your own PowerShell command.
A proxy command is usually utilised to customize the behavior of a provided cmdlet, usually by removing parameters. This is most frequently accomplished in scenarios exactly where you have created a delegated and restricted remote-endpoint, and you not only want to limit what commands can be run, but also what parameters can be employed. Typically the proxy command utilizes the exact same name as the original command and passes parameters to it.
You can generate a proxy function manually by 1st making a CommandMetaData object.
$ cmd = New-Object System.Management.Automation.CommandMetaData (Get-Command Get-Service)
This object is a snapshot of the command, its parameters, and settings.
This object has a technique that will generate a proxy command.
$ proxy = [System.Management.Automation.ProxyCommand]::Produce($ cmd)
The proxy code looks like this:
[CmdletBinding(DefaultParameterSetName='Default', HelpUri='http://go.microsoft.com/fwlink/?LinkID=113332', RemotingCapability='SupportedByCommand')] param( [Parameter(ParameterSetName='Default', Position=, ValueFromPipeline=$ correct, ValueFromPipelineByPropertyName=$ true)] [Alias('ServiceName')] [string] $ Name, [Parameter(ValueFromPipelineByPropertyName=$ accurate)] [Alias('Cn')] [ValidateNotNullOrEmpty()] [string] $ ComputerName, [Alias('DS')] [switch] $ DependentServices, [Alias('SDO','ServicesDependedOn')] [switch] $ RequiredServices, [Parameter(ParameterSetName='DisplayName', Mandatory=$ accurate)] [string] $ DisplayName, [ValidateNotNullOrEmpty()] [string] $ Include, [ValidateNotNullOrEmpty()] [string] $ Exclude, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$ true)] [ValidateNotNullOrEmpty()] [Program.ServiceProcess.ServiceController] $ InputObject) commence attempt $ outBuffer = $ null if ($ PSBoundParameters.TryGetValue('OutBuffer', [ref]$ outBuffer)) $ PSBoundParameters['OutBuffer'] = 1 $ wrappedCmd = $ ExecutionContext.InvokeCommand.GetCommand('Get-Service', [Method.Management.Automation.CommandTypes]::Cmdlet) $ scriptCmd = & $ wrappedCmd @PSBoundParameters $ steppablePipeline = $ scriptCmd.GetSteppablePipeline($ myInvocation.CommandOrigin) $ steppablePipeline.Begin($ PSCmdlet) catch throw approach attempt $ steppablePipeline.Procedure($ _) catch throw end try $ steppablePipeline.Finish() catch throw <# .ForwardHelpTargetName Get-Service .ForwardHelpCategory Cmdlet #>
All you have to do is wrap it in a function scriptblock and modify as needed. Personally, I like to clean up parameter definitions and remove the characters from the parameter variables.
By contrast, a wrapper function normally focuses on a single cmdlet, but it can be customized to meet a distinct need. For example, you may want to adjust parameters, or you may well want to additional process the benefits. Many of the tools you will develop will most most likely rely on wrapper functions.
Here is a wrapper function I wrote for Get-History.
Function Get-MyHistory [CmdletBinding()] Param( [Parameter(Position=, ValueFromPipeline=$ correct)] [ValidateRange(1, 9223372036854775807)] [long]$ Id, [Parameter(Position=1)] [ValidateRange(, 32767)] [int]$ Count ) Begin out-string) #commence Process Pick ID,Commandline,StartExecutionTime,EndExecutionTime #procedure Finish Create-Verbose "Ending $ ($ MyInvocation.Mycommand)" #finish #finish function Get-MyHistory
I wanted some thing that would give me history but weed out the duplicates. Even so, I want my function to be as comprehensive as attainable with aid and all of the identical parameters as Get-History. But I don’t want to have to manually generate all of that content. Rather, I make PowerShell do the function for me.
I developed a function I call Copy-Command, which grew out of earlier efforts to streamline the creation of proxy functions. You can use the ProxyCommand class to get other sorts of details from a command, where you can even grab individual components if you want.
[Technique.Management.Automation.ProxyCommand]::GetCmdletBindingAttribute($ cmd) [Technique.Management.Automation.ProxyCommand]::GetParamBlock($ cmd)
This indicates that I can construct a new command on the fly, which includes copying the original help as a comment block.
I produced Copy-Command, so I could easily generate a wrapper command or a proxy function.
#calls for -version four. <# copy a PowerShell command such as parameters and support to turn it into a wrapper or a proxy function run this in the PowerShell ISE for greatest final results #> Function Copy-Command <# .Synopsis Copy a PowerShell command .Description This command will copy a PowerShell command, including parameters and aid to a new user-specified command. You can use this to generate a "wrapper" function or to easily develop a proxy function. The default behavior is to create a copy of the command comprehensive with the original comment-based aid block. For greatest final results, run this in the PowerShell ISE so that the copied command will be opened in a new tab. .Parameter Command The name of a PowerShell command, preferably a cmdlet but that is not a requirment. You can specify an alias and it will be resolved. .Parameter NewName Specify a name for your copy of the command. If no new name is specified, the original name will be employed. .Parameter IncludeDynamic The command will only copy explicitly defined parameters unless you specify to incorporate any dynamic parameters as properly. If you copy a command and it appears to be missing parameters, re-copy and consist of dynamic parameters. .Parameter AsProxy Create a conventional proxy function. .Parameter UseForwardHelp By default the copy procedure will create a comment-primarily based support block with the original command's aid which you can then edit to meet your requirements. Or you can opt to retain the forwarded aid links to the original command. .Example PS C:> Copy-Command Get-Procedure Get-MyProcess Generate a copy of Get-Approach referred to as Get-MyProcess. .Example PS C:> Copy-Command Get-Eventlog -asproxy -useforwardhelp Develop a proxy function for Get-Eventlog and use forwarded assist hyperlinks. .Example PS C:> Copy-Command Get-ADComputer Get-MyADComputer -includedynamic Produce a wrapper function for Get-ADComputer referred to as Get-MyADComputer. Due to the way the Active Directory cmdlets are written, most parameters appear to be dynamic so you need to include dynamic parameters otherwise there will be no parameters in the final function. .Notes Last Updated: 5/18/2015 Version : .9 Discover a lot more about PowerShell: http://jdhitsolutions.com/weblog/important-powershell-resources/ **************************************************************** * DO NOT USE IN A PRODUCTION Environment Until YOU HAVE TESTED * * Thoroughly IN A LAB Environment. USE AT YOUR Own Threat. IF * * YOU DO NOT Realize WHAT THIS SCRIPT DOES OR HOW IT Functions, * * DO NOT USE IT Outdoors OF A Secure, TEST SETTING. * **************************************************************** .Hyperlink Get-Command .Inputs None .Outputs [string] #> [cmdletbinding()] Param( [Parameter(Position=,Mandatory=$ Accurate,HelpMessage="Enter the name of a PowerShell command")] [ValidateNotNullorEmpty()] [string]$ Command, [Parameter(Position=1,HelpMessage="Enter the new name for your command using Verb-Noun convention")] [ValidateNotNullorEmpty()] [string]$ NewName, [switch]$ IncludeDynamic, [switch]$ AsProxy, [switch]$ UseForwardHelp ) Attempt Create-Verbose "Acquiring command metadata for $ command" $ gcm = Get-Command -Name $ command -ErrorAction Quit #allow an alias or command name if ($ gcm.CommandType -eq 'Alias') $ cmdName = $ gcm.ResolvedCommandName else $ cmdName = $ gcm.Name Create-Verbose "Resolved to $ cmdName" $ cmd = New-Object Program.Management.Automation.CommandMetaData $ gcm Catch Create-Warning "Failed to create command metadata for $ command" Write-Warning $ _.Exception.Message if ($ cmd) format-table -AutoSize Create-Verbose "Ending $ ($ MyInvocation.MyCommand)" #end Copy-Command Set-Alias -Name cc -Value Copy-Command
One more reason I wrote this is since I was obtaining an issue creating proxy commands of the Active Directory cmdlets like Get-ADUser. It turns out that in those cmdlets a lot of of the parameters are defined as dynamic parameters. These kind of parameters are not detected when creating a proxy command. My answer was to retrieve the dynamic parameters from Get-Command and add them to my $ cmd object, which would eventually be copied to my new command.
if ($ IncludeDynamic) Create-Verbose "Adding dynamic parameters" $ params = $ gcm.parameters.GetEnumerator()
The function’s default behavior is to copy and paste the assist as a comment, which you can then edit. If you prefer, you can inform the function to use the forwarded support hyperlinks. The whole goal of this command is to duplicate an existing command with minimal work. The function operates greatest in the ISE as your new command will automatically be opened in a new tab.
copy-command -Command Get-Eventlog -NewName Get-MyEventlog
All I have to do now is refine the function to do what I require. All of the grunt function of typing aid, parameters, and scriptblocks is automatically completed for me.
I hope you’ll let me know what you consider about this. If I’m doing anything you don’t quite understand, please post a query in the comments. I will be using this in a quantity of upcoming articles to create new commands to solve a handful of difficulties. I hope you’ll keep tuned.