4

Thanks to the great people at StackOverflow we received a very good answer on how to retrieve the values defined in ValidateSet within the Param() clause of a script or function:

Param (
    [ValidateSet('Startup', 'Shutdown', 'LogOn', 'LogOff')]
    [String]$Type = 'Startup'
)

(Get-Variable Type).Attributes.ValidValues

The only thing that bothers me is that this code only works the first time when you run it in the PowerShell ISE. The second time you run it, there is no output generated.

Is there a workaround to have it always working? We use PowerShell 4.0 on Win 7 and Win 2012.

Community
  • 1
  • 1
DarkLite1
  • 13,637
  • 40
  • 117
  • 214
  • All panes in ISE are in the same scope. So basically if you want to get that again, you should remove it from the current shell scope and re-initialize it. – Ranadip Dutta Mar 10 '17 at 08:18
  • 1
    @DarkLite1 Be aware of [differences](https://blogs.msdn.microsoft.com/powershell/2009/04/17/differences-between-the-ise-and-powershell-console/) between ISE and powershell.exe. Some of them are quite [weird](http://stackoverflow.com/questions/41959310/escapedatastring-having-differing-behaviour-between-powershell-ide-and-powershel). Some can be explained due to [different configuration](http://stackoverflow.com/questions/2094694/how-can-i-run-powershell-with-the-net-4-runtime). ISE have best intellisense; otherwise Visual Studio Code, PowerGUI, or others are better and more predictable. – Anton Krouglov Mar 10 '17 at 08:53

2 Answers2

3

First of all, this behavior is only seen in PowerShell ISE (it works perfectly outside). This might be explained by the following post.

Reading it, you'll see that there is a workaround:

Param (
    [ValidateSet('Startup', 'Shutdown', 'LogOn', 'LogOff')]
    [String] $Type = 'Startup'
)

(Get-Variable Type).Attributes.ValidValues

# Do your stuff here

Remove-Variable Type
David Brabant
  • 41,623
  • 16
  • 83
  • 111
  • The behavior is a _bug_, and while it is most likely to surface in the ISE, it can surface in the regular console too, because it is related to repeated dot-sourcing. `$MyInvocation.MyCommand.Parameters['Type'].Attributes.ValidValues` is an alternative that bypasses the bug without the need for an extra `Remove-Variable` call, which is not only cumbersome, but has side effects. – mklement0 Mar 10 '17 at 18:04
2

tl;dr

  • The observed behavior is arguably a bug, present in Windows PowerShell and still as of PowerShell (Core) 7.3.1- see GitHub issue #3301.

  • To bypass the problem without side effects, use the following approach:

Param (
    [ValidateSet('Startup', 'Shutdown', 'LogOn', 'LogOff')]
    [String] $Type = 'Startup'
)

$MyInvocation.MyCommand.Parameters['Type'].Attributes.ValidValues

If there's a chance that Set-StrictMode -version 2 or higher is in effect or you're using PSv2, use

Param (
    [ValidateSet('Startup', 'Shutdown', 'LogOn', 'LogOff')]
    [String] $Type = 'Startup'
)

($MyInvocation.MyCommand.Parameters['Type'].Attributes |
  Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).ValidValues

Optional background information

The problem is not related to the ISE per se, but to repeated dot-sourcing (the ISE just happens to run all scripts by dot-sourcing them).

Dot-sourcing runs scripts in the current scope (in the caller's scope itself, as opposed to in a child scope) and therefore typically modifies the current scope's state, such as by adding variables.
If you dot-source a script from an interactive session, you're effectively modifying the global session state, which is how definitions from the PS profile files are loaded, for instance.

In the case at hand, a dot-sourced invocation of the script effectively adds parameter variable $Type to the invoking scope as a regular variable, as designed.

The bug surfaces when you dot-source the same script again (assume that the script in the question is present as ./script.ps1:

  • After the first dot-sourced invocation, variable $Type is still intact with respect to its attribute:

      > . ./script.ps1; (Get-Variable Type).Attributes.Count
      Startup
      Shutdown
      LogOn
      LogOff
      3   # PS implicitly adds 2 add'l attributes behind the scenes
    
  • When you dot-source again, the attributes are lost:

      > . ./script.ps1; (Get-Variable Type).Attributes.Count
      0
    
mklement0
  • 382,024
  • 64
  • 607
  • 775