I was working on a project recently that required me to have an Enum that allowed bit fields as opposed to the normal fields that we might deal with in our day to day endeavors with PowerShell.

If you aren’t quite sure what I mean by this, here is a quick example that shows the difference between the two.

First, the regular Enum that we are used to:

([DayOfWeek]0..6).ForEach({[pscustomobject]@{Number=$_;Data=[DayOfWeek]$_}})

Notice that each number matches up to a single value: 0 = Sunday, 3 = Wednesday and so on.

Now for an Enum with bit fields:

([System.IO.FileAttributes]1..7).ForEach({ [pscustomobject]@{Number=$_;Data=[System.IO.FileAttributes]$_} })

You can see here that depending on the value, you may end up for more than 1 item. This typically will follow a 1,2,4,8,16…etc… approach for the single items and then they perform a bitwise XOR to combine values for more items.

Now that we have covered this topic in a very basic form, we still need to understand how to build one so we can use it in PowerShell. In PowerShell V2..4 we would have to settle on creating the enum by creating a C# here string and loading it up into the session like so:

Add-Type -TypeDefinition @" [System.Flags] public enum Animals { Dog = 1, Cat = 2, Bird = 4, Lizard = 8 } "@

All we have to do is ensure that the [System.Flags] attribute is used within our code.

Now we can verify that this actually worked like we wanted:

(1..15).ForEach{[pscustomobject]@{Number=$_;Animal=[Animals]$_}}

I’d say it passes the test!

Next up is building one dynamically using reflection. A little more complex but not something that we can’t do..right?

#region Module Builder $Domain = [AppDomain]::CurrentDomain $DynAssembly = New-Object System.Reflection.AssemblyName('Random') $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) # Only run in memory $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('Random', $False) #endregion Module Builder #region Animals $EnumBuilder = $ModuleBuilder.DefineEnum('Animals', 'Public', [int32]) [void]$EnumBuilder.DefineLiteral('Dog', [int32] 0x00000001) [void]$EnumBuilder.DefineLiteral('Cat', [int32] 0x00000002) [void]$EnumBuilder.DefineLiteral('Bird', [int32] 0x00000004) [void]$EnumBuilder.DefineLiteral('Lizard', [int32] 0x00000008) $EnumBuilder.SetCustomAttribute( [FlagsAttribute].GetConstructor([Type]::EmptyTypes), @() ) [void]$EnumBuilder.CreateType() #endregion Animals

The trick here is to create a custom attribute where we supply the default constructor for the Flags.Attribute type add that attribute to the enum.

We can test using the same code to see if everything matches up:

Success! Ok, so I have covered the old ways of doing it and even the reflection approach is still valid in V5 if your goal is keeping everything in memory and not writing to disk.

If you are interested in an even easier way of doing this, then check out fellow MVP Matt Graeber’s PSReflect module that gives you the PSEnum function that makes this whole thing a snap!

$Mod = New-InMemoryModule -ModuleName Win32 psenum -FullName Animals -Type Int32 -Bitfield -Module $Mod -EnumElements @{ Dog = 1 Cat = 2 Bird = 4 Lizard = 8 }

If you run the usual code, you can easily see that it works!

In PowerShell V5, we are greeted with an easier way to natively build an Enum in PowerShell with the…Enum keyword! But we don’t quite care about that as we really need to have support for Flags so we can have our bit field support.

Luckily we have that available albeit not documented anywhere yet. We just have to define that it will have the Flags attribute before calling the Enum keyword and we are ready to go!

[Flags()] enum Animals { Dog = 1 Cat = 2 Bird = 4 Lizard = 8 }

How simple was that? We just create the enum and it is ready to go with not much code or having to compile C# (not that it was that tough anyways) and we can verify once again just to make sure it works like we want it to.

No oddness here! It worked like a champ and we have yet another way to accomplish our goal! Just remember that this approach only works with PowerShell V5.

Enjoy!