Introduction
I know your wondering, why isn’t he writing about AI, and just talking about ValidateSet? If your in the early stages of your PowerShell journey, you may not know about ValidateSet and it is a great feature to know and to make sure your functions have it that you create. Have you ever written or used a PowerShell function where users could enter any value, only to have your script fail because they typed “prod” instead of “Production”? Or maybe they entered “DEvelopment” with creative capitalization? This is where ValidateSet becomes your best friend.
ValidateSet is a parameter attribute that restricts parameter values to a predefined set of options. Think of it as a dropdown menu for your PowerShell functions as users can only choose from the options you provide. Also it can save you big time, if you put an invalid value in, and it partially runs while not having the right checks and balances in, it could do a lot of damage. If you have ValidateSet in place, it won’t let an invalid value be inputted.
Why Use ValidateSet?
Before we dive into the how, let’s understand the why:
- Error Prevention: Stop invalid values before they cause problems
- User Guidance: Users see exactly what options are available
- Tab Completion: PowerShell automatically provides tab completion for your valid options
- Self-Documenting Code: The valid options are visible in your function definition
- Consistency: Ensures everyone uses the same values across your organization
Now, let’s build your ValidateSet skills step by step!
Level 1: Basic ValidateSet
Let’s start with the simplest example. Imagine you’re creating a function to restart a service, but only for specific environments:
function Restart-AppService {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[ValidateSet('Development', 'Testing', 'Production')]
[string]$Environment
)
Write-Host "Restarting service in $Environment environment..." -ForegroundColor Green
# Your restart logic here
}
What’s happening here?
- The
[ValidateSet()]attribute limits$Environmentto exactly three values - If someone tries
Restart-AppService -Environment "Staging", PowerShell will throw an error - Users can press TAB after typing
-Environmentto cycle through valid options
Try it yourself:
# This works ✓
Restart-AppService -Environment Development
# This fails ✗
Restart-AppService -Environment Staging
# Error: Cannot validate argument on parameter 'Environment'
Level 2: Case Sensitivity and Tab Completion
One beautiful feature of ValidateSet is that it’s case-insensitive by default:
function Set-LogLevel {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[ValidateSet('Verbose', 'Info', 'Warning', 'Error', 'Critical')]
[string]$Level
)
Write-Host "Log level set to: $Level"
}
# All of these work!
Set-LogLevel -Level verbose # ✓
Set-LogLevel -Level VERBOSE # ✓
Set-LogLevel -Level VeRbOsE # ✓
Set-LogLevel -Level Verbose # ✓
The Magic of Tab Completion:
When you type Set-LogLevel -Level and press TAB, PowerShell cycles through:
- Verbose → Info → Warning → Error → Critical → Verbose → …
This significantly improves user experience and reduces typing errors!
Level 3: Multiple Parameters with ValidateSet
Real-world functions often need multiple validated parameters. Let’s build upon our knowledge:
function Deploy-Application {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[ValidateSet('Development', 'Testing', 'Staging', 'Production')]
[string]$Environment,
[Parameter(Mandatory)]
[ValidateSet('East', 'West', 'Central', 'Europe', 'Asia')]
[string]$Region,
[Parameter()]
[ValidateSet('IIS', 'Apache', 'Nginx')]
[string]$WebServer = 'IIS'
)
Write-Host "Deploying to $Environment environment in $Region region using $WebServer" -ForegroundColor Cyan
}
Key Points:
- Each parameter has its own ValidateSet
- You can combine ValidateSet with default values (
$WebServer = 'IIS') - The default value must be one of the valid set values
# Example usage
Deploy-Application -Environment Production -Region East
# Output: Deploying to Production environment in East region using IIS
Level 4: Dynamic ValidateSet
Here’s where things get exciting! What if your valid options need to change based on your environment or context? Enter dynamic ValidateSet using classes. Make sure your .txt file only has 1 value per row with no commas, or it won’t read it correctly.
# First, create a class that implements IValidateSetValuesGenerator
class EnvironmentNamesGenerator : System.Management.Automation.IValidateSetValuesGenerator {
[string[]] GetValidValues() {
# This could read from a config file, database, or API
$environments = Get-Content "C:\Config\environments.txt" -ErrorAction SilentlyContinue
if ($environments) {
return $environments
}
# Fallback to default values
return @('Development', 'Testing', 'Production')
}
}
function Connect-ToEnvironment {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[ValidateSet([EnvironmentNamesGenerator])]
[string]$Environment
)
Write-Host "Connecting to $Environment..." -ForegroundColor Green
}
Why is this powerful?
- Valid values can come from files, databases, APIs, or Active Directory
- Values update without modifying the function
- Tab completion still works!
Level 5: ValidateSet with Arrays
Sometimes users need to select multiple values. ValidateSet works beautifully with arrays:
function Install-Features {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[ValidateSet('WebServer', 'Database', 'MessageQueue', 'Cache', 'Monitoring')]
[string[]]$Features
)
foreach ($feature in $Features) {
Write-Host "Installing $feature..." -ForegroundColor Yellow
# Installation logic here
}
}
# Usage - multiple valid values
Install-Features -Features WebServer, Database, Cache
# Each value is still validated
Install-Features -Features WebServer, InvalidFeature # ✗ Will fail
Level 6: Combining ValidateSet with Other Validators
You can stack multiple validation attributes for robust parameter validation:
function New-ServerName {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[ValidateSet('DEV', 'TEST', 'PROD')]
[string]$Environment,
[Parameter(Mandatory)]
[ValidateSet('WEB', 'DB', 'APP', 'API')]
[string]$Type,
[Parameter(Mandatory)]
[ValidateRange(1, 999)]
[int]$Number
)
$serverName = "{0}-{1}-{2:D3}" -f $Environment, $Type, $Number
Write-Host "Server name: $serverName" -ForegroundColor Green
return $serverName
}
# Usage
New-ServerName -Environment PROD -Type WEB -Number 42
# Output: PROD-WEB-042
Level 7: Real-World Enterprise Example
Let’s combine everything we’ve learned into a practical, enterprise-ready function:
# Dynamic validator for Azure regions
class AzureRegionsGenerator : System.Management.Automation.IValidateSetValuesGenerator {
[string[]] GetValidValues() {
# Get actual Azure locations (in real scenario)
# For demo, we'll use static list
return @('eastus', 'westus', 'centralus', 'northeurope', 'westeurope', 'southeastasia')
}
}
function New-AzureResource {
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter(Mandatory)]
[ValidateSet('Development', 'Testing', 'Staging', 'Production')]
[string]$Environment,
[Parameter(Mandatory)]
[ValidateSet([AzureRegionsGenerator])]
[string]$Location,
[Parameter(Mandatory)]
[ValidateSet('Basic', 'Standard', 'Premium')]
[string]$Tier,
[Parameter()]
[ValidateSet('Windows', 'Linux')]
[string]$OS = 'Windows',
[Parameter()]
[ValidateSet('Small', 'Medium', 'Large')]
[string[]]$Features = @('Small')
)
# Generate resource name
$resourceName = "rg-$Environment-$Location-$(Get-Date -Format 'yyyyMMdd')".ToLower()
if ($PSCmdlet.ShouldProcess($resourceName, "Create Azure Resource")) {
Write-Host "`nCreating Azure Resource:" -ForegroundColor Cyan
Write-Host " Name: $resourceName" -ForegroundColor White
Write-Host " Location: $Location" -ForegroundColor White
Write-Host " Tier: $Tier" -ForegroundColor White
Write-Host " OS: $OS" -ForegroundColor White
Write-Host " Features: $($Features -join ', ')" -ForegroundColor White
# Your actual Azure creation logic here
return $resourceName
}
}
# Usage examples
New-AzureResource -Environment Production -Location eastus -Tier Premium
New-AzureResource -Environment Development -Location westeurope -Tier Basic -OS Linux -Features Small, Medium
Common Mistake to Avoid
Forgetting ValidateSet Creates Strings
# The parameter is always a string, even if it looks like a number
function Set-Priority {
param (
[ValidateSet('1', '2', '3')]
[string]$Priority # Note: string type
)
# Need to convert if doing numeric operations
$numPriority = [int]$Priority
}
Conclusion
ValidateSet is a powerful, user-friendly feature that makes your PowerShell functions more robust and easier to use. Start with basic static sets, and as your needs grow, use dynamic ValidateSet with classes. I will say I haven’t used dynamic ValidateSet a much more than a few with no issues, but have heard to only use it when necessary. Maybe I’ll do a deep dive into it at sompoint.
Remember:
- ✓ Always use ValidateSet when there’s a limited, known set of valid values
- ✓ Leverage tab completion to improve user experience
- ✓ Use dynamic ValidateSet for values that change frequently
- ✓ Combine with other validators for bulletproof parameters
- ✓ Document your choices with comments
Happy scripting! 🚀
Practice Exercise
Try creating this function yourself:
# Create a function that:
# 1. Takes a ComputerName parameter (mandatory)
# 2. Takes an Action parameter that validates against: 'Restart', 'Shutdown', 'LogOff'
# 3. Takes an optional Force parameter (boolean)
# 4. Writes what action would be performed
Below is one way to do it, but try yourself first!
```powershell
# Solution:
function Invoke-RemoteAction {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]$ComputerName,
[Parameter(Mandatory)]
[ValidateSet('Restart', 'Shutdown', 'LogOff')]
[string]$Action,
[Parameter()]
[bool]$Force = $false
)
$forceText = if ($Force) { "forcefully" } else { "gracefully" }
Write-Host "Would $Action $ComputerName $forceText" -ForegroundColor Yellow
}
Now take what you’ve learned here and make those functions more secure and easier to use!
What do you think of this style of blog post for learning concepts? Are you strongly for or against it? Let me know and hope you learned something from this blog post and please keep learning and sharing.









