10

I'm writing a cmdlet (script) on powershell and I wanted to use eunm as one of the parameters. But I don't know where to put enum definition so that it would be visible for cmdlet parameters declaration.

For example, I have a parameters definition of the script like this

[cmdletbinding()]
param(
    [Parameter(Mandatory=$True)]
    [string]$Level
)

and an enum like this

enum LevelEnum { NC = 1; NML = 2; CS = 3 }

I can't replace [string] with [LevelEnum] in the parameter definition because script will fail to locate enum definition. And I can't put definition before cmdletbinding, it's not allowed. I know how to do it if that would've been a function, I know it can be solved using ValidateSet, but I need to have integer values correstonding to enum options.

[ValidateSet('NC','NML','CS')]

But the question is, can I do the same for a cmdlet?


Thanks to everyone. I ended up with a combination of defferent answers.

[cmdletbinding()]
param(
    [Parameter(Mandatory=$True)]
    [ValidateSet('NC','NML','CS')]
    [string]$Level
)
# Convert level from string to enum
enum PatchLevel { NC = 1; NML = 2; CS = 3 }
[PatchLevel]$l = $Level

# Use the numeric value
Write-Host $l.value__
Mak Sim
  • 2,148
  • 19
  • 30
  • 1
    Does this answer your question? [Enum declaration](https://stackoverflow.com/questions/40348069/export-powershell-5-enum-declaration-from-a-module) – Alex_P Jun 22 '20 at 10:27

4 Answers4

4

Unlike every other part of the PowerShell grammar, type definitions (and using statements) are emitted at parse-time, and you can leverage this by placing the enum definition inside the script or function that depends on it (however counter-intuitive it might seem):

[cmdletbinding()]
param(
    [Parameter(Mandatory=$True)]
    [LevelEnum]$Level
)

begin {
enum LevelEnum { NC = 1; NML = 2; CS = 3 }
}

end {
 # rest of your script goes here
}
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • 9
    I copied this exactly into a file test.ps1, and tried running `.\test.ps1 NC`. I get `Unable to find type [LevelEnum].` in both Powershell 5 and 7. Is there anything I'm likely to have missed? Does it only work in certain Powershell versions? – jsabrooke Dec 22 '20 at 11:28
3

I would try something like this:

Below I created a plain PSM1 file with the LevelEnum definition.

enum LevelEnum{
  High
  Medium
  Low
}

Afterwards, I use using module with the path to the PSM1 file.

using module C:\Users\Path\To\Module\testmodule.psm1

function Get-Verb {
    [CmdletBinding()]
    param (
        [LevelEnum]$b
    )
    Write-Host $b
}

I used this microsoft documentation, About_Using and I also run version 7.0.2.

Alex_P
  • 2,580
  • 3
  • 22
  • 37
3

If this script needs to accept a custom enum, it means that you will be calling it from some other place, where enum definition already exists. And now you're trying to add the same definition again in a script. It would be good idea to push it out to a module as per @Alex_P suggestion so the definition resides in one place, but the downside is that Import-Module or #Requires won't import it, thus the need for using module clause.

But if you're willing to accept simpler and less secure solution, you could take advantage of the fact that any enum you define is derived from System.Enum. [System.Enum]$Level will accept only and all enums and if it's not LevelEnum the script will break, but still it filters most possible mistakes before the script execution and gives some information about parameter type.

AdamL
  • 12,421
  • 5
  • 50
  • 74
  • 3
    Thanks to everyone who replied. While most upvoted answers was most technically correct, but this answer actually made me to conclussion that my approach and my question was wrong. So I'll mark this one as accepted. – Mak Sim Jun 23 '20 at 07:35
2
[cmdletbinding()]
param(
    [ArgumentCompleter({
        enum LevelEnum { NC = 1; NML = 2; CS = 3 }
        [LevelEnum].GetEnumValues()
    })]
    [ValidateScript({
        enum LevelEnum { NC = 1; NML = 2; CS = 3 }
        [LevelEnum]$_
    })]
    $Level
)

enum LevelEnum { NC = 1; NML = 2; CS = 3 }
[LevelEnum]$Level

This works, but I'm sure one will easily forget to edit all the enum definitions...

7cc
  • 1,149
  • 4
  • 10