{"id":4861,"date":"2009-01-04T11:55:00","date_gmt":"2009-01-04T11:55:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/powershell\/2009\/01\/04\/extending-andor-modifing-commands-with-proxies\/"},"modified":"2019-02-18T13:12:58","modified_gmt":"2019-02-18T20:12:58","slug":"extending-andor-modifing-commands-with-proxies","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/powershell\/extending-andor-modifing-commands-with-proxies\/","title":{"rendered":"Extending and\/or Modifing Commands with Proxies"},"content":{"rendered":"<p>There are so many powerful features in V2, it is hard to know where to begin.&nbsp; This one is going to blow to top of your head off when you understand what it enables you to do.<\/p>\n<p>In this blog, I talk about Proxy Cmdlets which is the ability for one <strike>Cmdlet<\/strike> to call another.&nbsp; You could always do this but to do it <strong>right<\/strong> has always been very difficult.&nbsp; In particular, what you want to have happen is to be able to control the execution of the calling command &#8211; to control when it&#8217;s BEGINPROCESS(), PROCESSRECORD(), ENDPROCESS(), etc methods are called so that it behave with the correct behavior.&nbsp; What you needed was a &#8220;Steppable Pipeline&#8221;.&nbsp; You know have that in CTP3.&nbsp; Now the bit-biters will want to explore steppable pipelines to great detail as they can be both powerful and complex &#8211; BUT the thing I want to make clear is that EVERYONE can and should use them to great effect by simply following the recipe I&#8217;m going to provide you in this blog and with the attached Module.<\/p>\n<p><strong><\/strong>&nbsp;<\/p>\n<p><strong>Scenario 1:&nbsp; Removing Functions<br \/><\/strong>Image the case where you are <a href=\"http:\/\/blogs.msdn.com\/powershell\/archive\/2008\/12\/24\/configuring-powershell-for-remoting-part-1.aspx\">Configuring PowerShell for Remoting<\/a> and you are setting up an ConfigurationName which will give people access to some but not all commands.&nbsp; You might want to also further restrict the commands&nbsp; you do provide.&nbsp; <\/p>\n<p><strong><\/strong>&nbsp;<\/p>\n<p><strong>Scenario 2: Adding Functions<br \/><\/strong>In V2 CTP3 we added the -FileVersionInfo and -Module parameters to Get-Process (did you know that?&nbsp; Stop a minute and go give them a try &#8211; they are pretty cool\/useful.&nbsp; try this:&nbsp; gps powershell -FileVersionInfo |fl * ).&nbsp; With proxies &#8211; you could do this yourself very easily.<\/p>\n<p><strong><\/strong>&nbsp;<\/p>\n<p><strong>Concept<br \/><\/strong>Let&#8217;s take a minute and get things into focus.&nbsp; PowerShell Cmdlets are .NET classes with attributes on the properties.&nbsp; WHY?&nbsp; Because that allows us to reflect on them and EXTRACT ITS METADATA (its grammar).&nbsp; We then use that grammar to drive a command command line syntax.&nbsp; This is the magic behind PowerShell.&nbsp; You&#8217;ll see us do all sorts of wonderful things with this in the future (e.g. early-bound WebService interface, emit earlybound C# accessors, autogenerate GUI front ends, etc., etc.).&nbsp; The big things we did in V2 is <\/p>\n<blockquote>\n<p>1) We <strong>expose<\/strong> the metadata.&nbsp; You can do a New-Object on System.Management.Automation.CommandMetaData passing it cmdletInfo and get it&#8217;s metadata.&nbsp; Try this: <br \/><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">&nbsp;&nbsp;&nbsp; PS&gt; New-Object System.Management.Automation.CommandMetaData (gcm Get-Process)<\/font><\/p>\n<p>2) We make the metadata <strong>programmable<\/strong>.&nbsp; You can add\/remove parameters, change the parameters, change the name, etc.<\/p>\n<p>3) We use metadata to emit a script Cmdlet. <br \/><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">&nbsp;&nbsp;&nbsp; PS&gt; $metaData = New-Object System.Management.Automation.CommandMetaData (gcm Get-Process) <br \/>&nbsp;&nbsp;&nbsp; PS&gt; [System.Management.Automation.ProxyCommand]::create($MetaData) <\/font><\/p>\n<p>&nbsp;<\/p>\n<\/blockquote>\n<p>&nbsp;<\/p>\n<p>Developers and advanced scripters are going to be all over that like white on rice but that can get to be chewy stuff for novice scripters.&nbsp; Does that mean that Novice scripters won&#8217;t use this?&nbsp; ABSOLUTELY NOT!&nbsp; With PowerShell, people produce abstractions to enable other groups of people.&nbsp; Advanced scripters often produce abstractions which enable lower skilled scripters to do things they would not have been able to do on their own.&nbsp; That is what this blog is about.&nbsp; I&#8217;ve attached a MetaProgramming Module which (hopefully) make this a pretty simple task.<\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">PS&gt; <strong>Import-Module MetaProgramming <\/p>\n<p><\/strong>PS&gt; <strong>Get-Command -Module MetaProgramming <\/strong><\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">CommandType&nbsp;&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Definition <br \/>&#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;- <br \/>Function&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; New-ParameterAttribute&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8230; <br \/>Function&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; New-ProxyCommand&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8230;<\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\"><br \/>PS&gt; <strong>Get-Help New-ProxyCommand -Detailed<\/strong> <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">NAME <br \/>&nbsp;&nbsp;&nbsp; New-ProxyCommand <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">SYNOPSIS <br \/>&nbsp;&nbsp;&nbsp; Generate a script for a ProxyCommand to call a base Cmdlet adding or re <br \/>&nbsp;&nbsp;&nbsp; moving parameters. <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">SYNTAX <br \/>&nbsp;&nbsp;&nbsp; New-ProxyCommand [[-Name] [&lt;String&gt;]] [[-CommandType] [&lt;CommandTypes&gt;]] <br \/>&nbsp;&nbsp;&nbsp;&nbsp; [[-AddParameter] [&lt;String[]&gt;]] [[-RemoveParameter] [&lt;String[]&gt;]] [[-Ad <br \/>&nbsp;&nbsp;&nbsp; dParameterAttribute] [&lt;Hashtable&gt;]] [&lt;CommonParameters&gt;] <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">DETAILED DESCRIPTION <br \/>&nbsp;&nbsp;&nbsp; This command generates command which calls another command (a ProxyComm <br \/>&nbsp;&nbsp;&nbsp; and). <br \/>&nbsp;&nbsp;&nbsp; In doing so, it can add additional attributes to the existing parameter <br \/>&nbsp;&nbsp;&nbsp; s. <br \/>&nbsp;&nbsp;&nbsp; This is useful for things like enforcing corporate naming standards. <br \/>&nbsp;&nbsp;&nbsp; It can also ADD or REMOVE parameters.&nbsp; If you ADD a parameter, you&#8217;ll h <br \/>&nbsp;&nbsp;&nbsp; ave <br \/>&nbsp;&nbsp;&nbsp; to implement the semantics of that parameter in the code that gets gene <br \/>&nbsp;&nbsp;&nbsp; rated. <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">PARAMETERS <br \/>&nbsp;&nbsp;&nbsp; -Name <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Name of the Cmdlet to proxy. <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">&nbsp;&nbsp;&nbsp; -CommandType <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Type of Command we are proxying.&nbsp; In general you dont&#8217; need to spec <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ify this but <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; it becomes necessary if there is both a cmdlet and a function with <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; the same <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">&nbsp;&nbsp;&nbsp; -AddParameter <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List of Parameters you would like to add. NOTE &#8211; you have to edit t <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; he resultant <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; code to implement the semantics of these parameters.&nbsp; ALSO &#8211; you ne <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ed to remove <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; them from $PSBOUND <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">&nbsp;&nbsp;&nbsp; -RemoveParameter <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">&nbsp;&nbsp;&nbsp; -AddParameterAttribute <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">&nbsp;&nbsp;&nbsp; &lt;CommonParameters&gt; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This cmdlet supports the common parameters: -Verbose, -Debug, <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -ErrorAction, -ErrorVariable, -WarningAction, -WarningVariable, <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -OutBuffer and -OutVariable. For more information, type, <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;get-help about_commonparameters&#8221;. <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; EXAMPLE 1 &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">&nbsp;&nbsp;&nbsp; New-ProxyCommand get-process -typ all -RemoveParameter FileVersionInfo, <br \/>&nbsp;&nbsp;&nbsp; Module,ComputerName -AddParameter SortBy &gt; c:\\ps\\get-myprocess.ps1 <br \/>REMARKS <br \/>&nbsp;&nbsp;&nbsp; To see the examples, type: &#8220;get-help New-ProxyCommand -examples&#8221;. <br \/>&nbsp;&nbsp;&nbsp; For more information, type: &#8220;get-help New-ProxyCommand -detailed&#8221;. <br \/>&nbsp;&nbsp;&nbsp; For technical information, type: &#8220;get-help New-ProxyCommand -full&#8221;.<\/font><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>So let&#8217;s try a simple example to see how easy it can be:<\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">PS&gt;New-ProxyCommand get-process -RemoveParameter ID &gt; .\\get-myprocess.ps1 <br \/>PS&gt;.\\Get-MyProcess -Name lsass <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">Handles&nbsp; NPM(K)&nbsp;&nbsp;&nbsp; PM(K)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WS(K) VM(M)&nbsp;&nbsp; CPU(s)&nbsp;&nbsp;&nbsp;&nbsp; Id ProcessName <br \/>&#8212;&#8212;-&nbsp; &#8212;&#8212;&nbsp;&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8211; &#8212;&#8211;&nbsp;&nbsp; &#8212;&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212; &#8212;&#8212;&#8212;&#8211; <br \/>&nbsp;&nbsp; 1126&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 17&nbsp;&nbsp;&nbsp;&nbsp; 5796&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 11744&nbsp;&nbsp;&nbsp; 37&nbsp;&nbsp;&nbsp; 97.64&nbsp;&nbsp;&nbsp; 488 lsass <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">PS&gt;.\\Get-MyProcess -ID 488 <br \/><font color=\"#ff0000\">C:\\temp\\get-myprocess.ps1 : A parameter cannot be found that matches parame <br \/>ter name &#8216;ID&#8217;. <br \/>At line:1 char:20 <br \/>+ .\\Get-MyProcess -ID &lt;&lt;&lt;&lt;&nbsp; 488 <br \/>&nbsp;&nbsp;&nbsp; + CategoryInfo&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : InvalidArgument: (:) [get-myprocess.ps1], Pa <br \/>&nbsp;&nbsp; rameterBindingException <br \/>&nbsp;&nbsp;&nbsp; + FullyQualifiedErrorId : NamedParameterNotFound,get-myprocess.ps1<\/font><\/font><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>Adding parameter is a bit more complicated but if you follow the steps &#8211; it is pretty straightforward (It might look intimidating but trust me &#8211; just do it a couple times and you&#8217;ll see that is is super simple.).&nbsp; How do you know what the steps are?&nbsp; I put it into the script.&nbsp; If you the following command:<\/p>\n<p><font face=\"Consolas\" color=\"#0000ff\" size=\"2\">PS&gt; New-ProxyCommand get-process -AddParameter SortBy &gt; .\\get-myprocess.ps1 <br \/>PS&gt; Powershell_ise .\\get-myprocess.ps1 <br \/><\/font><\/p>\n<p>This is what you&#8217;ll see at the top of that file:<\/p>\n<p><font face=\"Consolas\" color=\"#008000\" size=\"2\">&lt;# <br \/>You are responsible for implementing the logic for added parameters.&nbsp; These <br \/>parameters are bound to $PSBoundParameters so if you pass them on the the <br \/>command you are proxying, it will almost certainly cause an error.&nbsp; This logic <br \/>should be added to your BEGIN statement to remove any specified parameters <br \/>from $PSBoundParameters. <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#008000\" size=\"2\">In general, the way you are going to implement additional parameters is by <br \/>modifying the way you generate the $scriptCmd variable.&nbsp; Here is an example <br \/>of how you would add a -SORTBY parameter to a cmdlet: <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#008000\" size=\"2\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ($SortBy) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Void]$PSBoundParameters.Remove(&#8220;SortBy&#8221;) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $scriptCmd = {&amp; $wrappedCmd @PSBoundParameters |Sort-Object -Property $SortBy} <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }else <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $scriptCmd = {&amp; $wrappedCmd @PSBoundParameters } <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br \/>################################################################################&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br \/>New ATTRIBUTES <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ($SortBy) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Void]$PSBoundParameters.Remove(&#8220;SortBy&#8221;) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <\/font><\/p>\n<p><font face=\"Consolas\" color=\"#008000\" size=\"2\">################################################################################ <br \/>#&gt;<\/font><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>NOTE &#8211; The New-ProxyCommand code should emit a HELP template for the proxy but I have not had time to go implement that.&nbsp; Sorry.<\/p>\n<p>Enjoy!<\/p>\n<p>Jeffrey Snover [MSFT] <br \/>Windows Management Partner Architect <br \/>Visit the Windows PowerShell Team blog at:&nbsp;&nbsp;&nbsp; <a href=\"http:\/\/blogs.msdn.com\/PowerShell\">http:\/\/blogs.msdn.com\/PowerShell<\/a> <br \/>Visit the Windows PowerShell ScriptCenter at:&nbsp; <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/hubs\/msh.mspx\">http:\/\/www.microsoft.com\/technet\/scriptcenter\/hubs\/msh.mspx<\/a><\/p>\n<p><a href=\"https:\/\/msdnshared.blob.core.windows.net\/media\/MSDNBlogsFS\/prod.evol.blogs.msdn.com\/CommunityServer.Components.PostAttachments\/00\/09\/27\/14\/34\/MetaProgramming.zip\">MetaProgramming.zip<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>There are so many powerful features in V2, it is hard to know where to begin.&nbsp; This one is going to blow to top of your head off when you understand what it enables you to do. In this blog, I talk about Proxy Cmdlets which is the ability for one Cmdlet to call another.&nbsp; [&hellip;]<\/p>\n","protected":false},"author":600,"featured_media":13641,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[137,210,217,270,278],"class_list":["post-4861","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-powershell","tag-ctp3","tag-jeffrey-snover","tag-modules","tag-powershell-v2","tag-proxy"],"acf":[],"blog_post_summary":"<p>There are so many powerful features in V2, it is hard to know where to begin.&nbsp; This one is going to blow to top of your head off when you understand what it enables you to do. In this blog, I talk about Proxy Cmdlets which is the ability for one Cmdlet to call another.&nbsp; [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/4861","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/users\/600"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/comments?post=4861"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/4861\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media\/13641"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media?parent=4861"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/categories?post=4861"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/tags?post=4861"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}