Weekend Scripter: Using .NET Framework Enumerations in Windows PowerShell
Summary: Guest bloggers Microsoft PFEs Adam Haynes and Shubert Somer talk about using .NET Framework enumerations with Active Directory.
Microsoft Scripting Guy, Ed Wilson, is here. Today is part 3 of a 5-part series by guest blogger Adam Haynes with help along the way from his friend Shubert Somer. You will want to go back to read part 1 and part 2 (if you have not already) before you read today’s blog post.
Here’s Adam …
In parts 1 and 2 of this series, Shubert and I opened up a can of worms and described several very important concepts and terms regarding the .NET Framework and how it can be used in Windows PowerShell. We did not cover every topic concerning .NET Framework, but we discussed the most important parts as far as we, and this blog series, are concerned. Now that I have all of the pieces I need to get what I want, here is a line-by-line break down to help you interpret what is on MSDN and how to translate that into Windows PowerShell awesomeness.
If you want to follow along in your TEST environment, just make sure the Active Directory module is available on the computer that you are running the script from. The Active Directory module is added when you install RSAT.
These first few lines are pretty straightforward, so we aren’t going to talk about them other than one of the things I like about PowerShell in general. That is, if you look at the script as a whole, you notice that I switch back and forth between cmdlets and .NET Framework “programming.” The reality is that it is all .NET, but Windows PowerShell and the cmdlets abstract that for administrators, so we can get in and get out with a quick script or single line in the console. Second, I am a little more controlling with my Windows PowerShell objects on lines 3 and 4, in that I specify the type/class of my variables. That is more of a personal preference, though, because Windows PowerShell lets us get away with a lot of sloppiness (as Shubert reminds me of quite often). I will leave line 5 for Shubert to explain.
Shubert: Line 5 is an example of another special class, called an enumeration. Technically, an enumeration is a type, and not a class, but that distinction for our needs is not important. Enumerations are just integers under the covers, but they are mapped to human-friendly textual names. They are used to give you an easy and consistent way to indicate a particular constant value something might have, in this case, the Directory Context Type. The developers who wrote the Active Directory module knew that there are only a certain set of possible values, and so they created the DirectoryContextType enumeration to list them out. The text values will now show up in IntelliSense and other typing “helpers,” and you don’t need to worry about breaking your code because you fat-fingered a long text value.
Notice the syntax used, too: [The.Full.Enumeration.Name]::SomeLabel. This is the same syntax we would use to get at a static member of a class, so make sure not to get the two concepts confused: enumerations are just labels, and they do not have any further functionality.
Adam: So, basically, when I look at the MSDN site at the DirectoryContextType Enumeration, I can see all of the options.
I enter domain and .NET uses 0 or forest is 1, etc. as an example?
Shubert: Yes, what gets passed around in .NET Framework is the integer value, but you will always reference it with the enumeration name like the example above.
Adam: Line 7 initializes a new instance of an Active Directory context with the current user credentials. Notice that I am starting to use some of the definitions and terminology we talked about in the previous post.
Shubert: Here we see an example of a constructor. Note the New-Object cmdlet, and you’ll notice you do not put the class name inside square brackets . That’s because it’s not really a reference to the class, but instead it is a reference to the constructor method of the class. That’s pretty subtle, so make sure you are comfortable with the distinction.
Adam: Line 8 calls the FindOne() static method of the DomainController class in the specified context we created on line 7.
Let’s break this down a little further.
This syntax references the domain controller class. As we’ve seen, in Windows PowerShell you must enclose the class name in brackets to tell PowerShell it is a class. You can use MSDN or pipe the class to Get-Member to discover what the class can do. However, piping to Get-Member may not return all of the class methods in a single entry:
Notice the static methods of the DomainController Class listed in MSDN do not appear in the above command. To see the static members of a class in Windows PowerShell, you will need to use the –Static switch on the Get-Member cmdlet. Try both and see the difference for yourself:
Once we have identified the static method we want to use, this syntax is how we actually call a static method in Windows PowerShell. The double colon is PowerShell notation for calling a static method of the class we just referenced in square brackets. Remember what Shubert said regarding the difference between a static method and enumeration? Syntactically nothing!! We just know what they are because MSDN says so.
Shubert: You may ask, “So, how do we know to use the FindOne() static method? And how do we know we need to pass in a context object?” Well, this comes from knowing the .NET Framework object model for your technology. As Adam has repeated for Windows PowerShell: “There’s a cmdlet for that,” so it is for .NET: “There’s a class for that.” Very often there is more than one way to do something, so if you think what you have found is too messy, then keep looking—there is probably another method or class that can help. Especially look at static methods because they are often used as utilities to reduce code/script complexity. For example, the FindOne() static method we use here creates a fully provisioned and ready to use domain controller object without needing to use the constructor/New-Object cmdlet.
As you navigate around MSDN, you will see a few important little icons tagging the methods and properties. One is a fat S.
This is the marker for something that is static, so you have to reference it via the class, as in “[Some.Long.Name.Space.AClass]::StaticMethod(), rather than via a variable (i.e. an instance) like $newInstance.Method().
The other icon you might see is a little key icon.
This means that the member is protected (i.e. for use by class developers only), and it will not be generally available to you from a script.
Adam: The rest of the script is normal Windows PowerShell syntax.
Line 12 retrieves the replication metadata from the $domainController object that was returned on line 8 by calling its GetReplicationMetadata method and passing it the current $partition of the loop.
Line 13 extracts the dsaSignature item from the $domainControllerMetadata object on line 12.
Line 14 is the easy line. We simply access the attributes or properties of the replication metadata and build a string to say what we want.
Shubert: I hope by now you are a little more comfortable with this code. Still, there is a lot going on here, so let’s break it down.
$DomainController is a .NET DomainController object, and we can use the ‘.’ to get access to all of its members. One of those members is the GetReplicationMetadata() method. This method returns another .NET object that is an ActiveDirectoryReplicationMetadata object.
OK, so we have $DomainController.GetReplicationMetatdata($Partition). By checking MSDN we know this is an ActiveDirectoryReplicationMetatdata object, but what is that? It turns out that it is another special kind of object called a Dictionary, which is basically an array of objects that are each tied to a unique keyword so they can be looked up easily. This is done via the Item() property, which, unfortunately, looks a lot like a method. It’s actually a reference to the contents of the Dictionary. To get a specific object out, we have to provide the keyword/ID value, and that is why it seems to be taking a parameter. Here we provide the “dsaSignature” key word, and we get back the AttributeMetadata object.
Adam: Oh, so a dictionary object in .NET Framework is a lot like a hash table in Windows PowerShell?
Shubert: Yes, they are very similar in concept in that both are sets of key/value pairs, but discovering what keywords are in the dictionary can be a challenge. In this case, the ActiveDirectoryReplicationMetadata class has another property, called AttributeNames that is a collection of all the keywords. You can always write a small script to loop through everything in AttributeNames and then write it out to the console (perhaps by piping it to one of PowerShell’s formatting cmdlets). Get-Member will not help, unfortunately, because it would give you all of the methods and properties of the dictionary. This is a different thing than the items in the dictionary.
Adam: Get-Member gives us a starting point and Windows PowerShell does a lot of work for us. For example, if we run $DomainControllerMetadata.AttributeNames, PowerShell will dump the list of the Attribute Names. This is another cool thing about Windows PowerShell—the default output formatting a.k.a. Out-Default. For more information, check out a blog from one of the Windows PowerShell architects: How PowerShell Formatting and Outputting REALLY works.
So … clear as mud? I hope you are better off now than before you started. If you are better off, then you are welcome. If you are worse off, then it is Shubert’s fault. J Even if you are not an Active Directorytype administrator, the base knowledge from our first post carries across all of .NET Framework. Join us in part 4 and we will use that new-found knowledge on another sample script where we introduce one more .NET component, the assembly. It’s kind of a big deal, so don’t miss it.
Thank you, Adam and Shubert! Great stuff. Looking forward to your article tomorrow on assemblies.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at firstname.lastname@example.org, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy