Reputation: 462
I'm working on a PowerShell library that automates some network management operations. Some of these operations have arbitrary delays, and each can fail in unique ways. To handle these delays gracefully, I'm creating a generic retry function that has three main purposes:
The problem is item #2. I want to be able to specify the expected exception type for the command. How can I do this in PowerShell?
Here's my function:
Function Retry-Command {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True, Position=0)]
[String] $name,
[Parameter(Mandatory=$True, Position=1)]
[String] $scriptBlock,
[String[]] $argumentList,
[Int] $maxAttempts=3,
[Int] $retrySeconds=10,
[System.Exception] $retryException=[System.Management.Automation.RuntimeException]
)
$attempts = 1
$keepTrying = $True
$cmd = [ScriptBlock]::Create($scriptblock)
do {
try {
&$cmd @argumentList
$keepTrying = $False
Write-Verbose "Command [$commandName] succeeded after $attmpts attempts."
} catch [$retryException] {
$msg = "Command [$commandName] failed. Attempt $attempts of $maxAttempts."
Write-Verbose $msg;
if ($maxAttempts -gt $attempts) {
Write-Debug "Sleeping $retrySeconds"
Start-Sleep -Seconds $retrySeconds
} else {
$keepTrying = $False
Write-Debug "Reached $attempts attempts. Re-raising exception."
Throw $_.Exception
}
} catch [System.Exception] {
$keepTrying = $False
$msg = "Unexpected exception while executing command [$CommandName]: "
Write-Error $msg + $_.Exception.ToString()
Throw $_.Exception
} finally {
$attempts += 1
}
} while ($True -eq $keepTrying)
}
I call it like this:
$result = Retry-Command -Name = "Foo the bar" -ScriptBlock $cmd -ArgumentList $cmdArgs
But this is the result:
Retry-Command : Cannot process argument transformation on parameter 'retryException'.
Cannot convert the "System.Management.Automation.RuntimeException" value of type "System.RuntimeType" to type "System.Exception".
At Foo.ps1:111 char:11
+ $result = Retry-Command <<<< -Name "Foo the bar" -ScriptBlock $cmd -ArgumentList $cmdArgs
+ CategoryInfo : InvalidData: (:) [Retry-Command], ParameterBindin...mationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,Retry-Command
This seems to be saying that the type of [System.Management.Automation.RuntimeException]
is not itself a [System.Exception]
, but is instead a [System.RuntimeType]
which makes sense.
So, how do I specify the type of the exception to be caught?
Upvotes: 3
Views: 2903
Reputation: 54871
It's not possible to use a variable as a catch criteria, it has to be a type-object (or something), everything else gives you an error. A workaround would be something like this:
#You can get the name of the exception using the following (or .Name for just the short name)
#PS > $myerr.Exception.GetType().Fullname
#System.UnauthorizedAccessException
function test {
param(
#Validate that specified name is a class that inherits from System.Exception base class
[ValidateScript({[System.Exception].IsAssignableFrom([type]$_)})]
$ExceptionType
)
try {
#Test-script, Will throw UnauthorizedAccessException when not run as admin
(Get-Content C:\test.txt) | % { $_ -replace 'test','lol' } | Set-Content C:\test.txt
}
catch [System.Exception] {
#Check if exceptiontype is equal to the value specified in exceptiontype parameter
if($_.Exception.GetType() -eq ([type]$ExceptionType)) {
"Hello. You caught me"
} else {
"Uncaught stuff: $($_.Exception.Gettype())"
}
}
}
A few tests. One with non-existing type, then with non-exception type, and finally a working one
PS > test -ExceptionType system.unaut
test : Cannot validate argument on parameter 'ExceptionType'. Cannot convert the "system.unaut" val
ue of type "System.String" to type "System.Type".
At line:1 char:21
+ test -ExceptionType system.unaut
+ ~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [test], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,test
PS > test -ExceptionType String
test : Cannot validate argument on parameter 'ExceptionType'. The "[System.Exception].IsAssignableF
rom([type]$_)" validation script for the argument with value "String" did not return true. Determin
e why the validation script failed and then try the command again.
At line:1 char:21
+ test -ExceptionType String
+ ~~~~~~
+ CategoryInfo : InvalidData: (:) [test], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,test
PS > test -ExceptionType UnauthorizedAccessException
Hello. You caught me
Upvotes: 2