Summary: Microsoft PowerShell MVP and guest blogger Dave Moravec talks about using default parameter values to simplify Windows PowerShell scripts.

Microsoft Scripting Guy, Ed Wilson, is here. Well, our European Tour is drawing down, but it’s not over yet. Yesterday, we had a great time with Bartek in Warsaw, and last night we took the train back from Warsaw to Berlin.

This morning, we are on a train to Prague to see Windows PowerShell MVP Dave Moravec. We will not have too much time—only about six hours before we have to hop back on a train to Frankfurt. But when Dave began talking to the Scripting Wife on Facebook and on Twitter, well, it was decided that we had the time, and we wanted to meet Dave. In honor of the occasion, I asked Dave to write a little bit about his favorite Windows PowerShell 3.0 feature. I will admit I was a bit surprised by his choice—perhaps you will be as well.

Take it away, Dave …

I have been in the IT industry for 15 years. The last 10 years working mainly with Microsoft management technologies. I work for one of the biggest international accounting companies as an administrator responsible for System Center: Configuration Manager (SCCM). I like to automate my infrastructure-related tasks with Windows PowerShell. I’m an author and speaker for Microsoft Czech Republic, and I run my own blog at PowerShell.cz. You can reach me on Twitter at @makovec.

Changing default parameter values

When I was asked to write about my favorite Windows PowerShell 3.0 feature, my #1 $PSDefaultParameterValues came to mind immediately. From my point of view, this was something I was looking for, for a long time.

How does it work? With $PSDefaultParameterValues, you can define (overwrite) default values of parameters for Windows PowerShell cmdlets. Actually, I just found that Scripting Guy already showed a nice usage of this a month ago in his PowerTip: Automatically Format Your PowerShell Table. Let me show you a few other practical examples.

How to set a default value

Let’s imagine you work frequently with Windows PowerShell jobs (and I am sure you do!). So I will use a really complicated example:

  PS> Start-Job -Name sj -ScriptBlock { $pid } | Wait-Job | Receive-Job

  8828

  PS> Receive-Job -Name sj

  PS>

No result received, as expected. If you want to leave the data in queue, you have to use the -Keep switch parameter. By using $PSDefaultParameterValues, you can tell PowerShell to set -Keep to True every time. $PSDefaultParameterValues is a hash table where the Key is combination of CmdletName:ParameterName and Value is the actual value you want to assign. In our example, the assignment is:

 PS> $PSDefaultParameterValues = @{'Receive-Job:Keep'=$true}

You see that for the Keep parameter of the Receive-Job cmdlet, we used variable $true (as it is a switch, True means it’s enabled), so now we can write:

  PS> Start-Job -Name sj2 -ScriptBlock { $pid } | Wait-Job | Receive-Job

  2433

  PS> Receive-Job -Name sj2

  2432

  PS>

If you check $PSDefaultParameterValues, you can see that it’s really a hash table.

  PS> $PSDefaultParameterValues

  Name                   Value

  ----                   -----

  Receive-Job:Keep       True

If you want to overwrite the actual default value, you can specify $false for Keep parameter

  PS> Start-Job -Name sj3 -ScriptBlock { $pid } | Wait-Job | Receive-Job –Keep:$false

  998

  PS> Receive-Job -Name sj3

  PS>

Another usage for $PSDefaultParameterValues I found relates to work with the Export-Csv cmdlet. I export a lot of data from our infrastructure very often and pass them on in CSV format to my boss. Therefore, I don’t like the default behavior, which includes type info at the beginning of the file. I decided to change that. Furthermore, as work with different language settings, I also changed the delimiter.

  PS> $PSDefaultParameterValues = @{'Export-Csv:Delimiter'=';'; 'Export-Csv:NoTypeInformation'=$true}

  PS> Get-Process -Name p* | Select Name, WS | Export-Csv -Path c:\tmp\ps.csv

  PS> Get-Content C:\tmp\ps.csv

  “Name”;”WS”

  “powershell_ise”;”17477223’

 

  PS> $PSDefaultParameterValues

  Name                            Value

  ----                            -----

  Export-Csv:Delimiter            ;

  Export-Csv:NoTypeInformation    True

Works like a charm, but in the last output, you may have noticed an unpleasant surprise. Our modification from the first example (Receive-Job) disappeared. You probably discovered it happened during our second modification of $PSDefaultParameterValues. To avoid this, we will use techniques for work with the hash tables. There are two different possibilities:

  PS> $PSDefaultParameterValues['Receive-Job:Keep']=$true

  PS> $PSDefaultParameterValues.Add('Enter-PSSession:ComputerName', 'localhost')

After that, two additional default values were received, and we should have four in total now. Let’s check it:

  PS> $PSDefaultParameterValues

  Name                            Value

  ----                            -----

  Export-Csv:Delimiter            ;

  Receive-Job:Keep                True

  Export-Csv:NoTypeInformation    True

  Enter-PSSession:ComputerName    localhost

You can immediately test newly added values. As of now, if you write Enter-PSSession, you will be connected to your local computer by default. This is probably not so useful, so feel free to modify it for the server you are mostly connecting to.

There are other options for $PSDefaultParameterValues modification:

  • $PSDefaultParameterValues = @{'Get-ChildItem:Force' = $true} will allow you to run dir command with force enabled, so you’ll see hidden files and folders.
  • $PSDefaultParameterValues = @{'*:Credential' = $cred} is very useful if you have your administrator credentials stored in a variable. As $PSDefaultParameterValues supports wild cards. This example will use these credentials for all cmdlets supporting it.
  • Do you know people who are adjusting their watch ten minutes ahead? Let’s replicate this in Windows PowerShell: $PSDefaultParameterValues = @{'Get-Date:Date' = {[System.DateTime]::Now.AddMinutes(10)}} Here I used the possibility of $PSDefaultParameterValues to use ScriptBlock as value for the parameter.

The last one was a bit funny, I know. You can also do some awful assignments, but I will not show you any examples.

Changing the default value

If you want to change the actual value, you can use a standard operation used for work with the hash tables. We will change the delimiter for the Export-Csv cmdlet:

  PS> $PSDefaultParameterValues[‘Export-Csv:Delimiter’]=’%’

Now all my exported CSVs will be delimited by the percent sign. If I don’t like it, I can overwrite the behavior as I did in the first example with Receive-Job.

  PS> Get-Process –Name p* | Select Name, WS | Export-Csv –Path c:\tmp\ps.csv –Delimiter ‘,’

  PS> Get-Content c:\tmp\ps.csv

  “Name”,”WS”

  “powershell_ise”,”17477223’

Removing the default value

If you want to remove your settings from $PSDefaultParameterValues, you can remove just a single record, or you can remove all records at once. Removing a single record is as easy as adding one; just use Remove method of the hash table:

  PS> $PSDefaultParameterValues

  PS> $PSDefaultParameterValues.Remove(‘Enter-PSSession:ComputerName’)

  PS> $PSDefaultParameterValues

Removing the whole variable is arranged by using the Remove-Variable cmdlet. As this variable is automatic, Remove-Variable cleans its content only. As the Windows PowerShell team is full of clever people, they invented an additional option—temporarily disabling $PSDefaultParameterValues. You can set its Disable key to True and after that any of $PSDefaultParameterValues will apply.

Image of command output

You can see how it works from the above image. I removed all actual values from $PSDefaultParameterValues and then set Delimiter for Export-Csv. The next lines contain testing of disabled and enabled functionality of $PSDefaultParameterValues. As a confirmation, you see the content of all three exported files.

A few extra tips

You are probably aware of scoping in Windows PowerShell (if not, please read the about_Scopes Help file). If you define $PSDefaultParameterValues in your Global scope, it will be inherited to your script or function scope. This can sometimes lead to a really bad side effect. You can use disabling of $PSDefaultParameterValues in this case.

The second point you need to be aware of is that $PSDefaultParameterValues is active in your current session. If you have some useful assignments, you have to define it in your profile script.

For further investigation of the $PSDefaultParameterValues concept, see about_Parameters_Default_Values.

~David

Thank you, David, for an excellent article.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy