{"id":15451,"date":"2011-03-01T00:01:00","date_gmt":"2011-03-01T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2011\/03\/01\/proxy-functions-spice-up-your-powershell-core-cmdlets\/"},"modified":"2024-04-02T09:53:31","modified_gmt":"2024-04-02T16:53:31","slug":"proxy-functions-spice-up-your-powershell-core-cmdlets","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/proxy-functions-spice-up-your-powershell-core-cmdlets\/","title":{"rendered":"Proxy Functions: Spice Up Your PowerShell Core Cmdlets"},"content":{"rendered":"<p><strong>Summary<\/strong>: Microsoft MVP, Shay Levy, shows how to use Windows PowerShell proxy functions to extend the capability of Windows PowerShell cmdlets.<\/p>\n<p><img decoding=\"async\" title=\"Hey, Scripting Guy! Question\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" alt=\"Hey, Scripting Guy! Question\" width=\"34\" height=\"34\" align=\"left\" border=\"0\" \/><\/p>\n<p>Hey, Scripting Guy! I need to be able to modify the behavior of existing Windows PowerShell cmdlets. Is this something that can be accomplished by using \u201cnormal\u201d tools, or do I need to reverse engineer them by using Visual Studio?<\/p>\n<p>\u2014LS<\/p>\n<p><img decoding=\"async\" title=\"Hey, Scripting Guy! Answer\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" alt=\"Hey, Scripting Guy! Answer\" width=\"34\" height=\"34\" align=\"left\" border=\"0\" \/> Hello LS,<\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, here. Today our guest blogger is Shay Levy.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/0167.HSG-3-01-11-3_6A7CB575.jpg\"><img decoding=\"async\" style=\"padding-left: 0px; padding-right: 0px; padding-top: 0px; border: 0px;\" title=\"Photo of Shay Levy\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8510.HSG-3-01-11-3_thumb_5C5DC678.jpg\" alt=\"Photo of Shay Levy\" width=\"204\" height=\"272\" border=\"0\" \/><\/a><\/p>\n<h4><\/h4>\n<p>Here is a bit of information about Shay:\nWindows PowerShell MVP\nBlog: <a href=\"http:\/\/PowerShay.com\" target=\"_blank\" rel=\"noopener\">http:\/\/PowerShay.com<\/a>\nFollow Shay on Twitter: <a href=\"https:\/\/twitter.com\/ShayLevy\" target=\"_blank\" rel=\"noopener\">https:\/\/twitter.com\/ShayLevy<\/a>\nDownload the PowerShell Community Toolbar: <a href=\"http:\/\/tinyurl.com\/PSToolbar\" target=\"_blank\" rel=\"noopener\">http:\/\/tinyurl.com\/PSToolbar<\/a><\/p>\n<p>Have you ever had a thought like, \u201cI wish that cmdlet had that parameter?\u201d<\/p>\n<p>I have had several thoughts like that one, and with proxy functions, I was able to implement them on my system. For example, take a look at a snippet that reads XML file content and casts the content to an XML document object:<\/p>\n<blockquote><p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">PS &gt; $xml = [xml](Get-Content -Path c:\\test.xml)<\/span><\/p><\/blockquote>\n<p>Wouldn&#8217;t it be nice if the <a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113310\" target=\"_blank\" rel=\"noopener\">Get-Content<\/a> Windows PowerShell cmdlet had an <i>AsXml <\/i>parameter? We could write the same command as shown here.<\/p>\n<blockquote><p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">PS &gt; Get-Content -Path c:\\test.xml -AsXml<\/span><\/p><\/blockquote>\n<p><a href=\"http:\/\/www.microsoft.com\/PowerShell\" target=\"_blank\" rel=\"noopener\">Windows PowerShell 2.0<\/a> includes a new and exciting feature called proxy commands. Proxy commands are used primarily by the remoting mechanism in Windows PowerShell. We can use it in restricted runspace configurations where we can modify sessions by adding or removing certain commands, providers, cmdlet parameters, and every other component of a Windows PowerShell session. For instance, we can remove the <i>ComputerName<\/i> parameter from all cmdlets that support that parameter.<\/p>\n<p>Another major use for proxy commands is performed in implicit remoting. In a nutshell, implicit remoting lets you use commands from a remote session inside your local session (by using the <a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=135221\" target=\"_blank\" rel=\"noopener\">Import-PSSession<\/a> cmdlet). For example, say you manage an Exchange Server 2010 server and you don&#8217;t have the Exchange Server admin tools installed on your admin computer. Normally, you would create a Remote Desktop session, launch the Exchange Management Shell, and perform your tasks.<\/p>\n<p>With implicit remoting, you can create a PSSession to the Exchange Server and import the Exchange commands into your local session. When you do that, Windows PowerShell dynamically creates a module in your TEMP folder, and the imported commands are converted to proxy functions. From now on, each time you run an Exchange command in your local session, it will run on the remote server. This feature enables us to manage multiple servers without installing admin tools locally on each server!<\/p>\n<p>So what is a proxy function? It is a wrapper function around a cmdlet (or another function). When we proxy a command, we get access to its parameters (which we can add or remove). We also get control over the command steppable pipeline, which is the three script blocks of a Windows PowerShell function: <b>Begin<\/b>, <b>Process<\/b>, and <b>End<\/b>.<\/p>\n<p>In this post, I want to show you how easy it is to create a proxy function and how you can harness its power to extend existing cmdlets with new functionality by adding new parameters.<\/p>\n<p>The process of creating a proxy function is relatively easy (you can read more about it <a href=\"https:\/\/devblogs.microsoft.com\/powershell\/extending-andor-modifing-commands-with-proxies\/\" target=\"_blank\" rel=\"noopener\">here<\/a>). You decide which cmdlet you want to wrap and then get the cmdlet metadata (command line syntax). In the following example, we get the code for the <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd347686.aspx\" target=\"_blank\" rel=\"noopener\">Get-ChildItem<\/a> cmdlet.<\/p>\n<p class=\"CodeBlockScreened\" style=\"line-height: 10pt; list-style-type: disc; margin: 15pt 17.3pt 0.25in 0in; background: white;\"><span lang=\"EN\"><span style=\"font-family: Lucida Sans Typewriter;\"><span style=\"color: #000000; font-size: 10pt;\">PS &gt; $MetaData = New-Object System.Management.Automation.CommandMetaData (Get-Command\u00a0 ConvertTo-Html)\nPS &gt; [System.Management.Automation.ProxyCommand]::Create($MetaData) <\/span><\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\" style=\"line-height: 10pt; list-style-type: disc; margin: 15pt 17.3pt 0.25in 0in; background: white;\"><span lang=\"EN\"><span style=\"font-family: Lucida Sans Typewriter;\"><span style=\"color: #000000; font-size: 10pt;\">[CmdletBinding(DefaultParameterSetName=&#8217;Items&#8217;, SupportsTransactions=$true)]\nparam(\n[Parameter(ParameterSetName=&#8217;Items&#8217;, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]\n[System.String[]]\n${Path},<\/span><\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\" style=\"line-height: 10pt; list-style-type: disc; margin: 15pt 17.3pt 0.25in 0in; background: white;\"><span lang=\"EN\"><span style=\"font-family: Lucida Sans Typewriter;\"><span style=\"color: #000000; font-size: 10pt;\">\u00a0\u00a0\u00a0 [Parameter(ParameterSetName=&#8217;LiteralItems&#8217;, Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)]\n[Alias(&#8216;PSPath&#8217;)]\n[System.String[]]\n${LiteralPath},<\/span><\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\" style=\"line-height: 10pt; list-style-type: disc; margin: 15pt 17.3pt 0.25in 0in; background: white;\"><span lang=\"EN\"><span style=\"font-family: Lucida Sans Typewriter;\"><span style=\"color: #000000; font-size: 10pt;\">\u00a0\u00a0\u00a0 [Parameter(Position=1)]\n[System.String]\n${Filter},<\/span><\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\" style=\"line-height: 10pt; list-style-type: disc; margin: 15pt 17.3pt 0.25in 0in; background: white;\"><span lang=\"EN\"><span style=\"font-family: Lucida Sans Typewriter;\"><span style=\"color: #000000; font-size: 10pt;\">\u00a0\u00a0\u00a0 [System.String[]]\n${Include},<\/span><\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\" style=\"line-height: 10pt; list-style-type: disc; margin: 15pt 17.3pt 0.25in 0in; background: white;\"><span lang=\"EN\"><span style=\"font-family: Lucida Sans Typewriter;\"><span style=\"color: #000000; font-size: 10pt;\">\u00a0\u00a0\u00a0 [System.String[]]\n${Exclude},<\/span><\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\" style=\"line-height: 10pt; list-style-type: disc; margin: 15pt 17.3pt 0.25in 0in; background: white;\"><span lang=\"EN\"><span style=\"font-family: Lucida Sans Typewriter;\"><span style=\"color: #000000; font-size: 10pt;\">\u00a0\u00a0\u00a0 [Switch]\n${Recurse},<\/span><\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\" style=\"line-height: 10pt; list-style-type: disc; margin: 15pt 17.3pt 0.25in 0in; background: white;\"><span lang=\"EN\"><span style=\"font-family: Lucida Sans Typewriter;\"><span style=\"color: #000000; font-size: 10pt;\">\u00a0\u00a0\u00a0 [Switch]\n${Force},<\/span><\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\" style=\"line-height: 10pt; list-style-type: disc; margin: 15pt 17.3pt 0.25in 0in; background: white;\"><span lang=\"EN\"><span style=\"font-family: Lucida Sans Typewriter;\"><span style=\"color: #000000; font-size: 10pt;\">\u00a0\u00a0\u00a0 [Switch]\n${Name})<\/span><\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\" style=\"line-height: 10pt; list-style-type: disc; margin: 15pt 17.3pt 0.25in 0in; background: white;\"><span lang=\"EN\"><span style=\"font-family: Lucida Sans Typewriter;\"><span style=\"color: #000000; font-size: 10pt;\">begin\n{\ntry {\n$outBuffer = $null\nif ($PSBoundParameters.TryGetValue(&#8216;OutBuffer&#8217;, [ref]$outBuffer))\n{\n$PSBoundParameters[&#8216;OutBuffer&#8217;] = 1\n}\n$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand(&#8216;Get-ChildItem&#8217;, [System.Management.Automation.Command\nTypes]::Cmdlet)\n$scriptCmd = {&amp; $wrappedCmd @PSBoundParameters }\n$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)\n$steppablePipeline.Begin($PSCmdlet)\n} catch {\nthrow\n}\n}<\/span><\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\" style=\"line-height: 10pt; list-style-type: disc; margin: 15pt 17.3pt 0.25in 0in; background: white;\"><span lang=\"EN\"><span style=\"font-family: Lucida Sans Typewriter;\"><span style=\"color: #000000; font-size: 10pt;\">process\n{\ntry {\n$steppablePipeline.Process($_)\n} catch {\nthrow\n}\n}<\/span><\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\" style=\"line-height: 10pt; list-style-type: disc; margin: 15pt 17.3pt 0.25in 0in; background: white;\"><span lang=\"EN\"><span style=\"font-family: Lucida Sans Typewriter;\"><span style=\"color: #000000; font-size: 10pt;\">end\n{\ntry {\n$steppablePipeline.End()\n} catch {\nthrow\n}\n}\n&lt;#<\/span><\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\" style=\"line-height: 10pt; list-style-type: disc; margin: 15pt 17.3pt 0.25in 0in; background: white;\"><span lang=\"EN\"><span style=\"font-family: Lucida Sans Typewriter;\"><span style=\"color: #000000; font-size: 10pt;\">.ForwardHelpTargetName Get-ChildItem\n.ForwardHelpCategory Cmdlet<\/span><\/span><\/span><\/p>\n<p class=\"CodeBlockScreened\" style=\"line-height: 10pt; list-style-type: disc; margin: 15pt 17.3pt 0.25in 0in; background: white;\"><span lang=\"EN\"><span style=\"font-family: Lucida Sans Typewriter;\"><span style=\"color: #000000; font-size: 10pt;\">#&gt;<\/span><\/span><\/span><\/p>\n<p>Don&#8217;t get intimidated by the result. This is what a proxy function looks like. As you can see, the generated code starts by including all the cmdlet parameters and then defines the script blocks of the command. At the end, there are instructions for the <a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113316\" target=\"_blank\" rel=\"noopener\">Get-Help<\/a> cmdlet.<\/p>\n<p>In the <b>Begin<\/b> block, the <b>Get-ChildItem<\/b> command ($wrappedCmd) is retrieved and the parameters are passed to it (@PSBoundParameters). A steppable pipeline is initialized ($steppablePipeline), which invokes the cmdlet ($scriptCmd), and finally the <b>Begin<\/b> starts. At the end of the code sample, you can find a multiline comment that is used to instruct the Help that <b>Get-Help<\/b> should display when you type <b>Get-Help<\/b> &lt;ProxyFunction&gt;.<\/p>\n<p>After this long introduction, it is time to introduce the proxy function that we are going to work on. For this blog, I chose to wrap the <a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113290\" target=\"_blank\" rel=\"noopener\">ConvertTo-Html<\/a> cmdlet. It can do everything that the <b>ConvertTo-Html<\/b> cmdlet is capable of with two additional enhancements: It will be able to save the generated markup language (HTML) to a file and invoke the file in the default browser.<\/p>\n<p>Consider the following command:<\/p>\n<blockquote><p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">PS &gt; <\/span><a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113324\" target=\"_blank\" rel=\"noopener\"><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">Get-Process<\/span><\/a><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\"> | <\/span><a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113387\" target=\"_blank\" rel=\"noopener\"><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">Select-Object<\/span><\/a><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\"> -Property Name,Id | ConvertTo-Html -FilePath gps.html -Invoke<\/span><\/p><\/blockquote>\n<p>Note the <i>FilePath<\/i> and <i>Invoke <\/i>parameters<i>. <\/i>These parameters are not a part of the default <b>ConvertTo-Html<\/b> parameters; they are added by our proxy function.<\/p>\n<p>If we wanted to use the default Windows PowerShell core cmdlets to achieve the same result, we would have to write it as follows:<\/p>\n<blockquote><p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">PS &gt; Get-Process | Select-Object -Property Name,Id | ConvertTo-Html | <\/span><a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113363\" target=\"_blank\" rel=\"noopener\"><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">Out-File<\/span><\/a><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\"> -FilePath gps.html\nPS &gt; <\/span><a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113345\" target=\"_blank\" rel=\"noopener\"><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">Invoke-Item<\/span><\/a><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\"> -Path gps.html <\/span><\/p><\/blockquote>\n<p>So, in essence we are creating a shortcut of some sort, with the added advantage that the proxy command is easier to read and understand. The output results for both of the previous commands is the same.<\/p>\n<p>So let&#8217;s get started. First, we need to generate the proxy command for the <b>ConvertTo-Html<\/b> cmdlet. We will redirect the code to a new file and edit the file in the Windows PowerShell Integrated Scripting Environment (ISE) as seen here.<\/p>\n<blockquote><p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">PS &gt; $MetaData = <\/span><a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113355\" target=\"_blank\" rel=\"noopener\"><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">New-Object<\/span><\/a><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\"> System.Management.Automation.CommandMetaData (<\/span><a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113309\" target=\"_blank\" rel=\"noopener\"><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">Get-Command<\/span><\/a><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\"> ConvertTo-Html)\nPS &gt; [System.Management.Automation.ProxyCommand]::Create($MetaData) | Out-File -FilePath C:\\ConvertTo-Html.ps1\nPS &gt; ise C:\\ConvertTo-Html.ps1<\/span><\/p><\/blockquote>\n<p>The Windows PowerShell ISE is shown here with the code generated by the previous command.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4212.HSG-3-01-11-01_6BB12887.jpg\"><img decoding=\"async\" style=\"padding-left: 0px; padding-right: 0px; padding-top: 0px; border: 0px;\" title=\"Image of code\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1663.HSG-3-01-11-01_thumb_4EE78DA5.jpg\" alt=\"Image of code\" width=\"604\" height=\"604\" border=\"0\" \/><\/a><\/p>\n<p>Now let&#8217;s add a new parameter to the parameters section in the script file. (The two new parameters <i>FilePath <\/i>and <i>Invoke<\/i> are shown in bold text.)<\/p>\n<blockquote><p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">[ValidateNotNullOrEmpty()]\n[System.String[]]\n${PreContent},<\/span><\/p>\n<p><b><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">[ValidateNotNullOrEmpty()]\n[System.String]\n${FilePath},<\/span><\/b><\/p>\n<p><span style=\"font-family: Lucida Sans Typewriter;\"><span style=\"font-size: x-small;\"><b>[Switch]\n${Invoke}\n<\/b>) <\/span><\/span><\/p><\/blockquote>\n<p>The main logic of the following code is inside the <b>Begin<\/b> script block (comments are inline, and code modifications are in bold text).<\/p>\n<blockquote><p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">begin\n{\ntry {\n$outBuffer = $null\nif ($PSBoundParameters.TryGetValue(&#8216;OutBuffer&#8217;, [ref]$outBuffer))\n{\n$PSBoundParameters[&#8216;OutBuffer&#8217;] = 1\n}\n# make sure we are wrapping a core cmdlet by specifiyng the command qualified name\n$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand(<b>&#8216;Microsoft.PowerShell.Utility\\<\/b>ConvertTo-Html&#8217;, [System.Management.Automation.CommandTypes]::Cmdlet)<\/span><\/p>\n<p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">\u00a0\u00a0\u00a0 # remove the FilePath parameter if it was specified, otherwise it will get to the core cmdlet and we&#8217;ll get an error\n<\/span><span style=\"font-family: Lucida Sans Typewriter;\"><span style=\"font-size: x-small;\"><b>if($PSBoundParameters[&#8216;FilePath&#8217;])\n{\n$null = $PSBoundParameters.Remove(&#8216;FilePath&#8217;)<\/b>\n# redirect the output to the a file\n<b>$scriptCmd = {&amp; $wrappedCmd @PSBoundParameters | Out-File $FilePath}<\/b>\n# if Invoke has been specified &#8211; remove it and invoke the file\n<\/span><\/span><span style=\"font-family: Lucida Sans Typewriter;\"><span style=\"font-size: x-small;\"><b>if($PSBoundParameters[&#8216;Invoke&#8217;])\n{\n$null = $PSBoundParameters.Remove(&#8216;Invoke&#8217;)\nInvoke-Item $FilePath\n}\n}\nelse\n{\n$scriptCmd = {&amp; $wrappedCmd @PSBoundParameters }\n} <\/b>\n$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)\n$steppablePipeline.Begin($PSCmdlet)\n} catch {\nthrow\n}\n} <\/span><\/span><\/p><\/blockquote>\n<p>Finally, the code is wrapped in a function declaration that has the same name as the cmdlet, as shown here:<\/p>\n<blockquote><p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">function ConvertTo-Html\n{<\/span><\/p>\n<p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">\u00a0\u00a0\u00a0 proxy function generated code<\/span><\/p>\n<p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">}<\/span><\/p><\/blockquote>\n<p>By choosing the same name as the cmdlet, we can take advantage of the command discovery process in Windows PowerShell. Basically, if you have a cmdlet and a function with the same name, the function takes precedence. Here is the order of command precedence that is used by the command discovery process in Windows PowerShell:<\/p>\n<p>\u00b7 <b>Alias<\/b>:<b> <\/b>All Windows PowerShell aliases in the current session<\/p>\n<p>\u00b7 <b>Filter\/Function<\/b>: All Windows PowerShell functions<\/p>\n<p>\u00b7 <b>Cmdlet<\/b>: The cmdlets in the current session (&#8220;Cmdlet&#8221; is the default)<\/p>\n<p>\u00b7 <b>ExternalScript<\/b>:<b> <\/b>All .ps1 files in the paths that are listed in the <b>Path<\/b> environment variable ($env:PATH)<\/p>\n<p>\u00b7 <b>Application<\/b>:<b> <\/b>All non-Windows-PowerShell files in paths that are listed in the <b>Path<\/b> environment variable<\/p>\n<p>\u00b7 <b>Script<\/b>:<b> <\/b>Script blocks in the current session<\/p>\n<p>There is one gotcha to using the same name. If you need to call the core <b>ConvertTo-Html<\/b> cmdlet, you need to specify its module\/snap-in fully qualified name. For the <b>ConvertTo-Html<\/b> cmdlet, that name is seen here.<\/p>\n<blockquote><p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">PS &gt; Microsoft.PowerShell.Utility\\ConvertTo-Html &#8230;.<\/span><\/p><\/blockquote>\n<p>More than likely, you will not know what that module\/snap-in fully qualified name is. Luckily, you can use Windows PowerShell to get the full name of a cmdlet by using the following command.<\/p>\n<blockquote><p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">PS &gt; $cmd = Get-Command -Name ConvertTo-Html\nPS &gt; <\/span><a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113347\" target=\"_blank\" rel=\"noopener\"><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">Join-Path<\/span><\/a><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\"> -Path $cmd.ModuleName -ChildPath $cmd.Name\nMicrosoft.PowerShell.Utility\\ConvertTo-Html <\/span><\/p><\/blockquote>\n<p>The same technique applies for getting Help. To get the Help for the core cmdlet and specify its full name, you would use the command shown here.<\/p>\n<blockquote><p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">PS &gt; Get-help Microsoft.PowerShell.Utility\\ConvertTo-Html<\/span><\/p><\/blockquote>\n<p>If you do not like this behavior, you can give the proxy function a different name.<\/p>\n<p>Another gotcha occurs when you are using the <b>Get-Help<\/b> cmdlet. By default, <b>Get-Help<\/b> displays the Help content of a cmdlet, but if we have two commands with the same name, <b>Get-Help<\/b> lists the two commands (note the <b>Category<\/b> column):<\/p>\n<blockquote><p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">PS &gt; Get-Help ConvertTo-Html <\/span><\/p>\n<p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">Name Category Synopsis<\/span><\/p>\n<p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">&#8212;- &#8212;&#8212;&#8211; &#8212;&#8212;&#8211;<\/span><\/p>\n<p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">ConvertTo-Html Function ConvertTo-Html [[-Property] ] [[-Head] ] [[-Title] ] [[-Body] &lt; browser.&gt;<\/span><\/p>\n<p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">ConvertTo-Html Cmdlet\u00a0\u00a0 Converts Microsoft .NET Framework objects into HTML that can be&#8230;<\/span><\/p><\/blockquote>\n<p>To load the proxy command into your session, simply \u201cdot-source\u201d it as follows.<\/p>\n<blockquote><p><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">PS &gt; . C:\\ConvertTo-Html.ps1<\/span><\/p><\/blockquote>\n<p>Now, let us try the command that we used previously with the new proxy command.<\/p>\n<blockquote><p><a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113324\" target=\"_blank\" rel=\"noopener\"><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">Get-Process<\/span><\/a><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\"> | <\/span><a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113387\" target=\"_blank\" rel=\"noopener\"><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\">Select-Object<\/span><\/a><span style=\"font-family: Lucida Sans Typewriter; font-size: x-small;\"> -Property Name,Id | ConvertTo-Html -FilePath gps.html -Invoke<\/span><\/p><\/blockquote>\n<p>You can see that it works great, as shown here.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/5873.HSG-3-01-11-2_63A96058.jpg\"><img decoding=\"async\" style=\"padding-left: 0px; padding-right: 0px; padding-top: 0px; border: 0px;\" title=\"Image of HTML table\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/5488.HSG-3-01-11-2_thumb_1317CF25.jpg\" alt=\"Image of HTML table\" width=\"604\" height=\"742\" border=\"0\" \/><\/a><\/p>\n<p>That is it. We now have a proxy function that wraps a cmdlet with additional functionality. You can download the function file <a href=\"http:\/\/blogs.microsoft.co.il\/blogs\/scriptfanatic\/ConvertTo-Html.zip\" target=\"_blank\" rel=\"noopener\">here<\/a>.<\/p>\n<p>With regard to the <i>AsXml<\/i> parameter mentioned earlier\u2026I already have this functionality in my profile, but I strongly recommend that you file a suggestion on <a href=\"https:\/\/connect.microsoft.com\/PowerShell\/\" target=\"_blank\" rel=\"noopener\">Microsoft&#8217;s Connect<\/a> website so we all can get the benefit of having new parameters in future releases of Windows PowerShell. (Here&#8217;s <a href=\"https:\/\/connect.microsoft.com\/PowerShell\/feedback\/details\/632433\/new-get-content-parameter-for-xml-content-support\" target=\"_blank\" rel=\"noopener\">my suggestion<\/a> for <i>AsXml <\/i>support\u2014feel free to add your vote.) Until then, we can add this kind of functionality to core Windows PowerShell cmdlets right now!<\/p>\n<p>LS, that is all there is to using Windows PowerShell proxy functions to extend the capabilities of Windows PowerShell cmdlets. Thank you, Shay, for your hard work and for sharing it with us. Guest Blogger Week will continue tomorrow when Rob Campbell will join us.<\/p>\n<p>I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\" rel=\"noopener\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\" rel=\"noopener\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"mailto:scripter@microsoft.com\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\" rel=\"noopener\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><b>Ed Wilson, Microsoft Scripting Guy<\/b><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Microsoft MVP, Shay Levy, shows how to use Windows PowerShell proxy functions to extend the capability of Windows PowerShell cmdlets. Hey, Scripting Guy! I need to be able to modify the behavior of existing Windows PowerShell cmdlets. Is this something that can be accomplished by using \u201cnormal\u201d tools, or do I need to reverse [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[56,3,4,224,45],"class_list":["post-15451","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-guest-blogger","tag-scripting-guy","tag-scripting-techniques","tag-shay-levy","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Microsoft MVP, Shay Levy, shows how to use Windows PowerShell proxy functions to extend the capability of Windows PowerShell cmdlets. Hey, Scripting Guy! I need to be able to modify the behavior of existing Windows PowerShell cmdlets. Is this something that can be accomplished by using \u201cnormal\u201d tools, or do I need to reverse [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/15451","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=15451"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/15451\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=15451"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=15451"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=15451"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}