{"id":1795,"date":"2014-03-21T00:01:00","date_gmt":"2014-03-21T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2014\/03\/21\/use-dynamic-parameters-to-populate-list-of-printer-names\/"},"modified":"2014-03-21T00:01:00","modified_gmt":"2014-03-21T00:01:00","slug":"use-dynamic-parameters-to-populate-list-of-printer-names","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/use-dynamic-parameters-to-populate-list-of-printer-names\/","title":{"rendered":"Use Dynamic Parameters to Populate List of Printer Names"},"content":{"rendered":"<p><b>Summary<\/b>: Microsoft Scripting Guy, Ed Wilson, shows how to use dynamic parameters to populate a list of printer names.<\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. Using a Windows PowerShell function to send a test page to a printer is pretty cool (see <a href=\"https:\/\/devblogs.microsoft.com\/scripting\/use-powershell-to-send-test-page-to-a-printer\/\" target=\"_blank\">Use PowerShell to Send Test Page to a Printer<\/a>). But it would be even better if I did not have to type the name of the printer. I know I can use the <b>Get-Printer<\/b> function in Windows 8.1 to list all of my printers (and I can use a CIM session to do this remotely), so why can&rsquo;t I run that script to create my list of available printers? It turns out that I can do this by using dynamic parameters. The problem is that it is a bit confusing to do.<\/p>\n<p style=\"margin-left:30px\"><b>Note&nbsp;<\/b>I am not going to be able to explain all of this to everyone&rsquo;s fullest satisfaction. It is a bit complicated. But hopefully, I will explain it well enough that you can copy my script and modify it to do other things. So in effect, what I hope to create is a dynamic parameter template that you can use to create powerful and useful scripts. This is cool stuff, and for me, it is worth the effort. Also note that you do not have to do this. You can easily use available tools that will populate a WinForm with a drop-down list of available printers. Part of the point of today&rsquo;s post is to show you how you can do this. If you choose to do it is entirely up to you. Hope you enjoy the post as much as I had fun writing it.<\/p>\n<p>When I use the <b>Param<\/b> statement to add parameters to a function, I am accepting input that will change the way the function works. Typically, I use parameters to permit me to change the target of a function, and I call the <b>ComputerName<\/b> parameter as shown here:<\/p>\n<p style=\"margin-left:30px\"><strong>Function example<\/strong><\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;Param([string]$computername)<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p>I can also set default values for a parameter, which gives me a default behavior for my function. Typically, I like to target a function on my local computer. I like to obtain this value from the environmental variables as shown here:<\/p>\n<p style=\"margin-left:30px\"><strong>Function example<\/strong><\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">&nbsp;Param([string]$computername = $env:computername)<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p>But what if I wanted to produce a list of available computers that might be on my target network? This would require running a script block, and unfortunately I cannot do this. I need a dynamic parameter.<\/p>\n<p>Here is what I need to do to create a dynamic parameter:<\/p>\n<ol>\n<li>Use the <b>DynamicParam<\/b> keyword.<\/li>\n<li>Create a <b>ParameterAttribute<\/b> object.<\/li>\n<li>Create an attribute collection.<\/li>\n<li>Create a validation set.<\/li>\n<li>Create a <b>RuntimeDefinedParameter<\/b>.<\/li>\n<li>Create a <b>RuntimeDefinedParameterDictionary<\/b> object.<\/li>\n<\/ol>\n<h2>Create ParameterAttribute object<\/h2>\n<p>I need to create a <b>ParameterAttribute<\/b> object. To do this, I use the <b>New-Object<\/b> cmdlet, and then I create a <b>ParameterAttribute<\/b> object. This class resides in the System.Management.Automation namespace. (This is the namespace that contains all of the Windows PowerShell stuff). I store the newly created object in a variable named <b>$attributes<\/b>.<\/p>\n<p>Now I want to specify a name for the parameter set. This is a straight-forward value assignment to the <b>ParameterSetName<\/b> property. I decide to call it <b>__ParameterSets<\/b>. I also decide to make the attribute mandatory. Here are the three lines of script that accomplish this task:<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $attributes = new-object System.Management.Automation.ParameterAttribute<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $attributes.ParameterSetName = &quot;__AllParameterSets&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $attributes.Mandatory = $true<\/p>\n<h2>Create attribute collection<\/h2>\n<p>Now I need to create an attribute collection so I have a place to put the attributes that I just created. I use the <b>New-Object<\/b> cmdlet again, and this time, I store the returned attribute collection object in the <b>$attributeCollection<\/b> variable. The collection object comes from the System.Collections.ObjectModel namespace, and I specify that it will be of the <b>[system.Attribute]<\/b> type. When I have the object, I add my attributes to it by calling the <b>Add<\/b> method. This script is shown here:<\/p>\n<p style=\"margin-left:30px\">$attributeCollection =<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $attributeCollection.Add($attributes)<\/p>\n<h2>Create validation set<\/h2>\n<p>I need to get my collection of printer names. I use the <b>Get-CimInstance<\/b> cmdlet to query the Win32_Printer WMI class, and I return only the <b>Name<\/b> property. I store these printer names in the <b>$_Values<\/b> variable.<\/p>\n<p>Now I create another object. This time it is the <b>ValidateSetAttribute<\/b> object. Again, this comes from the System.Management.Automation .NET Framework namespace. When I am creating the <b>ValidateSetAttribute<\/b> class, I specify the printer names stored in the <b>$_Values<\/b> variable. I store the returned <b>ValidateSetAttribute<\/b> object in the <b>$validateSet<\/b> variable. I pass this object to the <b>Add<\/b> method of the <b>AttributeCollection<\/b> object that I created in the previous step. Here is the script that does this:<\/p>\n<p style=\"margin-left:30px\">$_Values = (Get-CimInstance win32_Printer).name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $ValidateSet =<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new-object System.Management.Automation.ValidateSetAttribute($_Values)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $attributeCollection.Add($ValidateSet)<\/p>\n<h2>Create RuntimeDefinedParameter<\/h2>\n<p>Whew&hellip;<\/p>\n<p>I still have not created everything I need to permit me to create my dynamic parameter. I need to create two more objects, and finally I will be done. I now use the <b>New-Object<\/b> cmdlet to create a <b>RuntimeDefinedParameter<\/b>. When I do this, I need to tell it the name of the parameter. In this example, it is <b>Printer<\/b>.<\/p>\n<p>I also need to tell it where to find the potential values. The values come from the <b>$attributeCollection<\/b> variable. I store the <b>RunTimeDefinedParameter<\/b> in the <b>$dynParam1<\/b> variable. Here is the script that accomplishes this task:<\/p>\n<p style=\"margin-left:30px\">$dynParam1 =<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new-object -Type System.Management.Automation.RuntimeDefinedParameter(<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;Printer&quot;, [string], $attributeCollection)<\/p>\n<h2>Create RuntimeDefinedParameterDictionary object<\/h2>\n<p>The last object I need to create is the <b>RuntimeDefinedParameterDictionary<\/b>. Once again, this dictionary object comes from the System.Management.Automation .NET Framework namespace. I add the name of <b>Printer<\/b>, and the <b>RuntimeDefinedParameter<\/b> object that I stored in the <b>$dynParam1<\/b> variable.<\/p>\n<p>Now it is time to return the <b>RuntimeDefinedParameterDictionary<\/b> object that I stored in the <b>$paramDictionary<\/b> object, so I call the <b>Return<\/b> keyword and pass the <b>$paramDictionary<\/b> variable as shown here:<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $paramDictionary =<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new-object -Type System.Management.Automation.RuntimeDefinedParameterDictionary<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $paramDictionary.Add(&quot;Printer&quot;, $dynParam1)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; return $paramDictionary }<\/p>\n<h2>Dude, it is a regular advanced function<\/h2>\n<p>At this point, it is just a regular advanced function. I add a <b>Begin<\/b> and a <b>Process<\/b> section to the function. I am not doing anything special in the begin section, and the process section is where I place my script block. Following the script block, I have my <b>End<\/b> section. This is shown here:<\/p>\n<p style=\"margin-left:30px\">begin {}<\/p>\n<p style=\"margin-left:30px\">&nbsp; process {<\/p>\n<p style=\"margin-left:30px\">&nbsp;$printer = $PSBoundParameters.printer<\/p>\n<p style=\"margin-left:30px\">&nbsp;Invoke-CimMethod -MethodName printtestpage -InputObject ( Get-CimInstance win32_printer -Filter &quot;name LIKE &#039;$printer&#039;&quot;) }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp; end {}<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p>When I run the function, I am presented with a nice drop-down list that permits me to select a printer. This is the dynamic portion of the parameters is shown in the following image:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-3-21-14-01.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-3-21-14-01.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>To me, it is worth a little bit of extra script to enable this functionality. &nbsp;Here is the complete script:<\/p>\n<p style=\"margin-left:30px\">Function out-TestPage {<\/p>\n<p style=\"margin-left:30px\">&nbsp;[CmdletBinding()]<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Param()<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; DynamicParam {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $attributes = new-object System.Management.Automation.ParameterAttribute<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $attributes.ParameterSetName = &quot;__AllParameterSets&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $attributes.Mandatory = $true<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $attributeCollection =<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $attributeCollection.Add($attributes)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $_Values = (Get-CimInstance win32_Printer).name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $ValidateSet =<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new-object System.Management.Automation.ValidateSetAttribute($_Values)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $attributeCollection.Add($ValidateSet)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $dynParam1 =<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new-object -Type System.Management.Automation.RuntimeDefinedParameter(<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;Printer&quot;, [string], $attributeCollection)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $paramDictionary =<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new-object -Type System.Management.Automation.RuntimeDefinedParameterDictionary<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $paramDictionary.Add(&quot;Printer&quot;, $dynParam1)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; return $paramDictionary }<\/p>\n<p style=\"margin-left:30px\">&nbsp; begin {}<\/p>\n<p style=\"margin-left:30px\">&nbsp; process {<\/p>\n<p style=\"margin-left:30px\">&nbsp;$printer = $PSBoundParameters.printer<\/p>\n<p style=\"margin-left:30px\">&nbsp;Invoke-CimMethod -MethodName printtestpage -InputObject ( Get-CimInstance win32_printer -Filter &quot;name LIKE &#039;$printer&#039;&quot;) }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp; end {}<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p>Join me tomorrow when we will have more cool Windows PowerShell stuff.<\/p>\n<p>I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"mailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><b>Ed Wilson, Microsoft Scripting Guy<\/b>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Microsoft Scripting Guy, Ed Wilson, shows how to use dynamic parameters to populate a list of printer names. Microsoft Scripting Guy, Ed Wilson, is here. Using a Windows PowerShell function to send a test page to a printer is pretty cool (see Use PowerShell to Send Test Page to a Printer). But it would [&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":[491,404,3,4,45],"class_list":["post-1795","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-printers","tag-printing","tag-scripting-guy","tag-scripting-techniques","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Microsoft Scripting Guy, Ed Wilson, shows how to use dynamic parameters to populate a list of printer names. Microsoft Scripting Guy, Ed Wilson, is here. Using a Windows PowerShell function to send a test page to a printer is pretty cool (see Use PowerShell to Send Test Page to a Printer). But it would [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/1795","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=1795"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/1795\/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=1795"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=1795"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=1795"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}