{"id":16751,"date":"2010-10-21T00:01:00","date_gmt":"2010-10-21T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2010\/10\/21\/packaging-net-framework-classes-into-windows-powershell-functions\/"},"modified":"2010-10-21T00:01:00","modified_gmt":"2010-10-21T00:01:00","slug":"packaging-net-framework-classes-into-windows-powershell-functions","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/packaging-net-framework-classes-into-windows-powershell-functions\/","title":{"rendered":"Packaging .NET Framework Classes into Windows PowerShell Functions"},"content":{"rendered":"<p>&nbsp;<\/p>\n<p><strong>Summary:<\/strong> Learn how to correctly package .NET Framework classes into reusable Windows PowerShell advanced functions in this step-by-step solution.<\/p>\n<p>&nbsp;<\/p>\n<p><img decoding=\"async\" height=\"34\" width=\"34\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" align=\"left\" alt=\"Hey, Scripting Guy! Question\" border=\"0\" title=\"Hey, Scripting Guy! Question\" \/>Hey, Scripting Guy! I have been exploring the Microsoft .NET Framework on MSDN and attempting to convert some examples I have found there into Windows PowerShell code. I will admit that it can be somewhat tedious and I find myself getting confused if the example is too complex. It seems that when I do get a sample working, I have a script that works but it is pretty much single purpose. If I want to be able to do something else I have to write another script. Is there a better approach that I should be taking?<\/p>\n<p>&nbsp;<\/p>\n<p>&#8212; BR<\/p>\n<p>&nbsp;<\/p>\n<p><img decoding=\"async\" height=\"34\" width=\"34\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" align=\"left\" alt=\"Hey, Scripting Guy! Answer\" border=\"0\" title=\"Hey, Scripting Guy! Answer\" \/>Hello BR, Microsoft Scripting Guy Ed Wilson here. I am glad you have started exploring the .NET Framework classes. In my mind, the ability to use the .NET Framework from inside a Windows PowerShell script is one of the great strengths of Windows PowerShell. It means that we are not limited to the 236 cmdlets that are included with and are preloaded&nbsp; in <a href=\"http:\/\/support.microsoft.com\/kb\/968929\">Windows PowerShell 2.0<\/a>. In fact, with a bit of work (sometimes with lots of work) you can wrap these .NET Framework classes into advanced functions that behave just like the Windows PowerShell cmdlets that are included with Windows. These functions could then be placed in a <a href=\"http:\/\/blogs.technet.com\/heyscriptingguy\/archive\/tags\/getting+started\/modules\/default.aspx\">module<\/a> to facilitate deployment. I asked James Brundage to share his thoughts on how to correctly package .NET Framework classes to promote ease of reuse.<\/p>\n<p>James Brundage is the founder of Start-Automating (<a href=\"http:\/\/www.start-automating.com\/\">http:\/\/www.start-automating.com<\/a>), a company dedicated to saving people time and money by helping them automate. Start-Automating offers Windows PowerShell training and custom Windows PowerShell and .NET development.&nbsp; James formerly worked on the Windows PowerShell team at Microsoft, and is the author of the <a href=\"http:\/\/code.msdn.microsoft.com\/PowerShellPack\">PowerShellPack<\/a> which is a collection of Windows PowerShell scripts to enable building user interfaces, interact with RSS feeds, retrieve system information, and more. You can follow James on Twitter. He is @JamesBru.<\/p>\n<p>&nbsp;<\/p>\n<h4>A Series on Splatting (and Stuff)<\/h4>\n<h5>Part 4: Splatting and the .NET Framework<\/h5>\n<p>Welcome back to A Series on Splatting (and Stuff).&nbsp; In previous posts, we&rsquo;ve covered how to build commands that wrap other commands with splatting and building bridges between two commands with splatting.&nbsp; Today, we&rsquo;ll learn how to apply those skills to make some usable Windows PowerShell scripts to replace some hard-to-use .NET Framework classes.<\/p>\n<p>Even though Windows PowerShell is built on top of the .NET Framework, I believe that using Windows PowerShell hides away its .NET Framework origins within the cmdlets. This makes things miles easier for everyone (yourself included) to understand than using the .NET Framework classes directly.&nbsp; Unfortunately, there is a lot of stuff surfaced in the .NET Framework universe that is not put into really clean Windows PowerShell cmdlets (at least not yet). This means that if you write enough Windows PowerShell scripts, you will eventually come across something that can only be achieved by resorting to directly manipulating .NET Framework classes. <\/p>\n<p>Because you&rsquo;re reading this series and this blog, and have made it to this point, I&rsquo;m going to assume that you are worth your salt.&nbsp; I&rsquo;m going to go out on a limb and guess that you still might prefer to think of things in Windows PowerShell&rsquo;s verb-noun parlance (<b>Get-Process<\/b>) instead of C# coding conventions (<b>System.Diagnostics.Processes.GetProcesses()<\/b>).<\/p>\n<p>Today, we&rsquo;ll walk through turning .NET commands into easy to use Windows PowerShell functions, by using splatting and some other stuff we&rsquo;ve talked about this week.<\/p>\n<p>Splatting is especially great here because most interactions with .NET can be thought of as creating an object, setting lots of properties, and invoking methods.&nbsp; You can put creating an object and setting its properties in a private function and you can put whatever things that you would need to do before and after you run the method inside a another function, and you can use splatting to call these two functions for many different operations.<\/p>\n<p>We&rsquo;ll learn how to do this by using <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.net.webclient.aspx\">System.Net.Webclient<\/a>. The <b>WebClient<\/b> .NET Framework class lets you upload or download data to the web. It is the key to interacting with web sites, FTP sites, and REST web services from Windows PowerShell.<\/p>\n<p><b>System.Net.Webclient<\/b> is actually a great candidate, because it has lots of operations that are very similar and several properties that do something useful.<\/p>\n<p>Let&#8217;s start off by creating a new instance of the webclient class. To do this, use the <b>New-Object<\/b> Windows PowerShell cmdlet as shown here.<\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-family: courier new,courier\">$webClient = New-Object Net.Webclient<\/span><\/p>\n<p>Now let&rsquo;s see what methods and properties might be interesting by piping the newly created object to the <b>Get-Member<\/b> Windows PowerShell cmdlet and the <b>Out-GridView<\/b> cmdlet as seen here. <\/p>\n<blockquote>\n<div class=\"code\">\n<p><span style=\"font-family: courier new,courier\">$webClient | <br \/>&nbsp;&nbsp;&nbsp; Get-Member | <br \/>&nbsp;&nbsp;&nbsp; Out-GridView<\/span><\/p>\n<\/div>\n<\/blockquote>\n<p>There are a lot there, but I&#8217;d say the operations that are most interesting are the download\/upload operations: <b>DownloadData<\/b> \/ <b>DownloadString<\/b> \/ <b>DownloadFile<\/b> and <b>UploadData<\/b> \/ <b>UploadString<\/b> \/ <b>UploadFile<\/b> \/ <b>UploadValues<\/b>.<\/p>\n<p>In the properties, there are many more we will use with any of the operations.&nbsp; There are the credentials to use, and a collection of parameters to use when a web request is made, and a collection of headers to use. All this information is documented on MSDN.<\/p>\n<p>I won&#8217;t lead you on. Making a good command out of this will be a bit of work but the techniques that you learn today will help you any time that you have to make similar commands.<\/p>\n<p>The first step will be creating good Windows PowerShell parameters for each .NET property we could end up using in any scenario.&nbsp; The properties we are interested in are:<\/p>\n<ul>\n<li><strong>BaseAddress<\/strong><\/li>\n<li><strong>Credentials<\/strong><\/li>\n<li><strong>Headers<\/strong><\/li>\n<li><strong>QueryString<\/strong><\/li>\n<li><strong>UseDefaultCredentials<\/strong><\/li>\n<\/ul>\n<p>Windows PowerShell 2.0 introduced a new parameter for the <b>New-Object<\/b> cmdlet property, that makes setting these up very easy.&nbsp; The core function that creates the <b>WebClient<\/b> will take these parameters and convert them into a webclient object. This is seen here. <\/p>\n<blockquote>\n<div class=\"code\">\n<p><span style=\"font-family: courier new,courier\">function New-WebClient(<br \/>[string]$BaseAddress,<br \/>[Net.ICredentials]$Credentials,<br \/>[Net.WebHeaderCollection]$Headers,<br \/>[Collections.Specialized.NameValueCollection]$QueryString,<br \/>[bool]$UseDefaultCredentials<br \/>) {<br \/>&nbsp;&nbsp;&nbsp; New-Object Net.Webclient -Property $psBoundParameters<br \/>}<\/span><\/p>\n<\/div>\n<\/blockquote>\n<p>If you have done a decent amount of Windows PowerShell scripting, you might notice some problems with the script above.&nbsp; There are several things &rdquo;wrong&rdquo; with this kind of script which makes it both inconvenient and buggy to use. These problems are listed here. <\/p>\n<ul>\n<li>There is no clear way to turn <strong>Get-Credential<\/strong> into [<strong>Net.ICredentials<\/strong>].<\/li>\n<li><strong>WebHeaderCollection<\/strong> and <strong>NameValueCollection<\/strong> might be convenient to put in as a hash table. But it won&#8217;t work.<\/li>\n<li><strong>UseDefaultCredentials<\/strong> should be a switch parameter.<\/li>\n<li><strong>Credentials<\/strong>, <strong>Headers<\/strong>, and <strong>UseDefaultCredentials<\/strong> are plurals (good Windows PowerShell parameters are never plural).<\/li>\n<\/ul>\n<p>We want a function that has &ldquo;good&rdquo; Windows PowerShell input.&nbsp; Here&rsquo;s what <b>New-Webclient<\/b> might resemble if it had the parameters we wanted.<\/p>\n<blockquote>\n<div class=\"code\">\n<p><span style=\"font-family: courier new,courier\">function New-WebClient(<br \/>[string]$BaseAddress,<br \/>[Management.Automation.PSCredential]$Credential,<br \/>[Hashtable]$Header,<br \/>[Hashtable]$Query,<br \/>[Switch]$Anonymous<br \/>) {<br \/>&nbsp; &nbsp;&nbsp;<br \/>}<\/span><\/p>\n<\/div>\n<\/blockquote>\n<p>The following things have changed.<\/p>\n<ul>\n<li><strong>$Credential<\/strong> is now [<strong>Management.Automation.PSCredential<\/strong>], and is no longer plural.<\/li>\n<li><strong>$Headers<\/strong> is no longer plural, and is now a hash table.<\/li>\n<li><strong>$QueryString<\/strong> was renamed to <strong>$Query<\/strong> (because we are not constructing a string).<\/li>\n<li><strong>$UseDefaultCredentials<\/strong> was turned into <strong>$Anonymous<\/strong> (this will be handy if most of the stuff you want to access requires impersonation).<\/li>\n<\/ul>\n<p>We&rsquo;ve created several wrapper commands in the past.&nbsp; If you have a situation like this where you want to provide convenient input for users for a complex set of properties, make a second parameter set that has the complex properties. Creating multiple parameter sets is easier than it sounds, just add [Parameter(ParameterSetName=&rdquo;ParameterSetName&rdquo;)] in front of each parameter that is in each set.&nbsp; If you want the parameter to appear in multiple parameter sets, use the attribute multiple times.&nbsp; If you want the parameter to be in all parameter sets, leave the attribute out.<\/p>\n<p>Because one parameter set is a lot more difficult to use in Windows PowerShell than the other, I&rsquo;ve named the parameter sets &ldquo;Hard&rdquo; and &ldquo;Easy&rdquo;.&nbsp; When the &ldquo;Hard&rdquo; parameter set is used, the values just get passed on in.&nbsp; The &ldquo;Easy&rdquo; parameter set will translate them. Here&rsquo;s a stub that shows performing this task.<\/p>\n<blockquote>\n<div class=\"code\">\n<p><span style=\"font-family: courier new,courier\">function New-WebClient<br \/>{<br \/>&nbsp;&nbsp;&nbsp; param(<br \/>&nbsp;&nbsp;&nbsp; [string]$BaseAddress,<br \/>&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;Hard&#8217;)]<br \/>&nbsp;&nbsp;&nbsp; [Net.ICredentials]$Credentials,<br \/>&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;Easy&#8217;)]<br \/>&nbsp;&nbsp;&nbsp; [Management.Automation.PSCredential]$Credential,<br \/>&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;Hard&#8217;)]<br \/>&nbsp;&nbsp;&nbsp; [Net.WebHeaderCollection]$Headers,<br \/>&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;Easy&#8217;)]<br \/>&nbsp;&nbsp;&nbsp; [Hashtable]$Header,<br \/>&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;Hard&#8217;)]<br \/>&nbsp;&nbsp;&nbsp; [Collections.Specialized.NameValueCollection]$QueryString,<br \/>&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;Easy&#8217;)]<br \/>&nbsp;&nbsp;&nbsp; [Hashtable]$Query,<br \/>&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;Hard&#8217;)]<br \/>&nbsp;&nbsp;&nbsp; [bool]$UseDefaultCredentials,<br \/>&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;Easy&#8217;)]<br \/>&nbsp;&nbsp;&nbsp; [Switch]$Anonymous&nbsp;&nbsp;&nbsp; <br \/>&nbsp;&nbsp;&nbsp; )<br \/>&nbsp;&nbsp;&nbsp; <br \/>&nbsp;&nbsp;&nbsp; process {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ($psCmdlet.ParameterSetName -eq &#8220;Hard&#8221;) {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;New-Object Net.Webclient -Property $psBoundParameters<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # &#8230;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br \/>&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp; <br \/>}<\/span><\/p>\n<\/div>\n<\/blockquote>\n<p>We&rsquo;ll walk through filling out that stub one-by-one in the next bit, but let&rsquo;s set up some test data first.&nbsp; Here&rsquo;s a fairly complex <b>$psBoundParameters<\/b> I might get from the &ldquo;Easy&rdquo; parameter set that I&rsquo;ll want to translate into the Hard one.<\/p>\n<blockquote>\n<div class=\"code\">\n<p><span style=\"font-family: courier new,courier\">$psBoundParameters = @{<br \/>&nbsp;&nbsp;&nbsp; BaseAddress = &#8220;http:\/\/www.bing.com&#8221;<br \/>&nbsp;&nbsp;&nbsp; Credential = $null<br \/>&nbsp;&nbsp;&nbsp; Header = @{<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;User-Agent&#8221; = &#8220;WindowsPowerShell \/2.0&#8221;<br \/>&nbsp;&nbsp; &nbsp;}<br \/>&nbsp;&nbsp;&nbsp; Query = @{<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;Q&#8221; = &#8220;PowerShell&#8221;<br \/>&nbsp;&nbsp;&nbsp; }<br \/>&nbsp;&nbsp;&nbsp; Anonymous = $true&nbsp;&nbsp;&nbsp; <br \/>}<\/span><\/p>\n<\/div>\n<\/blockquote>\n<p>Everything else goes inside the &ldquo;&hellip;&rdquo; in the <b>New-WebClient<\/b> function. <\/p>\n<p>To turn this into <b>New-WebClient<\/b>&#8216;s Hard parameters, we&#8217;ll have to correct the parameters one by one. To do this, we&#8217;ll first create a copy of <b>$psBoundParameters<\/b> by adding it to a blank hash table. This is seen here. <\/p>\n<blockquote>\n<div class=\"code\">\n<p><span style=\"font-family: courier new,courier\">$newWebClientParameters = @{} + $psBoundParameters<\/span><\/p>\n<\/div>\n<\/blockquote>\n<p>Now we have to turn each item into the correct one.&nbsp; Because several were renamed we&#8217;ll have to see whether the convenient value is there, create a new entry with the raw value, and remove the convenient value.&nbsp; We do not have to worry about <b>BaseAddress<\/b> because this will be the same in both the easy and hard parameter sets.<\/p>\n<p><b>Anonymous<\/b> is the easiest. &nbsp;Because -Anonymous is a switch, it may or may not have been in <b>psBoundParameters<\/b> (<em>remember,<\/em> <strong>$psBoundParameters<\/strong><em> contains supplied values, not defaults<\/em>).&nbsp; We simply force <strong>$newWebClientParameters<\/strong> to not contain a value for <strong>UseDefaultCredentials<\/strong> and remove the value for <strong>Anonymous<\/strong>. This is illustrated here. <\/p>\n<blockquote>\n<div class=\"code\">\n<p><span style=\"font-family: courier new,courier\">$newWebClientParameters.UseDefaultCredentials = -not $Anonymous<br \/>$null = $newWebClientParameters.Remove(&#8220;Anonymous&#8221;)<\/span><\/p>\n<\/div>\n<\/blockquote>\n<p><b>$Credential<\/b> is fairly easy.&nbsp; <b>ICredentials<\/b> are used throughout .NET. Therefore, Windows PowerShell credentials have a fairly well paved road to them. This is seen here. <\/p>\n<blockquote>\n<div class=\"code\">\n<p><span style=\"font-family: courier new,courier\">if ($newWebClientParameters.Credential) {<br \/>&nbsp;&nbsp;&nbsp; $newWebClientParameters.Credentials = $newWebClientParameters.Credential.GetNetworkCredential()<br \/>&nbsp;&nbsp;&nbsp; $null = $newWebClientParameters.Remove(&#8220;Credential&#8221;)<br \/>}<\/span><\/p>\n<\/div>\n<\/blockquote>\n<p><b>$Header<\/b> and <b>$Query<\/b> are almost the same.&nbsp; We have to walk through the hash table and convert it into the collection that is needed. This is illustrated here.<\/p>\n<blockquote>\n<div class=\"code\">\n<p><span style=\"font-family: courier new,courier\">if ($newWebClientParameters.Header) {<br \/>&nbsp;&nbsp;&nbsp; $newWebClientParameters.Headers = New-Object Net.WebHeadercollection<br \/>&nbsp;&nbsp;&nbsp; foreach ($headerPair in $newWebClientParameters.Header.GetEnumerator()) {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $null = $newWebClientParameters.Headers.Add($headerPair.Key, $headerPair.Value)<br \/>&nbsp;&nbsp;&nbsp; }<br \/>&nbsp;&nbsp;&nbsp; $null = $newWebClientParameters.Remove(&#8220;Header&#8221;)<br \/>}<br \/>if ($newWebClientParameters.Query) {<br \/>&nbsp;&nbsp;&nbsp; $newWebClientParameters.QueryString = New-Object Collections.Specialized.NameValueCollection<br \/>&nbsp;&nbsp;&nbsp; foreach ($QueryPair in $newWebClientParameters.Query.GetEnumerator()) {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $null = $newWebClientParameters.QueryString.Add($QueryPair.Key, $QueryPair.Value)<br \/>&nbsp;&nbsp;&nbsp; }<br \/>&nbsp;&nbsp;&nbsp; $null = $newWebClientParameters.Remove(&#8220;Query&#8221;)<br \/>}<\/span><\/p>\n<\/div>\n<\/blockquote>\n<p>Now here&rsquo;s our finished <b>New-WebClient<\/b>, all in one function:<\/p>\n<blockquote>\n<div class=\"code\">\n<p><span style=\"font-family: courier new,courier\">function New-WebClient<br \/>{<br \/>&nbsp;&nbsp;&nbsp; [CmdletBinding(DefaultParameterSetName=&#8221;Easy&#8221;)]<br \/>&nbsp;&nbsp;&nbsp; param(<br \/>&nbsp;&nbsp;&nbsp; [string]$BaseAddress,<br \/>&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;Hard&#8217;)]<br \/>&nbsp;&nbsp;&nbsp; [Net.ICredentials]$Credentials,<br \/>&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;Easy&#8217;)]<br \/>&nbsp;&nbsp;&nbsp; [Management.Automation.PSCredential]$Credential,<br \/>&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;Hard&#8217;)]<br \/>&nbsp;&nbsp;&nbsp; [Net.WebHeaderCollection]$Headers,<br \/>&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;Easy&#8217;)]<br \/>&nbsp;&nbsp;&nbsp; [Hashtable]$Header,<br \/>&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;Hard&#8217;)]<br \/>&nbsp;&nbsp;&nbsp; [Collections.Specialized.NameValueCollection]$QueryString,<br \/>&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;Easy&#8217;)]<br \/>&nbsp;&nbsp;&nbsp; [Hashtable]$Query,<br \/>&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;Hard&#8217;)]<br \/>&nbsp;&nbsp;&nbsp; [bool]$UseDefaultCredentials,<br \/>&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;Easy&#8217;)]<br \/>&nbsp;&nbsp;&nbsp; [Switch]$Anonymous&nbsp;&nbsp;&nbsp; <br \/>&nbsp;&nbsp;&nbsp; )<br \/>&nbsp;&nbsp;&nbsp; <br \/>&nbsp;&nbsp;&nbsp; process {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ($psCmdlet.ParameterSetName -eq &#8220;Hard&#8221;) {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; New-Object Net.Webclient -Property $psBoundParameters<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $newWebClientParameters = @{} + $psBoundParameters<\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $newWebClientParameters.UseDefaultCredentials = -not $Anonymous<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $null = $newWebClientParameters.Remove(&#8220;Anonymous&#8221;)<\/p>\n<p>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($newWebClientParameters.Credential) {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $newWebClientParameters.Credentials = $newWebClientParameters.Credential.GetNetworkCredential()<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $null = $newWebClientParameters.Remove(&#8220;Credential&#8221;)<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;if ($newWebClientParameters.Header) {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $newWebClientParameters.Headers = New-Object Net.WebHeadercollection<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach ($headerPair in $newWebClientParameters.Header.GetEnumerator()) {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $null = $newWebClientParameters.Headers.Add($headerPair.Key, $headerPair.Value)<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $null = $newWebClientParameters.Remove(&#8220;Header&#8221;)<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ($newWebClientParameters.Query) {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $newWebClientParameters.QueryString = New-Object Collections.Specialized.NameValueCollection<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach ($QueryPair in $newWebClientParameters.Query.GetEnumerator()) {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $null = $newWebClientParameters.QueryString.Add($QueryPair.Key, $QueryPair.Value)<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $null = $newWebClientParameters.Remove(&#8220;Query&#8221;)<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; New-WebClient @newWebclientParameters<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br \/>&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp; <br \/>}<\/span><\/p>\n<\/div>\n<\/blockquote>\n<p>Let&rsquo;s take it for a spin. To do this, all that is required is to supply a few parameters when calling the <b>New-WebClient<\/b> function and store the returned object in the <b>$webClient<\/b> variable. The <b>DownloadString<\/b> method is then called and outputs the webpage to a htm file. The Invoke-Item cmdlet the opens the saved Webpage. This code is shown here.<\/p>\n<blockquote>\n<div class=\"code\">\n<p><span style=\"font-family: courier new,courier\">$webClient = New-WebClient -BaseAddress &#8220;http:\/\/www.start-automating.com\/&#8221; -Header @{&#8220;User-Agent&#8221; = &#8220;WindowsPowerShell \/2.0&#8221;} <br \/>$webClient.DownloadString(&#8220;&#8221;) &gt; test.htm<br \/>Invoke-Item .\\Test.htm<\/span><\/p>\n<\/div>\n<\/blockquote>\n<p>The output from this command is seen in the figure below.<\/p>\n<p>&nbsp;<img decoding=\"async\" height=\"356\" width=\"601\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4722.HSG-10-21-10-1.jpg\" border=\"0\" \/><\/p>\n<p>BR, that is all there is to using Windows PowerShell splatting to wrap messy .NET Framework commands into a nice, easy to use Windows PowerShell function. Guest blogger week will continue tomorrow when James will wrap up his series on splatting and stuff in Windows PowerShell 2.0. <\/p>\n<p>We invite you to follow us on <a target=\"_blank\" href=\"http:\/\/bit.ly\/scriptingguystwitter\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\">Facebook<\/a>. If you have any questions, send email to us at <a target=\"_blank\" href=\"mailto:scripter@microsoft.com\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/social.technet.microsoft.com\/Forums\/en\/ITCG\/threads\/\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>Ed Wilson and Craig Liebendorfer, Scripting Guys<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>&nbsp; Summary: Learn how to correctly package .NET Framework classes into reusable Windows PowerShell advanced functions in this step-by-step solution. &nbsp; Hey, Scripting Guy! I have been exploring the Microsoft .NET Framework on MSDN and attempting to convert some examples I have found there into Windows PowerShell code. I will admit that it can be [&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":[51,56,184,3,4,170,45],"class_list":["post-16751","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-getting-started","tag-guest-blogger","tag-james-brundage","tag-scripting-guy","tag-scripting-techniques","tag-splatting","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>&nbsp; Summary: Learn how to correctly package .NET Framework classes into reusable Windows PowerShell advanced functions in this step-by-step solution. &nbsp; Hey, Scripting Guy! I have been exploring the Microsoft .NET Framework on MSDN and attempting to convert some examples I have found there into Windows PowerShell code. I will admit that it can be [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/16751","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=16751"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/16751\/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=16751"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=16751"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=16751"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}