{"id":69766,"date":"2017-01-30T10:37:49","date_gmt":"2017-01-30T10:37:49","guid":{"rendered":"https:\/\/www.simple-talk.com\/?p=69766"},"modified":"2017-03-06T15:13:47","modified_gmt":"2017-03-06T15:13:47","slug":"powershell-time-saver-automatic-defaults","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/powershell\/powershell-time-saver-automatic-defaults\/","title":{"rendered":"PowerShell Time Saver: Automatic Defaults"},"content":{"rendered":"<p>With practically every application you use on your computer\u2014be it a word processor, an IDE, a web browser, an instant messager\u2014after you install it and use it a bit, you inevitably want to customize it to your working style, so you open up the preferences panel and tweak a few settings to your liking. <strong>You make those changes just once; they are saved by the application so that the next time you open it up<\/strong>, it remembers your preferences and gives you the user experience you prefer. PowerShell is a different kind of application, of course, in that it uses a command-line rather than a GUI, but it has a cleverly hidden capability to give you the same preference-setting capability. A few examples:<\/p>\n<ul>\n<li>When you list files (<code>Get-ChildItem<\/code>) do you always like to include hidden files (with the <code>-Force<\/code> switch)?<\/li>\n<li>When you want help on a cmdlet (<code>Get-Help<\/code>) do you always use the <code>-ShowWindow<\/code> switch so that it pops open help in a separate window?<\/li>\n<li>Are you still new to PowerShell, treading carefully by always including <code>-Confirm<\/code> on any cmdlet that accepts it?<\/li>\n<\/ul>\n<p>All of those preferences can be set once and then automatically applied with no further intervention on your part.<\/p>\n<p>The second handy use of such preferences is on the occasion when you need to use several cmdlets in succession, all of which need a certain value for a parameter. Say, for example, you frequently run cmdlets on a remote machine. Here you might need to use the built-in cmdlets <code>New-PSSession<\/code>, <code>Enter-PSSession<\/code>, and <code>Invoke-Command<\/code>, and on each of those you need to specify, e.g.<\/p>\n<p><code>-ComputerName htxyz1032243.dev.mycompany-testdev-1<\/code><\/p>\n<p>Rather than having to type that on each cmdlet (today, then tomorrow, then\u2026) you have likely already realized that you could put that unwieldy computer name into a variable and shorten it to, say,<\/p>\n<p><code>-ComputerName $myRemoteMachine<\/code><\/p>\n<p>But with PowerShell&#8217;s preference capability you can go one better, not having to include the <code>ComputerName<\/code> parameter at all unless you access a different remote machine!<\/p>\n<p>PowerShell provides a special preference variable, <a href=\"https:\/\/msdn.microsoft.com\/powershell\/reference\/5.1\/Microsoft.PowerShell.Core\/about\/about_Parameters_Default_Values\">$PSDefaultParameterValues<\/a>, that can save all that needless repetition by specifying the default values for parameters. The values you assign to this variable are somewhat less transparent than specifying values with a given command, as you&#8217;ll see, but once you configure those values you can safely tuck them away in your PowerShell profile and not be further concerned with them. This article explains how to use it directly, and provides some helper functions to make it even more powerful.<\/p>\n<h2>Suggested Uses<\/h2>\n<p>Before explaining the details of how to use this special preference variable, the following table presents a few ideas for useful general purpose settings. At this point, focus more on the right-hand column to see what the setup does for you; you&#8217;ll soon be up to speed on precisely what is being done in the left-hand column.<\/p>\n<table style=\"width: 954.257px;\">\n<thead style=\"color: white; background-color: #666699;\">\n<tr>\n<td style=\"padding: 0 5px 0 5px; width: 312px;\">\n<p>Assignment<\/p>\n<\/td>\n<td style=\"padding: 0 5px 0 5px; width: 648.257px;\">\n<p>Purpose<\/p>\n<\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"padding: 0 5px 0 5px; width: 312px;\">\n<p><code><strong>$PSDefaultParameterValues += <br \/> @{'Get*:Verbose' = $true}<\/strong><\/code><\/p>\n<\/td>\n<td style=\"padding: 0 5px 0 5px; width: 648.257px;\">\n<p>Apply <code>-Verbose<\/code> to any cmdlet that supports it and begins with &#8220;Get&#8221;.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0 5px 0 5px; width: 312px;\">\n<p><code><strong>$ PSDefaultParameterValues += <br \/> @{'Get-Help:ShowWindow' = $true}<\/strong><\/code><\/p>\n<\/td>\n<td style=\"padding: 0 5px 0 5px; width: 648.257px;\">\n<p>Whenever you invoke <code>Get-Help<\/code>, instead of just scrolling the help text in your current window, PowerShell will open a separate pop-up help window.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0 5px 0 5px; width: 312px;\">\n<p><code><strong>$PSDefaultParameterValues += <br \/> @{'*:Confirm' = $true}<\/strong><\/code><\/p>\n<\/td>\n<td style=\"padding: 0 5px 0 5px; width: 648.257px;\">\n<p>For <em>any<\/em> cmdlet that honors the <code>-Confirm<\/code> flag, use it. This gives you a nice safety net by always prompting you with an &#8220;Are you sure you want to do <code><strong><em>x<\/em><\/strong><\/code>?&#8221;<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0 5px 0 5px; width: 312px;\">\n<p><code><strong>$PSDefaultParameterValues += <br \/> @{'Get-ChildItem:Force' = $true}<\/strong><\/code><\/p>\n<\/td>\n<td style=\"padding: 0 5px 0 5px; width: 648.257px;\">\n<p>Whenever you list directory contents, always include hidden files and folders.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0 5px 0 5px; width: 312px;\">\n<p><code><strong>$myCredentials = Get-Credential<\/strong><\/code><\/p>\n<p><code><strong>$PSDefaultParameterValues += <br \/> @{'*:Credential' = $myCredentials}<\/strong><\/code><\/p>\n<\/td>\n<td style=\"padding: 0 5px 0 5px; width: 648.257px;\">\n<p>For <em>any<\/em> cmdlet that requires credentials, automatically apply saved credentials. Note that because of the first line of code at left, every time you start up PowerShell you will get a prompt asking you to enter your credentials. But you won&#8217;t have to enter it again as long as the window remains open.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0 5px 0 5px; width: 312px;\">\n<p><code><strong>$PSDefaultParameterValues += <br \/> @{ 'Get-History:Count' = 10 }<\/strong><\/code><\/p>\n<\/td>\n<td style=\"padding: 0 5px 0 5px; width: 648.257px;\">\n<p>Whenever you list your command history, just show the most recent 10 items.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0 5px 0 5px; width: 312px;\">\n<p><code><strong>$PSDefaultParameterValues += <br \/> @{ 'Format-Table:AutoSize' = $true }<\/strong><\/code><\/p>\n<\/td>\n<td style=\"padding: 0 5px 0 5px; width: 648.257px;\">\n<p>Always apply <code>-AutoSize<\/code> when you use the Format-Table cmdlet.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>(Kudos to Boe Prox in his post <a href=\"https:\/\/learn-powershell.net\/2013\/12\/11\/using-psdefaultparametervalues-in-powershell\/\">Using $PSDefaultParameterValues in\u00a0PowerShell<\/a> for a couple of these.)<\/p>\n<p>I should point out that, though this does present quite a convenience in many cases, you need to make sure you define default values carefully: because you can use wildcards in the definitions you can easily specify too broad a set of cmdlets, inadvertently setting defaults for cmdlets you did not intend. A second potential downside is that you will now be introducing obfuscation in your commands\u2014including behaviors via default that you are not explicitly calling out in your commands. So, if you are writing scripts, for example, best practice dictates you should avoid abbreviations and spell out cmdlet and parameter names. I would add to that to always be explicit with your parameters and do not rely on these implicit defaults.<\/p>\n<h2>Operations on $PSDefaultParameterValues<\/h2>\n<p><code>$PSDefaultParameterValues<\/code> is a standard hash table, so all the standard operations for a hash table may be used. This section details these standard operations in the context of <code>$PSDefaultParameterValues<\/code>.<\/p>\n<h3>Initialize<\/h3>\n<p>Option 1: By assigning a hash table literal, this overwrites any previous values of the hash. This style allows you to add multiple values at one time.<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">$PSDefaultParameterValues = @{\r\n      'foo:mode' = 5\r\n      'bar:name' = 'abc'\r\n      'baz:ErrorAction' = 'Continue'\r\n  }<\/pre>\n<p>Option 2: You can also use the <code>Add<\/code> method because <code>$PSDefaultParameterValues<\/code> starts out as an empty hash (rather than <code>$null<\/code>, like a typical variable). The <code>Add<\/code> method can only assign one value at a time, though, so multiple calls are needed to include multiple elements.<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">$PSDefaultParameterValues.Add('foo:mode', 5)\r\n$PSDefaultParameterValues.Add('bar:name', 'abc')\r\n$PSDefaultParameterValues.Add('baz:ErrorAction', 'Continue')<\/pre>\n<h3>Add Elements<\/h3>\n<p>Option 1: If you want to add to what is already present, make sure you use the hash <em>addition<\/em> operator (<code>+=<\/code>) rather than just <em>assignment<\/em> (<code>=<\/code>). Again, you can add multiple items at one time with this approach:<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">$PSDefaultParameterValues += @{\r\n    '*-PsSession:ComputerName' = 'htxyz1032243.dev.mycompany-testdev-1'\r\n    'Invoke-Command:ComputerName' = 'htxyz1032243.dev.mycompany-testdev-1'\r\n}<\/pre>\n<p>Option 2: Use the <code>Add<\/code> method on the hash table.<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">$PSDefaultParameterValues.Add(\r\n    '*-PsSession:ComputerName', 'htxyz1032243.dev.mycompany-testdev-1')\r\n$PSDefaultParameterValues.Add(\r\n    'Invoke-Command:ComputerName', 'htxyz1032243.dev.mycompany-testdev-1')<\/pre>\n<h3>Fetch an Element<\/h3>\n<p>Option 1: A hash table element may be fetched either with an <em>array<\/em>-like syntax or with a <em>property<\/em>-like syntax. Here&#8217;s the array-like approach. Here, a string key must always be quoted:<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">Write-Output $PSDefaultParameterValues['baz:ErrorAction']<\/pre>\n<p>Option 2: Typically when you use a property-like syntax you can drop the quotes, but in this case you must include them because of the embedded colon in the key.<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">Write-Output $PSDefaultParameterValues.'baz:ErrorAction'<\/pre>\n<h3>Update an Element<\/h3>\n<p>An update has the same options as a fetch; either way, just assign a new value to that key in the hash.<\/p>\n<p>Option 1:<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">$PSDefaultParameterValues['*-PsSession:ComputerName']\r\n    = 'xy3.dev.mycompany-testdev-2'<\/pre>\n<p>Option 2:<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">$PSDefaultParameterValues.'*-PsSession:ComputerName'\r\n    = 'xy3.dev.mycompany-testdev-2'<\/pre>\n<h3>Remove Elements<\/h3>\n<p>Option 1: Remove a single element with the <code>Remove<\/code> method.<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">$PSDefaultParameterValues.Remove('foo:mode')<\/pre>\n<p>Option 2: Remove all elements with the <code>Clear<\/code> method.<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">$PSDefaultParameterValues.Clear()<\/pre>\n<h2>$PSDefaultParameterValues Syntax Notes<\/h2>\n<p>This <code>$PSDefaultParameterValues<\/code> variable is just a standard hash table but augmented with the ability to validate the format of its hash keys (reference: <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/system.management.automation.defaultparameterdictionary(v=vs.85).aspx\">System.Management.Automation.DefaultParameterDictionary<\/a>).<\/p>\n<h3>The Key<\/h3>\n<p>A key in this particular dictionary must be of the form:<\/p>\n<p><em>&lt;Key&gt;<\/em> :: = &#8216;<span style=\"color: darkred;\">&#8220;<\/span>&#8216; <span style=\"color: darkred;\"><em>&lt;CmdletName&gt;<\/em><\/span> &#8216;<span style=\"color: darkred;\">:<\/span>&#8216; <span style=\"color: darkred;\"><em>&lt;ParameterName&gt;<\/em><\/span> &#8216;<span style=\"color: darkred;\">&#8220;<\/span>&#8216;<\/p>\n<p>The only validation that is actually done in a <code>DefaultParameterDictionary<\/code> is confirming that both <code>&lt;CmdletName&gt;<\/code> and <code>&lt;ParameterName&gt;<\/code> are non-empty and separated by a colon; it does <em>not<\/em> check that either the cmdlet or parameter actually exists.<\/p>\n<p>Both the <code>&lt;CmdletName&gt;<\/code> and <code>&lt;ParameterName&gt;<\/code> may contain standard globbing wildcards. (<em>Globbing<\/em> essentially means you can use an asterisk to match any group of characters or a question mark to match any single character; see <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/aa717088(v=vs.85).aspx\">Supporting Wildcard Characters in Cmdlet Parameters<\/a> for more details.) You&#8217;ll observe I showed an example of that under &#8220;Add Elements&#8221; above.<\/p>\n<h3>The Value<\/h3>\n<p>Each value in this dictionary must be one of these:<\/p>\n<p><em>&lt;Value&gt;<\/em> ::= <span style=\"color: darkred;\"><em>&lt;Object&gt;<\/em><\/span> | &#8216;{&#8216; <span style=\"color: darkred;\"><em>&lt;NormalScriptBlock&gt;<\/em><\/span> &#8216;}&#8217; | <span style=\"color: darkred;\"><em>&lt;SpecialScriptBlock&gt;<\/em><\/span><\/p>\n<p>If the default value is a constant in all cases, just assign that literal value\u2014all the instances in the previous section show examples of this. If, on the other hand, the default value may vary but in a predictable way, you can specify arbitrary code with a script block to determine a value under different conditions.<\/p>\n<div class=\"note\">\n<h4>Important Note:<\/h4>\n<p>The <a href=\"https:\/\/msdn.microsoft.com\/powershell\/reference\/5.1\/Microsoft.PowerShell.Core\/about\/about_Parameters_Default_Values\">official documentation<\/a> only gives part of the story on script blocks. In fact, it shows only one syntax option for script blocks:<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">$PSDefaultParameterValues=@{\"&lt;CmdletName&gt;:&lt;ParameterName&gt;\"={&lt;ScriptBlock&gt;}}<\/pre>\n<p>It goes on to state <em>[red highlighting mine]<\/em>: &#8220;When the specified parameter takes a script block value, enclose the script block value in a second set of braces, such as:<\/p>\n<p>$PSDefaultParameterValues=@{ &#8220;Invoke-Command:ScriptBlock&#8221;=<span style=\"color: red;\"><code><strong>{<\/strong><\/code><\/span>{Get-Process}<span style=\"color: red;\"><code><strong>}<\/strong><\/code><\/span> }<\/p>\n<p>That is <em>usually<\/em> true. If you run the above setup statement it evaluates the right-hand side of the assignment\u2014a script block containing a script block\u2014so ends up correctly assigning the inner script block to the hash value. So when you eventually run <code>Invoke-Command<\/code> it correctly executes the script block containing just <code>Get-Process<\/code>.<\/p>\n<p><em>Without<\/em> the second set of braces, what happens is odd. The resultant value added to the hash still seems to be a valid script block(!), as shown in [2] below, but then when executing [3] it fails.<\/p>\n<pre class=\"crayon:false\" style=\"width: 640px; color: white; font-family: 'Lucida Console', 'Courier New', Courier, monospace; font-size: 11px; line-height: 130%; padding: 8px; background: #1F3864; margin: 4px 0in 4px 0px;\">[1]: $PSDefaultParameterValues=@{ \"Invoke-Command:ScriptBlock\"={Get-Process} }\r\n[2]: $PSDefaultParameterValues['Invoke-Command:ScriptBlock'].GetType().FullName\r\nSystem.Management.Automation.ScriptBlock\r\n[3]: Invoke-Command\r\n<span style=\"color: yellow;\">WARNING: The binding of default value \r\n'System.Collections.ObjectModel.Collection`1[System.Management.Automation.PSObject]' to\r\nparameter 'ScriptBlock' failed<\/span><\/pre>\n<p>OK, so for some reason you must have a second set of braces to yield a true script block. This is what I refer to in the syntax specification above as a <em>NormalScriptBlock<\/em>. But it gets even more curious. A couple sections lower in this article, you will find a more in-depth example of using script blocks. In that example, it only works with a <em>single<\/em> set of braces; it fails if you add a second set of braces. This is what I am calling a <em>SpecialScriptBlock<\/em> in the syntax specification above. Alas, I have not yet isolated what makes it special, so if you have any clues please add a reader comment below!<\/p>\n<\/div>\n<h3>One Special Element<\/h3>\n<p>This hash has one special, optional element as well. You can define an element with the key <code>Disabled<\/code> and the value <code>$true<\/code> or <code>$false<\/code>. If this element is absent (either never added or at some point removed), or it is present with a value <code>$false<\/code>, then the hash is enabled and default value processing will be applied to your commands. If it is present with a value <code>$true<\/code>, that turns off default value processing for all commands. This provides a convenient mechanism for maintaining a list of defaults and allowing you to turn it off and on at any time should the need arise, without losing any of those default assignments.<\/p>\n<h2>Persisting your Settings<\/h2>\n<p>Any settings you make to <code>$PSDefaultParameterValues<\/code> exist only for the duration of your current PowerShell session. If you open a new PowerShell window later, the variable will again start out empty. Therefore, any values you setup for this hash table should be done in your PowerShell profile if you plan to use them frequently. That way, on startup of a PowerShell session, those values are preloaded for you automatically. (Be aware that you actually have more than one profile available\u2014see my <a href=\"https:\/\/www.simple-talk.com\/sysadmin\/powershell\/persistent-powershell-the-powershell-profile\/\">Persistent PowerShell: The PowerShell Profile<\/a> article for all the details. You can set the value of <code>$PSDefaultParameterValues<\/code> in any one of your profiles.)<\/p>\n<h2>Script Blocks and Dynamic Defaults<\/h2>\n<p>All of the above samples set the default value to an <em>object<\/em>\u2014<code>Boolean<\/code> in most of them, a <code>Credential<\/code> object in the last one. But recall from the syntax section earlier that you can also supply a <em>script block<\/em> when you want to apply a more dynamic default value. To illustrate this I am going to borrow Lee Holmes&#8217; excellent example in recipe 1.4, <em>Supply Default Values for Parameters,<\/em> from <a href=\"http:\/\/shop.oreilly.com\/product\/0636920024132.do\">Windows PowerShell Cookbook, 3rd Edition<\/a>. His example illustrates how, when you want to invoke cmdlets on remote machines, you can automatically supply different credentials based on the name of the remote machine. Holmes&#8217; particular example is well chosen as it takes very little code but shows a real use, <em>not<\/em> some academic (read: <em>useless<\/em>) example. Alas, there are a couple issues with his implementation that prevent it from working, which I address here.<\/p>\n<p>First, you need to set up the credentials you will be using. The obvious choice for this is to use a hash table keyed off the machine name. Let&#8217;s assume there are two machines you need to interact with very frequently. Further, we will assume that there are other machines you need to interact with only infrequently, so we won&#8217;t set defaults for those here. When you execute this first bit of setup code, you will be prompted by each instance of <code>Get-Credential<\/code> to enter the corresponding password for each user. Those passwords will be stored securely in credential objects in the hash table. (I have added the <code>UserName<\/code> values explicitly here so when you see the results showing the auto-default working, you have a concrete reference. Also, you&#8217;ll have less typing, only needing to supply a password for each.)<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">$credmap = @{}\r\n$credmap['RemoteMachine1'] = Get-Credential -UserName RemoteUser1 -Message 'pwd?'\r\n$credmap['RemoteMachine2'] = Get-Credential -UserName RemoteUser2 -Message 'pwd?'<\/pre>\n<p>Next let&#8217;s define a value in <code>$PSDefaultParameterValues<\/code> so that <em>any<\/em> cmdlet having a <code>Credential<\/code> parameter will apply this default, which is a script block. That&#8217;s line 1 in the code below. In order to lookup a credential by machine name, the cmdlet has to include a parameter named <code>ComputerName<\/code>; you will find that all the relevant remoting cmdlets include such a parameter: <code>New-PSSession<\/code>,<code> Invoke-Command<\/code>, <code>Receive-Job<\/code>, etc. Then line 2 checks if the user has actually supplied a value for <code>ComputerName<\/code>. If not, we cannot lookup any credential so the code returns nothing, which means that no default value will be applied. That&#8217;s certainly what one would reasonably expect.<\/p>\n<p>Let&#8217;s now focus on the case where the user has supplied a value for <code>ComputerName<\/code> to whatever cmdlet is invoked. Line 3 looks up the credential for that machine. If it is defined, it will have a truthy value so line 4 returns that credential; if not defined, line 5 will invoke <code>Get-Credential<\/code> on the spot, popping up a dialog to accept your username and password, and then return that new credential value it just created. So as long as a <code>ComputerName<\/code> is supplied to the cmdlet being invoked, this guarantees that a valid credential object will automatically be supplied to the cmdlet&#8217;s <code>Credential<\/code> parameter.<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">$PSDefaultParameterValues[\"*:Credential\"] = {                 # 1\r\n    if($args[0].BoundParameters -contains \"ComputerName\")     # 2\r\n    {\r\n        $cred = $credmap[$PSBoundParameters[\"ComputerName\"]]  # 3\r\n        If ($cred) { $cred }                                  # 4\r\n        else { Get-Credential }                               # 5\r\n    }\r\n}<\/pre>\n<p>Finally, this small <code>RemoteConnector<\/code> function is all we need to see how everything works. This function takes two parameters, <code>ComputerName<\/code> and <code>Credential<\/code>, exactly the two parameters that we&#8217;ve setup to be able to automatically supply a credential based on machine name. (I have added the <code>CmdletBinding<\/code> attribute here; without it, auto-defaulting does not work!)<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">function RemoteConnector {\r\n    [CmdletBinding()]\r\n    param($ComputerName, $Credential)\r\n    \"Connecting as \" + $Credential.UserName\r\n}<\/pre>\n<p>To prove that the auto-default is working, all the function does is display the user name from that auto-supplied credential. When I invoke the function with <code>RemoteMachine1<\/code>, it successfully looks up that credential and is then able to retrieve the <code>UserName<\/code> property, displaying <code>RemoteUser1<\/code>. Similarly for <code>RemoteMachine2<\/code> and <code>RemoteUser2<\/code>.<\/p>\n<pre class=\"theme:powershell-output lang:ps decode:true\">[1]: RemoteConnector -ComputerName RemoteMachine1\r\nConnecting as RemoteUser1\r\n\r\n[2]: RemoteConnector -ComputerName RemoteMachine2\r\nConnecting as RemoteUser2\r\n\r\n[3]: RemoteConnector -ComputerName RemoteMachine3\r\ncmdlet Get-Credential at command pipeline position 1\r\nSupply values for the following parameters:\r\nConnecting as OtherUser<\/pre>\n<p>However, when I use <code>RemoteMachine3<\/code>\u2014which has <em>not<\/em> had a default value supplied\u2014it first prints a message saying it is invoking <code>Get-Credential<\/code> then displays this pop-up\u2026<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"324\" height=\"206\" class=\"wp-image-69794\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2017\/01\/word-image-116.png\" \/><\/p>\n<p>\u2026and finally prints the user name I just entered from that pop-up.<\/p>\n<p>There&#8217;s just one final, important difference from Holmes&#8217; implementation to mention. In setting up the auto-defaulting he did not include line 5, the <code>else<\/code> clause, so if the machine name was not found, it would not supply a default. To compensate for that, he marked the <code>Credential<\/code> parameter of <code>RemoteConnector<\/code> as <code><strong>mandatory<\/strong><\/code>, meaning that if you did not supply a credential value on the command line, and there was nothing to auto-default, then <code>RemoteConnector<\/code> itself would prompt you to enter a credential object but <em>without the Get-Credential pop-up<\/em>. But that means it is expecting you to <em>type in<\/em> a <code>Credential<\/code> object, which is not possible. (Nonetheless, I am happy to stand on his shoulders and be able to present this cool example.)<\/p>\n<h2>Convenience for Your Custom PowerShell Code<\/h2>\n<p>Besides setting up some convenient shortcuts for use with built-in cmdlets, for developers it is also handy to be able to work some magic with your own custom cmdlets. In my shop, for example, we have one module containing a couple dozen cmdlets, many of which use a common parameter, <code>Mode<\/code>. This <code>Mode<\/code> parameter varies from client to client, but for any single client, every cmdlet working on their data needs to use the same value of <code>Mode<\/code>. So I have to add <code>-Mode hist-0010-dev.test<\/code> onto each cmdlet I am using, which gets very tiring\/annoying very quickly. You could, of course, put that value into a variable and then just use, e .g. <code>-Mode $myMode<\/code>, which is less typing, but if less is better, then no typing at all is better still.<\/p>\n<p>Now, you have seen that wildcards can be used to specify defaults, so I could just use a setting:<\/p>\n<p><code>$PSDefaultParameterValues += @{'*:Mode' = 'hist-0010-dev.test'}<\/code><\/p>\n<p>However, the term &#8220;Mode&#8221; is so common there is a high probability that I will run some unrelated cmdlet that also happens to accept a <code>Mode<\/code> parameter but where that parameter means something completely different. Even if the parameter name was less common, though, I would have similar reservations about a possible collision. I wanted a way to <em>guarantee<\/em> there is no chance of collision (assuming you have appropriately named your cmdlets uniquely).<\/p>\n<p>What I came up with is the <code>Set-DefaultParameter<\/code> cmdlet. This is a &#8220;wrapper&#8221; around the <code>$PSDefaultParameterValues<\/code> variable that applies a default for a specified parameter but <em>only<\/em> for cmdlets within a specified module. Put this code in your PowerShell profile so it is available to you whenever you need it. (By the way, when I say &#8220;put [it] in your PowerShell profile&#8221; that implies just inlining it but that would make for a long, messy profile! I urge you instead to put it in its own file in a profile_scripts directory that is read and sourced by your profile\u2014see the section on <em>Complex Functions<\/em> in my article <a href=\"https:\/\/www.simple-talk.com\/sysadmin\/powershell\/persistent-powershell-the-powershell-profile\/\">Persistent PowerShell: The PowerShell Profile<\/a> for details.)<\/p>\n<p>Note that this cmdlet comes fully documented in the project file accompanying this article.<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">function Set-DefaultParameter\r\n{\r\n  [CmdletBinding()]\r\n  param(\r\n    [Parameter(Mandatory)][string]$ModuleName,\r\n    [Parameter(Mandatory)][string]$ParameterName,\r\n    [Parameter(Mandatory)][object]$DefaultValue\r\n  )\r\n\r\n  Get-Command -module $ModuleName |\r\n  Where-Object { $_.Parameters.ContainsKey($ParameterName) } |\r\n  Foreach-Object { _AddDefault $_ $ParameterName $DefaultValue }\r\n}\r\n\r\nfunction _AddDefault(\r\n  [System.Management.Automation.FunctionInfo]$cmdlet,\r\n  [string]$paramName,\r\n  [string]$value)\r\n{\r\n  $cmdletName = $cmdlet.Name\r\n\r\n  # check for any standard cmdlet parameters here\r\n  $hasCmdletBinding = $cmdlet.Parameters.ContainsKey('ErrorAction')\r\n\r\n  if ($hasCmdletBinding) {\r\n    Write-Verbose $cmdletName\r\n    $key = \"$cmdletName`:$paramName\"\r\n    $global:PSDefaultParameterValues[$key] = $value\r\n  }\r\n  else {\r\n    Write-Warning \"Skipping '$cmdletName' because it would be ignored due to lack of cmdlet binding\"\r\n  }\r\n}<\/pre>\n<p>As you can see, you simply specify a module name, a parameter name, and a default value for that parameter. <code>Set-DefaultParameter<\/code> then adds entries to <code>$PSDefaultParameterValues<\/code> for each cmdlet in that module that has that parameter. I will use a trivial demo module to illustrate how this works. To try it yourself simply paste the three functions below into a new file called DemoModule.psm1. Note the <code>psm1<\/code> ending denoting a module (as opposed to a <code>ps1<\/code> ending denoting a plain script). Also note that this is fine for a throw-away module, but for creating real modules see my suggestions in <a href=\"http:\/\/www.simple-talk.com\/dotnet\/.net-tools\/further-down-the-rabbit-hole-powershell-modules-and-encapsulation\/\">Further Down the Rabbit Hole: PowerShell Modules and Encapsulation<\/a>. (The attached project file provides the fully realized module, complete with manifest.)<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true\">function Test-Cmdlet1\r\n{\r\n\t[CmdletBinding()]\r\n\tparam($Mode)\r\n\t\"Cmdlet1: Mode is [$Mode]\"\r\n}\r\n\r\nfunction Test-Cmdlet2\r\n{\r\n\t[CmdletBinding()]\r\n\tparam($Mode, $Color, $Name)\r\n\t\"Cmdlet2: Mode is [$Mode], Color is [$Color], Name is [$Name]\"\r\n}\r\n\r\nfunction Test-Cmdlet3\r\n{\r\n\t# No cmdlet binding so this function will not accept an external default!\r\n\t#[CmdletBinding()]\r\n\tparam($Mode, $Color, $Name)\r\n\r\n\t\"Cmdlet3: Mode is [$Mode]\"\r\n}<\/pre>\n<p>To set the stage, you need to [1] dot-source the <code>Set-DefaultParameter<\/code> cmdlet into the current PowerShell scope (explained in the <em>Using Dot Source Notation with Scope<\/em> section of the help topic <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd315289.aspx\">about_Scopes<\/a>) so it is available to use and [2] import the DemoModule. Note that I have included the <code>-Verbose<\/code> parameter here so you can see the three functions loaded from the module. Command [3] is just to show that <code>$PSDefaultParameterValues<\/code> starts out empty.<\/p>\n<pre class=\"crayon:false\" style=\"color: white; font-family: 'Lucida Console', 'Courier New', Courier, monospace; font-size: 11px; line-height: 130%; padding: 8px; background: #1F3864; margin: 4px 0in 4px 0px;\"><span style=\"color: #b5fcfc;\">1]: . .\\Set-DefaultParameter.ps1<\/span>\r\n<span style=\"color: #b5fcfc;\">[2]: Import-Module .\\DemoModule\\DemoModule.psm1 -Verbose<\/span>\r\n<span style=\"color: yellow;\">VERBOSE: Loading module from path 'C:\\usr\\simpletalk\\DemoModule\\DemoModule.psm1'.\r\nVERBOSE: Importing function 'Test-Cmdlet1'.\r\nVERBOSE: Importing function 'Test-Cmdlet2'.\r\nVERBOSE: Importing function 'Test-Cmdlet3'.<\/span>\r\n<span style=\"color: #b5fcfc;\">[3]: $PSDefaultParameterValues.Count<\/span>\r\n0\r\n\r\n<span style=\"color: #b5fcfc;\">[4]: Set-DefaultParameter -ModuleName DemoModule `\r\n     -ParameterName Mode -DefaultValue internal -Verbose<\/span>\r\n<span style=\"color: yellow;\">VERBOSE: Test-Cmdlet1\r\nVERBOSE: Test-Cmdlet2<\/span>\r\n<span style=\"color: red;\">WARNING: Skipping 'Test-Cmdlet3' because it would be ignored due to lack of cmdlet\r\nbinding<\/span>\r\n<span style=\"color: #b5fcfc;\">[5]: $PSDefaultParameterValues<\/span>\r\n\r\nName                           Value\r\n----                           -----\r\nTest-Cmdlet2:Mode              internal\r\nTest-Cmdlet1:Mode              internal\r\n\r\n<span style=\"color: #b5fcfc;\">[6]: Test-Cmdlet1<\/span>\r\nCmdlet1: Mode is [internal]\r\n\r\n<span style=\"color: #b5fcfc;\">[7]: Test-Cmdlet2<\/span>\r\nCmdlet2: Mode is [internal], Color is [], Name is []\r\n\r\n<span style=\"color: #b5fcfc;\">[8]: Test-Cmdlet3<\/span>\r\nCmdlet3: Mode is []\r\n\r\n<span style=\"color: #b5fcfc;\">[9]: Test-Cmdlet1 -Mode 'something else'<\/span>\r\nCmdlet1: Mode is [something else]\r\n\r\n<\/pre>\n<p>In command [4] you run <code>Set-DefaultParameter<\/code>. Adding <code>-Verbose<\/code> shows the cmdlets that have been processed from the module. Even though all three cmdlets above accept a <code>Mode<\/code> parameter, <code>$PSDefaultParameterValues<\/code> only works with true cmdlets, not plain functions. <code>Test-Cmdlet3<\/code> is just a plain function because it lacks the <code>CmdletBinding<\/code> attribute so it is skipped. (To be clear, there is nothing stopping you from adding <code>Test-Cmdlet3<\/code> to <code>$PSDefaultParameterValues<\/code> but it just would not do anything.) In command [5], then, you see the updated contents of <code>$PSDefaultParameterValues<\/code>. Commands [6], [7], and [8] show execution of the cmdlets in DemoModule; observe that the default has been automatically applied to the first two. Command [9] shows that if you do provide an explicit value, that nicely overrides the automatic default.<\/p>\n<h2>Conclusion<\/h2>\n<p>I am continually finding little known but powerful productivity boosters like <code>$PSDefaultParameterValues<\/code> in PowerShell; the language continues to surprise and delight me, though I&#8217;ve been using it for quite some time. I think you will agree that with just this one system variable, there are a variety of ways you can leverage it to save you time in your daily work.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Because PowerShell needs to be usable as an immediate scripting language by IT professionals who type in commands at a console,  there have to be language devices such as aliases that can make for terseness when appropriate.  There are several ways of cutting down the verbiage in a script, and  being able to specify default values via $PSDefaultParameterValues is one of the more generally useful ones. Michael Sorens explains how it can save you time in your daily work.&hellip;<\/p>\n","protected":false},"author":221868,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[35],"tags":[],"coauthors":[6802],"class_list":["post-69766","post","type-post","status-publish","format-standard","hentry","category-powershell"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/69766","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221868"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=69766"}],"version-history":[{"count":21,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/69766\/revisions"}],"predecessor-version":[{"id":69831,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/69766\/revisions\/69831"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=69766"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=69766"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=69766"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=69766"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}