PowerShell Parameter Validation for Safer Scripts
Validation attributes stop a PowerShell script before it runs on bad input, but ValidateSet and ValidatePattern ignore case unless told not to.

On this page
TL;DR
Add a validation attribute to each parameter: ValidateSet for a fixed list,
ValidatePattern for a format, ValidateRange for a number and
ValidateScript for anything else. PowerShell checks every value a caller
passes before the script’s first line runs, and stops with an error naming the
parameter. It never checks a default, so keep each default inside its own rule:
#Requires -Version 7
param(
[Parameter(Mandatory)]
[ValidateSet('dev', 'staging', 'prod', IgnoreCase = $false)]
[string]$Environment,
[Parameter(Mandatory)]
[ValidatePattern('^app-[a-z0-9-]+\z', Options = 'None',
ErrorMessage = '{0} is not a service name. Use app- and lowercase letters, digits or hyphens.')]
[string]$ServiceName,
[ValidateRange(1, 365)]
[int]$DaysToKeep = 30,
[Parameter(Mandatory)]
[ValidateScript({ Test-Path -Path $_ -PathType Container },
ErrorMessage = 'Folder {0} does not exist.')]
[string]$LogRoot
)ValidateSet and ValidatePattern ignore case by default, so Prod passes a
set of prod. IgnoreCase = $false and Options = 'None' make them exact.
Windows PowerShell 5.1 can’t parse this param block, because ErrorMessage
exists from PowerShell 6 on; drop that argument to run it there.
A script that deletes, deploys or overwrites is only as safe as the values it’s
given. A -DaysToKeep of 0 on a log cleanup removes every log; an environment
typed Prod instead of prod can slip past a later if ($Environment -ceq 'prod') and take the wrong branch. Validation attributes move those checks to
the parameter itself, where PowerShell enforces them on every value passed in,
before anything runs, and reports which parameter was wrong.
The example is a log cleanup script, Remove-OldLog.ps1, with four parameters.
Each section adds the rule for one of them.
Make the required parameters mandatory
[Parameter(Mandatory)] makes PowerShell refuse to run without a value. Run
interactively, it prompts for the missing value; run with -NonInteractive,
it stops with an error instead. It also
rejects an empty string, so -ServiceName '' fails before any other rule sees
it.
[Parameter(Mandatory)]
[string]$ServiceNameLimit a value to a list with ValidateSet
ValidateSet accepts only the values it names, and tab completion offers
them. It compares without case unless IgnoreCase is $false; with the
default, Prod passes and reaches the script with its capital P.
[ValidateSet('dev', 'staging', 'prod', IgnoreCase = $false)]
[string]$EnvironmentCheck a format with ValidatePattern
ValidatePattern matches the value against a regular expression. Like
ValidateSet, it ignores case by default; Options = 'None' turns that off.
Its default error message repeats the regular expression back at the reader,
so give it an ErrorMessage that says what a valid value looks like. {0} is
the value that failed.
[ValidatePattern('^app-[a-z0-9-]+\z', Options = 'None',
ErrorMessage = '{0} is not a service name. Use app- and lowercase letters, digits or hyphens.')]
[string]$ServiceNameAnchor the pattern with ^ and \z. Without anchors, app-api; Remove-Item
passes because it contains a match. Use \z rather than $: in .NET, $ also
matches before a trailing newline, so app-api followed by a newline passes
^app-[a-z0-9-]+$ and reaches the script with the newline still in it.
Bound a number with ValidateRange
ValidateRange sets an inclusive minimum and maximum. For a cleanup script,
the minimum is the safety rule: a 0 here would delete every log up to now.
[ValidateRange(1, 365)]
[int]$DaysToKeep = 30Check anything else with ValidateScript
ValidateScript runs a script block with the value in $_ and accepts it
when the block’s result is truthy. A result of 0, an empty string, $null or
no output fails it as surely as $false, so end the block on a test that
returns $true. Use it for checks no other attribute makes, such as
whether a folder exists.
[ValidateScript({ Test-Path -Path $_ -PathType Container },
ErrorMessage = 'Folder {0} does not exist.')]
[string]$LogRootExpected result
Valid input runs the script. Anything else stops it before the first line, with the parameter named:
PS> ./Remove-OldLog.ps1 -Environment prod -ServiceName app-api -DaysToKeep 0 -LogRoot ./logsOutput
Remove-OldLog.ps1: Cannot validate argument on parameter 'DaysToKeep'. The 0 argument is less than the minimum allowed range of 1. Supply an argument that is greater than or equal to 1 and then try the command again.The error is terminating, so a caller’s try/catch sees it, and nothing in
the script body has run.
Common failures
ValidateSet and ValidatePattern ignore case by default
ValidateSet and ValidatePattern compare without case unless told
otherwise, and the value reaches the script exactly as typed. Add
IgnoreCase = $false to ValidateSet and Options = 'None' to
ValidatePattern. With them, the mistyped value stops at the boundary:
Error
Remove-OldLog.ps1: Cannot validate argument on parameter 'Environment'. The argument "Prod" does not belong to the set "dev,staging,prod" specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again.Default values are never validated
PowerShell validates the values a caller passes, never the default. A
[ValidateRange(1, 365)] parameter with a default of 0 runs with 0. Check each
default against its own rule when you write it.
Invalid assignment inside the script doesn’t stop it
The attribute stays on the variable, so a later assignment is checked too. A failed check on an assignment ends only that statement: PowerShell prints the error, keeps the old value and runs the next line.
Error
MetadataError: <folder>/Remove-OldLog.ps1:7
Line |
7 | $DaysToKeep = 0
| ~~~~~~~~~~~~~~~
| The variable cannot be validated because the value 0 is not a valid
| value for the DaysToKeep variable.Treat that message as a bug in the script. try/catch catches it, and
$ErrorActionPreference = 'Stop' at the top makes it end the run.
ValidatePattern’s default error shows the regular expression
Without an ErrorMessage, a failed pattern reports the pattern itself:
Error
Remove-OldLog.ps1: Cannot validate argument on parameter 'ServiceName'. The argument "API" does not match the "^app-[a-z0-9-]+\z" pattern. Supply an argument that matches "^app-[a-z0-9-]+\z" and try the command again.Add ErrorMessage to ValidatePattern and ValidateScript, as in the earlier
steps. It exists from PowerShell 6 on, so Windows PowerShell 5.1 can’t run a
param block that uses it; the #Requires -Version 7 line at the top of the
script states that requirement before anything else. I tested only PowerShell
7.6.6. about_Functions_Advanced_Parameters on Microsoft Learn (opens in a new tab)
lists every validation attribute.
Scheduled run stops on a missing value
A mandatory parameter prompts when someone is at the console. Started with
-NonInteractive, the script stops instead:
Error
Remove-OldLog.ps1: Cannot process command because of one or more missing mandatory parameters: LogRoot.Start the task with pwsh -NonInteractive -File and pass every mandatory
parameter on its command line.
Tested with
- PowerShell
- 7.6.6
- OS
- macOS 27.0
- Date
- October 2026