This certainly isn’t a programming blog and I’m certainly not a developer, but one thing I try to do here is publish info that took me a lot of time to figure out and that isn’t readily available elsewhere. And this certainly qualifies.
I’ve recently started learning C# in order to develop some custom utilities to help in my department’s day to day operations. One of these utilities required retrieving some attributes from a computer object in Active Directory. The ComputerPrincipal class gives us direct access to certain attributes, but a quick look at a computer’s AD object shows many more attributes that are not directly accessible via this class. Turns out there are ExtensionGet and ExtensionSet methods that will read (or write) to these extended attributes, but to make things complicated they are private methods and thus not directly accessible.
After some further research I found this link and realized I needed to derive a class from ComputerPrincipal so that I could expose the ExtensionGet and ExtensionSet methods. The code in that link provides an example of doing so, but it focuses on users, not computers, and furthermore it’s written in such a way that each extended attribute needs its own get and set methods. I wanted something less specific (so I could read or write to any extended attribute) and also needed to access computer AD objects.
It was easy enough to follow that post and derive a class (ComputerPrincipalEx) exposing the two otherwise private methods. The problem came in that I needed to make use of the FindByIdentity method in the base class and every attempt I made to do so resulted in the compiler throwing an error that it could not convert from type ComputerPrincipal to ComputerPrincipalEx. This made no sense to me so after not getting anywhere for a while, I posted this thread on StackOverflow, which unfortunately was not that useful. One poster there did mention however that I needed a static method that created a ComputerPrincipalEx from an instance of the base class. While trying to figure that out I reread the original link I posted, specifically this comment. Turns out I needed to create a new FindByIdentity method in my derived class that called FindByIdentityWithType in the base class. That would allow me – with the appropriate casts – to have a return type of ComputerPrincipalEx instead of ComputerPrincipal.
Even after doing that however, I was receiving an error that ComputerPrincipalEx was not a valid object type with which to search AD. After yet more research I discovered I needed to add two attributes to the code to specify the object type was “computer.”
The end result of all this is a working ComputerPrincipalEx class which can access the ExtensionGet and ExtensionSet methods.
Here’s the code…use at your own risk:
public class ComputerPrincipalEx : ComputerPrincipal { public ComputerPrincipalEx(PrincipalContext context) : base(context) { } public ComputerPrincipalEx(PrincipalContext context, string samAccountName, string password, bool enabled) : base(context, samAccountName, password, enabled) { } new public string ExtensionGet(string extendedattribute) { try { if (base.ExtensionGet(extendedattribute).Length != 1) { return null; } else { return (string)base.ExtensionGet(extendedattribute)[0]; } } catch (Exception ex) { // This should be broken down to individual exceptions string message = string.Format("Exception occurred while retrieving extended attribute {0}. \r\nThe following error occurred:\r\n {1}", extendedattribute, ex); MessageBox.Show(message); Application.Exit(); return null; } } public void ExtensionSet(string extendedattribute, string value) { try { base.ExtensionSet(extendedattribute, value); base.Save(); } catch (Exception ex) { // This should be broken down to individual exceptions string message = string.Format("Exception occurred while attempting to set extended attribute {0}. \r\nThe following error occurred:\r\n {1}", extendedattribute, ex); MessageBox.Show(message); Application.Exit(); return; } } public static new ComputerPrincipalEx FindByIdentity(PrincipalContext ctx, string identityValue) { return (ComputerPrincipalEx)FindByIdentityWithType(ctx, typeof(ComputerPrincipalEx), identityValue); } }
If you find this useful or have any suggestions for improvements, please leave a comment. Thanks!