---
title: "PowerShell ValidateSet: Tab-Completion & Parameter Values"
description: "Discover PowerShell ValidateSet and its tab-completion feature for providing valid parameter values in your scripts."
canonical: "https://adamtheautomator.com/powershell-validateset/"
---

# PowerShell ValidateSet: Tab-Completion & Parameter Values

> Discover PowerShell ValidateSet and its tab-completion feature for providing valid parameter values in your scripts.

Source: https://adamtheautomator.com/powershell-validateset/

---

ATA Learning

Tap to hide

[

ATA Learning

](/)

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Search for:  

*   [](https://twitter.com/adbertram)
*   [](https://github.com/Adam-the-Automator)
*   [](https://www.linkedin.com/company/adam-the-automator-llc)
*   [](/feed/)

![PowerShell ValidateSet: Tab-Completion & Parameter Values](https://adamtheautomator.com/wp-content/uploads/2019/10/doors-1767562_1920.jpg)

# PowerShell ValidateSet: Tab-Completion & Parameter Values

[![](https://secure.gravatar.com/avatar/995fcc2faf7fc9d3f15061d8e6c5b84f4b751d96502ef7afb4f9f648a58b36a4?s=192&d=mm&r=g)Stuart Squibb](https://adamtheautomator.com/author/stuart/)23 October 201911 min. read

Categories: [IT Ops](/category/it-ops/)

Tags:[PowerShell](/tag/powershell/)

Table of Contents

*   [PowerShell ValidateSet: A Brief Overview](#validateset-a-brief-overview)
*   [Requirements](#requirements)
*   [Getting Started](#getting-started)
*   [Accepting Input using a Parameter](#accepting-input-using-a-parameter)
*   [The Options are Too Open](#the-options-are-too-open)
*   [Ensuring Only Certain Values are Used](#ensuring-only-certain-values-are-used)
*   [Making PowerShell ValidateSet Case Sensitive](#making-validateset-case-sensitive)
*   [Using Tab Completion](#using-tab-completion)
*   [Understanding Changes to ValidateSet in PowerShell 6.1](#understanding-changes-to-validateset-in-powershell-6-1)
*   [The ErrorMessage Property](#the-errormessage-property)
*   [PowerShell Classes](#powershell-classes)
*   [Using a Class for a ValidateSet Value List: A Real Example](#using-a-class-for-a-validateset-value-list-a-real-example)
*   [Creating a Data Source for the Class](#creating-a-data-source-for-the-class)
*   [Adding the Class to the Script](#adding-the-class-to-the-script)
*   [Running the Script](#running-the-script)
*   [Wrapping up](#wrapping-up)
*   [Further Reading](#further-reading)

When you are writing a PowerShell script or function you often want to accept user input via parameters. If you don’t limit the values those parameters accept, you can guarantee that there will be situations where inappropriate values are supplied. In this article, learn how to use the PowerShell _ValidateSet_ parameter validation attribute to limit those values to only ones you define.

Not a reader? Watch this related video tutorial!

**_Not seeing the video? Make sure your ad blocker is disabled._**

When writing a [PowerShell](https://adamtheautomator.com/tag/powershell/) script or function, you can use many different validation attributes to check that the values supplied to your parameters are acceptable and alert the user if they aren’t.

This article focuses on the _ValidateSet_ validation attribute. You will learn what _ValidateSet_ does, why you might want to use _ValidateSet_ in your code and how to do so. You will also learn about the tab-completion feature that is enabled by ValidateSet and will help users of your code provide valid parameter values.

## PowerShell ValidateSet: A Brief Overview

_ValidateSet_ is a parameter attribute that allows you to define a set of elements that are only accepted as the value for that parameter.

For example, perhaps, you have a script that is defined to work with Active Directory domain controllers. This script has a parameter that specifies the name of a domain controller. Wouldn’t it make sense to limit the list of acceptable values to the actual names of the domain controllers? There’s no reason the user should be able to use “foobar” as a value when you know beforehand what values the script neds. ValidateSet gives you that capability.

## Requirements

This article will be a learning walkthrough. If you plan to follow along, you will need the following:

*   [Visual Studio Code](https://code.visualstudio.com/) or any other code editor. I’ll be using Visual Studio Code.
*   At least [PowerShell 5.1](https://www.microsoft.com/en-us/download/details.aspx?id=54616) for the majority of the code in this article. There is one section that _requires_ [PowerShell 6.1](https://github.com/powershell/powershell) or later and I’ll identify that when we get to it

All of the code in this article has been tested in the following environments:

| Operating System | PowerShell Versions |
| --- | --- |
| Windows 7 SP1 | 5.1, Core 6.2 |
| Windows 10 1903 | 5.1, Core 6.2 |
| Linux Mint 19.2 | Core 6.2 |

To help explain the concepts around _ValidateSet_ you’re going to build a small script called _Get-PlanetSize.ps1_. This script returns information about the sizes of planets in our solar system.

You will start off with a simple script and gradually improve its ability to handle input from the end-user and make discovery of its possible parameter values easy for them.

## Getting Started

To get started, first copy and paste the PowerShell code below into your favorite text editor and save it as _Get-PlanetSize.ps1_.

```powershell
$planets = [ordered]@{
     'Mercury' = 4879
     'Venus'   = 12104
     'Earth'   = 12756
     'Mars'    = 6805
     'Jupiter' = 142984
     'Saturn'  = 120536
     'Uranus'  = 51118
     'Neptune' = 49528
     'Pluto'   = 2306
 }
 $planets.keys | Foreach-Object {
     $output = "The diameter of planet {0} is {1} km" -f $_, $planets[$_]
     Write-Output $output
 }
```

Run the script from a PowerShell prompt and you should get the following:

```powershell
PS51> .\Get-PlanetSize.ps1
 The diameter of planet Mercury is 4879 km
 The diameter of planet Venus is 12104 km
 The diameter of planet Earth is 12756 km
 The diameter of planet Mars is 6805 km
 The diameter of planet Jupiter is 142984 km
 The diameter of planet Saturn is 120536 km
 The diameter of planet Uranus is 51118 km
 The diameter of planet Neptune is 49528 km
 The diameter of planet Pluto is 2306 km
```

Informative but not very flexible; the information for every planet is returned, even if you only want the information for Mars.

Perhaps you’d like the capability to specify a single planet instead of returning all of them. You’d do that by introducing a parameter. Let’s look at how to achieve that next.

## Accepting Input using a Parameter

To allow the script to accept a parameter, add a `Param()` block to the top of the script. Call the parameter `Planet`. A suitable `Param()` block looks like below.

In the example below, the line `[Parameter(Mandatory)]` ensures that a planet name is _always_ supplied to the script. If it’s missing then the script will prompt for one.

```powershell
Param(
     [Parameter(Mandatory)]
     $Planet
 )
```

The simplest way to incorporate this `Planet` parameter into the script is to change the line  `$planets.keys | Foreach-Object {` to `$Planet | Foreach-Object {`. Now you’re not relying on the hashtable statically defined earlier and instead are reading the value of the `Planet` parameter.

Now if you run the script and specify a planet using the `Planet` parameter, you only see information about that particular planet.

```powershell
PS51> .\Get-PlanetSize.ps1 -Planet Mars
 The diameter of planet Mars is 6805 km
```

Excellent. Script completed? Maybe not.

### The Options are Too Open

What happens if you try to find the diameter of the planet _Barsoom_ using _Get-PlanetSize.ps1_ ?

```powershell
PS51> .\Get-PlanetSize.ps1 -Planet Barsoom
The diameter of planet Barsoom is  km
```

Hmm, that’s not right. _Barsoom_ isn’t on the list of planets, but the script runs anyway. How can we fix this?

The problem here is that the script accepts any input and uses it, irrespective of whether it’s a valid value or not. The script needs a way to limit which values are accepted for the `Planet` parameter. Enter _ValidateSet_!

## Ensuring Only Certain Values are Used

A _ValidateSet_ list is a comma-separated list of string values, wrapped in single or double-quotes. Adding a _ValidateSet_ attribute to a script or function parameter consists of adding a line of text to the `Param()` block, as shown below. Replace the `Param()` block in your copy of _Get-PlanetSize.ps1_ with the one below and save the file.

```powershell
Param(
     [Parameter(Mandatory)]
     [ValidateSet("Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune","Pluto")]
     $Planet
 )
```

Try running the script again using _Barsoom_ as your `Planet` parameter. Now a helpful error message is returned. The message is specific in what went wrong and even provides a list of possible values for the parameter.

```powershell
PS51> .\Get-PlanetSize.ps1 -Planet Barsoom
 Get-PlanetSize.ps1 : Cannot validate argument on parameter 'Planet'. The argument "Barsoom" does not belong to the set
 "Mercury,Venus,Earth,Mars,Jupiter,Saturn,Uranus,Neptune,Pluto" specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again.
 At line:1 char:32
 .\Get-PlanetSize.ps1 -Planet Barsoom
 ~~~
 CategoryInfo          : InvalidData: (:) [Get-PlanetSize.ps1], ParameterBindingValidationException
 FullyQualifiedErrorId : ParameterArgumentValidationError,Get-PlanetSize.ps1 
```

### Making PowerShell ValidateSet Case Sensitive

By default, the ValidateSet attribute is case insensitive. This means that it will allow any string granted it’s in the allowed list with any capitalization scheme. For example, the above example will accept `Mars` just as easily as it would accept `mars`. If necessary, you can force ValidateSet to be case sensitive by using the `IgnoreCase` option.

The `IgnoreCase` option in ValidateSet a validation attribute determines whether the values supplied to the parameter match exactly to the valid values list. By default, `IgnoreCase` is set to `$True` (ignore case). If you set this to `$False` then supplying _mars_ as a value for the `Planet` parameter for `Get-PlanetSize.ps1` would generate an error message.

You’d use the `IgnoreCase` option by assigning it a `$true` value at the end of the list of valid values as shown below.

```powershell
[ValidateSet("Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto", IgnoreCase = $false)]  
```

Now when you attempt to use a value for `Planet` that’s not _exactly_ like a value in the list, validation will fail.

## Using Tab Completion

Another perk of using ValidateSet is that it gives you tab completion. This means that you can cycle through the possible values for a parameter using the TAB key. This greatly improves the usability of a script or function, especially from the console.

In the examples below, there are a couple of things to note:

*   The tab completion loops back to the first value after having displayed the last.
*   The values are presented in alphabetical order, even though they are not listed in the ValidateSet alphabetically.
*   Typing an initial letter and hitting TAB restricts the values offered by tab completion to those starting with that letter.

![Cycling through parameter values using Tab Completion](/wp-content/uploads/2019/10/TabCompletion-Static-1.gif)

_Cycling through parameter values using Tab Completion_

![Restricting values returned by Tab Completion](/wp-content/uploads/2019/10/TabCompletion-Static-2.gif)

_Restricting values returned by Tab Completion_

You can also take advantage of ValidateSet tab completion in the _PowerShell Integrated Scripting Environment_ (ISE), as shown in the example below. The ISE _Intellisense_ feature shows you the list of possible values in a nice selection box.

> _Intellisense returns all of the values containing the letter you type, rather than just those that start with it._

![Tab Completion and Intellisense in ISE](/wp-content/uploads/2019/10/TabCompletion-ISE-1.gif)

_Tab Completion and Intellisense in ISE_

Now that we’ve covered ValidateSet validation attributes as they are in Windows 5.1, let’s take a look at what has been added in PowerShell Core 6.1 and see if that can give our script more validation capabilities.

## Understanding Changes to ValidateSet in PowerShell 6.1

With the arrival of PowerShell Core 6.1 two new capabilities have been added to ValidateSet validation attributes:

*   The `ErrorMessage` property
*   Use of classes in ValidateSet via access to `System.Management.Automation.IValidateSetValuesGenerator`

### The ErrorMessage Property

The default error message generated when you supply an incorrect planet name to _Get-PlanetSize.ps1_ is helpful, but a bit wordy:

```powershell
PS61> .\Get-PlanetSize.ps1 -Planet Barsoom
 Get-PlanetSize.ps1 : Cannot validate argument on parameter 'Planet'. The argument "Barsoom" does not belong to the set
 "Mercury,Venus,Earth,Mars,Jupiter,Saturn,Uranus,Neptune,Pluto" specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again.
 At line:1 char:32
 .\Get-PlanetSize.ps1 -Planet Barsoom
 ~~~
 CategoryInfo          : InvalidData: (:) [Get-PlanetSize.ps1], ParameterBindingValidationException
 FullyQualifiedErrorId : ParameterArgumentValidationError,Get-PlanetSize.ps1 
```

Use the `ErrorMessage` property of the `ValidateSet` validation attribute to set a different error message, as shown in the example below. `{0}` is automatically replaced with the submitted value and `{1}` is automatically replaced with the list of permitted values.

```powershell
Param(
     [Parameter(Mandatory)]
     [ValidateSet("Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune","Pluto",ErrorMessage="Value '{0}' is invalid. Try one of: '{1}'")]
     $Planet
 )
```

Replace the `Param()` block in the script file and save it. Then try `Get-PlanetSize.ps1 -Planet Barsoom` again. Notice below that the error is a lot less wordy and more descriptive.

```powershell
PS61> .\Get-PlanetSize.ps1 -Planet Barsoom
 Get-PlanetSize.ps1 : Cannot validate argument on parameter 'Planet'. Value 'Barsoom' is invalid. Try one of: 'Mercury,Venus,Earth,Mars,Jupiter,Saturn,Uranus,Neptune,Pluto'
 At line:1 char:32
 .\Get-PlanetSize.ps1 -Planet Barsoom
 ~~~
 CategoryInfo          : InvalidData: (:) [Get-PlanetSize.ps1], ParameterBindingValidationException
 FullyQualifiedErrorId : ParameterArgumentValidationError,Get-PlanetSize.ps1 
```

Next, take a look at a new way to define the acceptable values in ValidateSet via a PowerShell _[class](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_classes)_.

### PowerShell Classes

Custom types, known in PowerShell as classes, have been available since version 5. With the arrival of PowerShell Core 6.1 there is a new feature to allow the use of a class to provide the values for  ValidateSet.

Using a class allows you to work around the main limitation of a ValidateSet – it is static. That is, it is embedded as part of the function or script, and can only be changed by editing the script itself.

The new feature that works with `ValidateSet` is the ability to use the [System.Management.Automation.IValidateSetValuesGenerator](https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.ivalidatesetvaluesgenerator) class. We can use this as a base for our own classes using inheritance. To work with `ValidateSet`, the class must be based on System.Management.Automation.IValidateSetValuesGenerator  and it must implement a method called `GetValidValues()`.

The `GetValues()` method returns the list of values that you wish to accept. A `Param()` block with the static list of planets replaced by a `[Planet]` class would look like below. This example won’t work yet. Keep reading to learn how to implement this.

```powershell
Param(
     [Parameter(Mandator)]
     [ValidateSet([Planet],ErrorMessage="Value '{0}' is invalid. Try one of: {1}")]
     $Planet
 )
```

## Using a Class for a ValidateSet Value List: A Real Example

To demonstrate using a class for a _ValidateSet_ value list, you’re going to replace the static list of planets used earlier with a list loaded from a CSV text file. You’re no longer going to need to maintain a static list of values inside of the script itself!

### Creating a Data Source for the Class

First, you’ll need to create a CSV file containing each of the valid values. To do so, copy and paste this set of data into a new text file and save it as _planets.csv_ in the same folder as the _Get-PlanetSize.ps1_ script.

```powershell
Planet,Diameter
 "Mercury","4879"
 "Venus","12104"
 "Earth","12756"
 "Mars","6805"
 "Jupiter","142984"
 "Saturn","120536"
 "Uranus","51118"
 "Neptune","49528"
 "Pluto","2306"
```

### Adding the Class to the Script

Any class used by ValidateSet must already be defined before ValidateSet tries to use it. This means that the structure of _Get-PlanetSize.ps1_ as-is will not work.

The `[Planet]` class has to be defined before it can be used, so it has to go at the start of the script. A suitable skeleton class definition looks like below:

```powershell
class Planet : System.Management.Automation.IValidateSetValuesGenerator {
     [String[]] GetValidValues() {
 }
 }
```

Inside the `GetValidValues()` method of the class, use the [`Import-CSV`](https://adamtheautomator.com/import-csv/ "Import-CSV") cmdlet to import the text file _planets.csv_ created earlier. The file is imported into a variable with global scope, called `$planets`, so to access it later in the script.

Use the `return` statement to return the list of planet names via `GetValidValues()`. After these changes the class should now look like below.

```powershell
class Planet : System.Management.Automation.IValidateSetValuesGenerator {
     [String[]] GetValidValues() {
             $Global:planets = Import-CSV -Path planets.csv
             return ($Global:planets).Planet
     }
 }
```

Next, remove the `$planets` hash table declaration from the script as shown below. The global variable `$planets` that gets populated by the `[Planet]` class contains the planet data instead.

```powershell
$planets = [ordered]@{
     'Mercury' = 4879
     'Venus'   = 12104
     'Earth'   = 12756
     'Mars'    = 6805
     'Jupiter' = 142984
     'Saturn'  = 120536
     'Uranus'  = 51118
     'Neptune' = 49528
     'Pluto'   = 2306
 }
```

Now wrap the remaining original code in a function and call it `Get-PlanetDiameter` to differentiate it from the name of the script. Place the `Param()` block that was at the start of the script inside the function. Replace the static list of planets with a reference to the `[Planet]` class, as shown below.

```powershell
[ValidateSet([Planet],ErrorMessage="Value '{0}' is invalid. Try one of: {1}")]`
```

Replace the line `$output = "The diameter of planet {0} is {1} km" -f $_, $planets[$_]`  with the following two lines. These allow the script to look up a planet in the array of objects created by `Import-CSV`, rather than the hash table you created earlier, which you have removed from the script:

```powershell
$targetplanet = $planets | Where -Property Planet -match $_
 $output = "The diameter of planet {0} is {1} km" -f $targetplanet.Planet, $targetplanet.Diameter
```

After this set of changes your final script needs to look like this:

```powershell
class Planet : System.Management.Automation.IValidateSetValuesGenerator {
     [String[]] GetValidValues() {
         $Global:planets = Import-CSV -Path planets.csv
         return ($Global:planets).Planet
     }
 }
 Function Get-PlanetDiameter {
     Param(
         [Parameter(Mandatory)]
         [ValidateSet([Planet],ErrorMessage="Value '{0}' is invalid. Try one of: {1}")]
         $Planet
     )
     $Planet | Foreach-Object {
         $targetplanet = $planets | Where -Property Planet -match $_
         $output = "The diameter of planet {0} is {1} km" -f $targetplanet.Planet, $targetplanet.Diameter
         Write-Output $output
     }
 }
```

> _Remember, from this point on the script only works on PowerShell 6.1 or later_

Now, how do you _use_ this script?

### Running the Script

You can’t use the new version of the script directly by simply executing the script.  All of the useful code is now wrapped in a function. You need to _[dot source](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_scripts?view=powershell-6#script-scope-and-dot-sourcing)_ the file instead to allow access to the function from within the PowerShell session.

```powershell
PS61> . .\Get-PlanetSize.ps1
```

Once dot-sourced, you now have access to the new `Get-PlanetDiameter` function in the PowerShell session, with tab completion.

“_What’s the benefit of all that work?”,_ I hear you ask. “_The script seems to work in the same way, but it is more difficult to use the code!”_

Try this:

*   Open the `planets.csv` file that you created earlier.
*   Add a new row with a new name and diameter.
*   Save the CSV file.

In the same session that you originally dot sourced your script, try looking up the diameter of the new planet using `Get-PlanetDiameter`. It works!

Using a class in this way gives us several benefits:

*   The list of valid values is now separate from the code itself, but any changes to the values in the file are picked up by the script.
*   The file can be maintained by someone who never accesses the script.
*   A more complex script could look up information from a spreadsheet, database, Active Directory or a web API.

As you can see, the possibilities are almost endless when using a class to provide _ValidateSet_ values.

## Wrapping up

We have covered a lot of ground as we’ve built `Get-PlanetSize.ps1`, so let’s recap.

In this article you have learned:

*   What a _ValidateSet_ validation attribute is and why you might want to use one
*   How to add _ValidateSet_ to a [PowerShell function](https://adamtheautomator.com/powershell-functions/ "PowerShell function") or script
*   How tab-completion works with _ValidateSet_
*   How to use the `IgnoreCase` property to control whether your _ValidateSet_ is case sensitive
*   How to use the `ErrorMessage` property with your _ValidateSet_ and PowerShell 6.1
*   How to use a class to make a dynamic ValidateSet with PowerShell 6.1

What are you waiting for? Start using _ValidateSet_ today!

## Further Reading

*   [**_Attributes of Parameters_**](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_advanced_parameters?view=powershell-6#attributes-of-parameters)
*   [**_About Classes_**](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_classes?view=powershell-6)

Share this article

[Share on X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fadamtheautomator.com%2Fpowershell-validateset%2F&text=PowerShell%20ValidateSet%3A%20Tab-Completion%20%26%20Parameter%20Values)[Share on Facebook](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fadamtheautomator.com%2Fpowershell-validateset%2F)[Share on LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fadamtheautomator.com%2Fpowershell-validateset%2F)

## Related Posts

![](https://adamtheautomator.com/wp-content/uploads/publisher/3ec5d9c85b2b8183bec6e5cc0f0356c5/a323a4d03629188de1d5ed18ca60a64a65f94bef73d5390488bde5511859fa05.webp)

### [AZ-800 vs. AZ-802: Avoid the Wrong Windows Server Exam](/az-800-vs-az-802/)

See what the AZ-800 retirement means for your Windows Server certification path and how to re-weight your study plan around the current AZ-802 exam.

![](https://adamtheautomator.com/wp-content/uploads/publisher/3ec5d9c85b2b811089d5d31807a89aa3/4f6fca0bf1e9083ef5217f16ceed8f939aa4f322e0b4be8d8e3dbb7be8ca3114.webp)

### [Automate Windows 11 Context Menu Actions with Registry](/automate-windows-11-context-menu-registry/)

Add custom right-click actions to Windows 11 context menus by editing registry verbs, deploying .reg files, and scripting with PowerShell.

![](https://adamtheautomator.com/wp-content/uploads/publisher/3ec5d9c85b2b81d9ab7cd5dbb6caf044/21b786d67f19d1813150edf9871e36ead78714b63880de8c6161c7fde11a9769.webp)

### [Pktmon: Capture Windows Traffic Without a Third-Party Driver](/pktmon-capture-windows-traffic/)

Use the in-box Windows Pktmon tool to capture packets and packet-drop reasons, then convert the ETL to pcapng and text for offline analysis.

## Categories

*   [IT Ops](/category/it-ops/)
*   [Cloud](/category/cloud/)
*   [DevOps](/category/devops/)
*   [Home Ops](/category/home-ops/)
*   [Information Security](/category/infosec/)
*   [Software Development](/category/software-development/)

## Site

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Copyright 2026© ATA Learning | [Privacy Policy](/privacy/)
