If you’ve been working with PowerShell for any real length of time, you know that PowerShell is deeply tied to the .NET framework. If you weren’t aware of that, perhaps because you only use a handful of cmdlets for limited and specialized purposes, or only use scripts posted online, well…surprise!! Everything in PowerShell, at the end of the day, comes back to .NET. I’ll be taking a brief step back to cover a few basics, so no need to worry if this was news to you.
Short History Lesson
In his book ‘Monad’, the original name for what became PowerShell, Jeoffrey Snover said that this new shell was the result of an effort to rewrite the Windows Management Infrastructure (WMIC) using C#. He discovered, as part of this process, that all the great aspects of WMIC could be applied to any dotNET object. Unfortunately for all of us, there was originally only one way to extend the functionality of PowerShell, which was to use snap-ins. These ungainly things required registration of DLL files with the Global Assembly Cache (GAC), and a restart of the shell, in order to provide you with new cmdlets. Today, we now have the ‘power’ (see what I did there?) of modules, which can be loaded automatically or ad-hoc as required, as well as the ability to natively use C# in-line directly, without having to compile any code. These modules come in one of two flavors; Binary or Function. The Microsoft authoried ActiveDirectory module is a good example of a binary module, while the majority of modules found in the PowerShell Gallery are function modules.
The latter of these is far more common these days of course, but this is because the focus is a bit different than in the past. The sealed method is still used sometimes, but generally only when the module must use specialized access points not intended for general consumption. In most cases, PowerShell is just an additional interface for interacting with a given web API or other programming interface intended to be consumed, integrated, or interacted with by third-party developers in some fashion. What both methods still have in common, is that PowerShell is being used to wrap underlying dotNET (or at least dotNET accessible) functionality, into user friendly cmdlets.
The Problem with Wrappers
Even when developers are trying to provide the most functionality possible, such as the PowerShell team has tried to do with all the base functionality, they still have to make hard choices sometimes. In most cases, these choices are largely unimportant to us as end users, as most teams generally do a good job of figuring out what people do and don’t want/need to see or do. In other cases however, you will inevitably run into a situation where something that should be possible isn’t, either because the module maker didn’t see a need, or because it was more trouble than it was worth to them at the time.
As an example, consider the ActiveDirectory module provided by Microsoft. The AD module is fine, for what it is, and provides decent coverage for most things, but it also has gaps. Because the AD module is exclusively available as part of the Remote Server Administration Tools (RSAT), it requires specific installation steps and is non-transferrable. While there is some support for permissions via the associated provider, navigating that provider is a less than optimal experience. This is despite having more than 100 individual cmdlets I might add. The module is also wholly dependent upon AD web services, which introduces a host of limitations not present with LDAP or dotNET based integrations.
Microsoft also provides a number of type accelerators, such as an interface for ADSI, which offer more flexible functionality, but without closing all the gaps. There is still no mechanism for easily dealing with setting security using the ADSI accelerator for one thing. If you end up needing to do anything really advanced with AD, this means you’ll inevitably end up looking into dotNET classes to fill these gaps, which is where things start to get a little…weird.
A key thing you have to remember is that PowerShell itself still acts as a sort of wrapper. What you think of as PowerShell is really just a console that hosts an interface that uses PowerShell in the background. Just as with every other type of wrapper, it’s designed to show you what the developer thought you might need, not necessarily everything you might want…or expect. Fortunately, in the case of PowerShell itself, it’s designers were smart enough to provide a means of self-service.
Show Me Something
As you’ve probably noted if you’ve read any of my other articles, I tend to do a lot of work with Active Directory, and for quite some time now, I’ve worked hard to avoid using the Microsoft provided ActiveDirectory module. I’m not a fan of how they built it originally, and the fact that they’ve done absolutely nothing with it to evolve things hasn’t helped matters. As part of my journey of discovery, I eventually landed on using dotNET classes directly. This was partly to give me a project oriented approach to learning more about dotNET, and partly to allow me to use a consistent end-to-end approach that minimized third-party dependencies. It might also be because I’m maybe just a tiny bit stubborn…just a bit mind. This article isn’t about working with AD specifically, so I’ll not go into all the details of working with these classes, just enough to show my point and the work-around.
For the purposes of this article, we’ll focus on a single Organizational Unit object, looking at it through several different lenses. To follow along, you’ll want a system joined to an AD domain, as well as the AD RSAT tools installed. In my test environment, I have an OU called ‘Tier-2’ at the root of my domain. As a first step, let’s take a look at what we get from the AD module ‘Get-ADOrganizationalUnit’ cmdlet.
That looks well enough, but next we want to take a look at the ADSI type accelerator, which should be the same thing as using System.DirectoryServices.DirectoryEntry. Below you can see the output from both items side-by-side.
In looking at the output from the Microsoft cmdlet versus the dotNET/ADSI alternative, we can immediately see some differences. The AD cmdlet lists several properties and methods that are not present in the dotNET/ADSI output. We can get access to the additional attributes, such as City or Country easily enough in the dotNET/ADSI side, just as we can get objectCategory or objectClass on the AD side. We don’t have the ability to access the same methods, but this is because we have additional cmdlets that can be used to provide the same functionality, should the need arise. On the surface, it would appear that we are seeing the exact same thing, or close enough, regardless of the approach used, with the exception of the nTSecurityDescriptor property anyway. The nTSecurityDescriptor property can’t be readily read or translated, so it isn’t that much of a loss on the surface of things.
The issues only start to truly become evident when we look at the Docs page for System.DirectoryServices.DirectoryEntry. If you navigate to that page (the link will open in a separate window), you’ll see a host of both properties and methods that aren’t present in any of the output. For example, the Docs article indicates that there is a Children property, as well as a DeleteTree method that aren’t present in our output. You may find yourself thinking that it’s just a matter of including the additional properties in your query, but you would be incorrect. In order to query a property using the AD module, that property must exist on the object. In this case, the AD Schema does not contain an attribute named ‘Children’. So, why don’t we see those items, even when we bind directly to the dotNET class?
Hello PSBase
The answer, of course, is that pesky wrapper again. The developers did their best to determine what you might need to see without having to worry about knowing all the things a developer knows. Fortunately, in the case of PowerShell itself, the creators of the wrapper built in a mechanism to access the underlying details without the wrapper getting in the way. As it happens, there is a hidden property that exists called ‘PSBase’. This property does not show up with Get-Member, no matter how you might try. I only happened across it after a great deal of searching the internet for a way to get at the permissions of the AD objects without using the provider from the AD module. So, let’s see what happens when we run the dotNET object through Get-Member again, but this time with the ‘PSBase’ property specified.
Well that certainly looks a LOT more like the Docs link! We don’t see all the possible properties and methods still, but it’s no longer the wrapper getting in the way. Some methods and properties, such as DesignMode for example, are only intended to be accessible in specific circumstances. So, what about objects that aren’t dotNET objects?
In this instance, while using the PSBase property does add a few items, it’s not quite as drastic of a difference as before. I suspect that the difference is in how the wrapper is constructed versus the dotNET class. In the case of the AD module specifically, the developers created their own class definitions. These definitions are likely based on the results obtained from the AD web service, rather than the base dotNET classes I’ve shown above. While I have taken a crack at looking inside the MS module, my developer-fu is not yet strong enough to follow the code through all of its many…many…parts.
Now that you know this little gem exists though, it doesn’t hurt to try it and see right? The only downside here is that it will be strictly trial and error, as there’s really no way to know in most cases whether or not PSBase will reveal anything interesting…you just have to try it. The good news is that, once you have ‘.psbase’ added, if it’s viable, you can use tab complete again for any sub-properties or methods it holds.
That’s all for this article, but I’ll be back with more soon. Until next time friends!