PowerShell 5 introduced the new enum (enumeration) data type that allows you to predefine a set of allowed values for a variable. In this post, I will outline usage scenarios for enums in PowerShell scripts.
Avatar

Great developers (and scripters) know it's not good practice to repeat yourself in code. Repeating yourself may work in the short-term, but as the code base becomes larger, it soon turns into a management nightmare. Having to change the same variable in 20 different places is not fun! To prevent this code management nightmare, it's important to adopt good practices that allow you to make one change in one spot. One way to do this is by using enumerated types, or enums.

Although enums have multiple applications in various languages, I've found that they are most useful when writing code that prevents duplication. Let me explain.

First of all, what is an enum anyway? Enums, in fact, are simple constructs in any programming language. They are just a list of items in a collection. They are a way of setting a predefined set of items that are associated with a common type. I know that might be hard to grasp, so let's look at an example.

Enums allow me to create a set of predefined items ahead of time. Once I have this set created, I can then implement it in different ways. For this article, I'll go over a great use case for enums.

Let's say I have a script that I've created to give to my helpdesk. This script creates Active Directory users. I want to limit in which OUs this script can create users. To do this, I can define an enum of allowed OUs. To create an enum in PowerShell 5.0, we use the enum keyword followed by each item that's a part of that enum.

enum AllowedOUs 
{
	CompanyUsers
	Accounting
	HR
	IT
}

Here you can see I have an enum called AllowedOUs with four items inside. Once I do this, I can then reference all of the items in this enum by using two colons after specifying the AllowedOUs enumerated type. You can see below that the ISE knows what items are already in this enum, and Intellisense is kicking in to give me the list.

ISE knows the items in this enum and Intellisense provides the list

ISE knows the items in this enum and Intellisense provides the list

Note: There are a few gotchas with enums. First, the items in the enum cannot have spaces. So, don't even try to surround them with quotes; it's not going to work. In addition, enums cannot have special characters except an underscore. Just remember that the items are just simple strings.

Now that I have created the enum, how can I use it to restrict my helpdesk users from adding users into any OU they like? To demonstrate, let's start with a simple function template to do this.

function New-CompanyXActiveDirectoryUser {
    param (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$UserName,
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$FirstName,
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$LastName,
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$OrganizationalUnit
    )
    $params = @{
        Name = $UserName
        SamAccountName = $UserName
        GivenName = $FirstName
        SurName = $LastName
        Path = $OrganizationalUnit
        Server = 'MYDC'
        Enabled = $true
    }
    New-Aduser @params
}

You'll see that I'm building a helper function around the New-AdUser cmdlet. This gives me the ability to not only restrict the input to the cmdlet but to also provide some default values. However, anyone using this function can pass anything they want to the OrganizationalUnit parameter. Let's fix that by using the enum that we created earlier.

[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[AllowedOUs]$OrganizationalUnit

Notice the simple change? I just changed the type from string to AllowedOUs. You can see below that the ISE again knows that this is an enum and provides us with list of items, just like the ValidateSet parameter validation attribute does.

ISE knows that this is an enum and provides us with the list of items

ISE knows that this is an enum and provides us with the list of items

If I try to pass something other than an item in the enum, I'll immediately get an error and be presented with a list of allowable items.

An error and a list of allowable items

An error and a list of allowable items

You might think, "Big whoop! I can do that with the ValidateSet parameter validation attribute." True, but do you recall my intro where I mentioned not to repeat yourself? What if you had another function that removed an AD user and that same OU restriction needs to be placed there as well? At that point, you'd have two functions with this exact same parameter:

[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[ValidateSet('CompanyUsers','HR','Accounting','IT')]
[string]$OrganizationalUnit

Now you've got duplicate references, and if another OU needs to be added, it would have to be done in two different spots. But an enum can be used in multiple places. Simply set the parameter type to [AllowedOUs] in as many functions as you like, and they will all follow the same pattern.

Subscribe to 4sysops newsletter!

function Function1 {
    param(
        [AllowedOUs]$OrganizationalUnit
    )
}
function Function2 {
    param(
        [AllowedOUs]$OrganizationalUnit
    )
}

You can see now that using enums is not only an excellent way to organize like items but is also a great way to create a shared ValidateSet validation attribute as well!

avatar
8 Comments
  1. Avatar
    Marc 7 years ago

    In which file would the definition of the enum be saved?

     

  2. Avatar Author

    The enum could be created in any file you wish as long as it’s in the same scope as what you’re using. For modules, I’ll typically declare the enums at the top and then reuse them throughout. For scripts, you could do the same as well.

  3. Avatar
    Wojciech Sciesinski 7 years ago

    In the first example you prepared a hash table for parameters splatting but after that you assigned values to parameters directly.

    Is it your oversight?

  4. Avatar
    Henry Padilla 5 years ago

    If I am writing a self-contained script I cannot use enum to delineate parameters. Here’s a small sample…

    param(
    [string] $ServerType,
    [Location] $ServerLocation
    )
    
    enum Location {
     EastUS
     CentralUS
    }
    
    Get-AzureRmVmUsage -Location $ServerLocation
    

    The parameter expansion does not work. I can’t get it to work. Any ideas?

    • Avatar

      @Henry

      You must place the Enum section before you use it with the Param section.

      Here is an example which works:

      enum Location {
          EastUS
          CentralUS
      }
      
      function Get-VmUsage{
      
          param(
              [string] $ServerType,
      
              [parameter(mandatory)]
              [Location] $ServerLocation
          )
       
          Get-AzureRmVmUsage -Location $ServerLocation
      }
      
      Get-VmUsage -ServerLocation 'EastUS'

      • Avatar
        Henry Padilla 5 years ago

        But that wouldn’t expose the enum to the PS CLI. I’m looking for a way to use the enum in the PS CLI but specify it in the script. That’s only doable with ValidateSet() as far as I can see. But that’s OK, if I’m writing a stand-alone one shot script I don’t really need to reuse an enum. I really just want to restrict input.

         

        • Avatar

          @henry

          Yes that’s correct.
          The enum is only avaibale in the context where it has been created.

          In your case the ValidateSet attribute is probably more appropriate.

          An enum can be useful for a module containing several functions which are using the same lists.

Leave a reply

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account