I was working on a project with an advanced PowerShell function. One of the goals was to take advantage of the common parameters like -ErrorVariable and -WarningVariable so that when you run the function you can save errors and warnings and work with them later. Turns out one of these works and one doesn't. But I worked out a hack for the obstinate one.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
The common parameters are supported on all cmdlets and any PowerShell script or function using cmdletbinding. That's the thing you see at the beginning of most scripts:
[cc lang="PowerShell"]
[cmdletBinding()]
[/cc]
This is what allows you to use commands like Write-Verbose in your function that will only execute when you pass -Verbose when running the function. Actually, the command always runs it's just that the verbose message doesn't get written unless you use -Verbose. You can do the same thing with Write-Warning.
A related common parameter is -WarningVariable (and its companion -ErrorVariable). When you use these parameters to call your function, warnings and errors should be written to these variables.
[cc lang="PowerShell"]
PS C:\> MyFunction -name "Jeff" -warningvariable wv -errorvariable ev -verbose
[/cc]
Any Write-Verbose messages will be displayed, warnings will be stored in $wv and errors in $ev. The error variable works as designed, warnings do not. This has been reported as a bug on the Connect site. Although if I had checked there first, I might not have figured out a work around.
It turns out this will still work, but you have to explicitly use the -WarningVariable parameter with any Write-Warning expressions in your script or function. The value will be the WarningVariable value from $PSBoundParameters. Use an expression like this:
[cc lang="Powershell"]
Write-Warning "Foo Warning $(Get-Date)" -WarningVariable $PSBoundParameters.WarningVariable
[/cc]
The second part to the hack is that when you invoke the function and use the -WarningVariable, you need to specify the variable name with the + sign. This tells PowerShell to append to the variable. Now I can run my command and save warnings and errors to their respective variables.
[cc lang="PowerShell"]
PS C:\> myfunction -WarningVariable +wv -ErrorVariable ev -name foober -verbose
[/cc]
Here's the code:
[cc lang="PowerShell"]
Function MyFunction {
[cmdletbinding()]
Param(
[string]$Name="Chaokoh")
write-verbose "Running the function"
#$PSBoundParameters
Get-Date
write $name
Write-Warning "Foo Warning $(Get-Date)" -WarningVariable $PSBoundParameters.WarningVariable
Try {
Write-Host "I'm trying..." -Foreground green
Write-Error "I am problem $(Get-Random) $(Get-Date)" -ErrorAction Stop
}
Catch {
Write-Host "Exception caught" -ForegroundColor red
Write-Warning "oops $(Get-date)" -WarningVariable $PSBoundParameters.WarningVariable
}
Write-Warning "Foo Warning $(Get-Date)" -WarningVariable $PSBoundParameters.WarningVariable
write-error "I am another error"
Get-date
}
[/cc]
Or if you would like to try this out yourself, you can download Myfunction.ps1.
Hi Jeff,
Let me see if I have understood properly.
Using [cmdletBinding()] at the beggining of a script, allows common parameters to be used with all cmdlets, even the ones that usually would not allow this parameters?
Not quite. Using cmdletbinding means your script or function can use the common parameters. These also get passed to cmdlets that already support them. For example, if a cmdlet supports -Verbose and you use it in your function, if you run your function with -Verbose this gets passed down. But the cmdlet has to be written to support it. For example, Get-Service doesn’t use -Verbose. If you run it you get nothing extra. So even putting Get-Service in your function doesn’t change this behavior. Bottom line, if the cmdlet is designed to take advantage of the common parameters, then you can pass that along in your function or script.
The workaround you posted there doesn’t work because the scope on the variable is wrong (the value doesn’t get exported out of your function when I set -WarningVariable) … and you can’t pass null to -WarningAction, so you have to write extra code for that too.
A better workaround is actually in the details of the bug I filed on connect ( https://connect.microsoft.com/PowerShell/feedback/details/508392/warningvariable-doesnt-store-warnings ): always use
$PSCmdlet.WriteWarning()
instead of using Write-Warning.function Test-Warn {
[CmdletBinding()]
param()
$PSCmdlet.WriteWarning("This is a warning")
}
Without using PSCmdlet.WriteWarning you have to add a bunch of code like:
function Test-WarnJeff {
[CmdletBinding()]
param()
## Make sure we have a default value for WarningAction
if( -not $PSBoundParameters.ContainsKey("WarningAction")) {
$PSBoundParameters.WarningAction = $WarningPreference
}
Write-Warning "This is a warning" -WarningVariable $PSBoundParameters.WarningVariable -WarningAction $PSBoundParameters.WarningAction
## Set the variable in the calling scope (do not set global, and make sure you set it even if it's null):
Set-Variable $PSBoundParameters.WarningVariable (Get-Variable $PSBoundParameters.WarningVariable).Value -Scope 1
}
Yeah…I had resorted to $psCmdlet.WriteWarining() but wanted to avoid that. Sadly, I think this is borked until some future version.
I should have added: the problem with both of these approaches is that they don’t affect any cmdlets you call inside your function. You could set the $WarningPreference variable inside your function based on what is passed to you (if anything is passed), but there’s no preference variable for the WarningVariable … so any warnings that come from cmdlets that you call won’t end up in your variable unless you manually add -WarningVariable parameters to every cmdlet or function invocation in your code … and make sure to collect all of them (by default each call just overwrites the variable).
Bottom line: don’t count on WarningVariable or WarningAction
I am not sure if you have discovered this yet but in case anyone is having issues:
Both of the following are true if you copy and paste the function code but not if you run or load the function from a file.
!. ‘Verbose ‘ will not work as expected on 32 bit PoSH unless you explicitly use a ‘Process’ block. WIthout the ‘Process’ block the ‘Write-Verbose’ line will always be displayed. I am waiting to test this on 64 bit and on Win7
2. In 32 bit Posh on XP the ‘CmdletBinding’ must not have a blank line after or it will generate a syntax error when copied and pasted.
Thanks Jeff. This is a very good demo and discussion of the use of ‘Commaon Parameters’.
I forgot this piece. It is a little shorthand that makes these parameters much more friendly to type.
myfunction -ea 0 -wa 0 -v
Tis will suppress all error and warning messages and enable verbose messages