Tanaka Saito
Tanaka Saito

Reputation: 1122

Powershell integer parameter validation with multiple ranges

I know you can declare an integer parameter that only accepts values within a specific range:

[Parameter()][ValidateRange(1024,66535)]$Port

Is it possible to validate parameter input for several ranges? For example, say I want to allow port inputs to be 1 through 80, 135 through 445, and 1024 through 65535, I could do it with:

[Parameter()][ValidateRange(1,66535)]$Port

if ((($Port -gt 80) -and ($Port -lt 135)) -or (($Port -gt 445) -and ($Port -lt 1024))) {
    
    Write-Error "Incorrect input, please enter a value between 1-80, 135-445, or 1024-65535"
    break

}

However that doesn't strike me as a particularly neat way of doing things. ValidateSet() also can't take number ranges like 1024..65535. Anyone have a better idea of input validation for multiple integer ranges?

Upvotes: 3

Views: 1289

Answers (2)

Santiago Squarzon
Santiago Squarzon

Reputation: 61313

You can definitely implement your custom ValidateRange attribute declaration by creating a class that inherits from the ValidateEnumeratedArgumentsAttribute Class. Here is a little example:

class ValidateCustomRange : Management.Automation.ValidateEnumeratedArgumentsAttribute {
    [void] ValidateElement([object] $Element) {
        if($Element -gt 80 -and $Element -lt 135 -or $Element -gt 445 -and $Element -lt 1024) {
            throw [Management.Automation.MetadataException]::new(
                'Invalid range! Must be ...your call here :)'
            )
        }
    }
}

function Test-Range {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateCustomRange()]
        [int] $Port
    )
}

Test-Range 80 # All good!
Test-Range 81 # Error

Upvotes: 4

mklement0
mklement0

Reputation: 440677

To complement Santiago Squarzon's helpful answer with an alternative that may be of interest for making a function or script self-contained:

Using the [ValidateScript()] allows you to specify a script block for validation, in which you can refer to the argument given via $_

  • That way you don't need to declare a separate class, which in a stand-alone script you wouldn't be able to reference in your script's param(...) block, which must come first in the file.

  • Conversely, however, if you're authoring a module, where you can package your classes along with the exported functions that use them, Santiago's ValidateEnumeratedArgumentsAttribute-based class solution may be preferable.

A solution that also works in Windows PowerShell (throw is used to provide a meaningful custom error message):

function foo {
 param(
   [ValidateScript({ 
     if ($_ -in 1..80 + 135..445 + 1024..65535) { return $true }
     throw "Value $_ is not between 1 and 80, 135 and 445, or 1024 and 65535."
   })]
   $Port
 )
 $Port # Sample output: echo the port given, if it passed validation
}

foo 10  # OK
foo 100 # Fails validation.

A simplified solution that requires PowerShell (Core) 7+, where you can now use an ErrorMessage property to provide a custom error message:

# PS v7+, using the ErrorMessage property.
function foo {
 param(
   [ValidateScript(
     { $_ -in 1..80 + 135..445 + 1024..65535 },
     ErrorMessage = 'Value {0} is not between 1 and 80, 135 and 445, or 1024 and 65535.'
   )]
   $Port
 )
 $Port # Sample output: echo the port given, if it passed validation
}

foo 10  # OK
foo 100 # Fails validation.

Upvotes: 2

Related Questions