Hey, Scripting Guy! I recently came across your article where you showed people how to use VBScript to add a domain user to a local group. How can I do that same task using Windows PowerShell?
Hey, KE. Before we begin we thought we should mention that the Scripting Guy who writes this column has a new favorite TV commercial. He’s not even sure what the commercial is advertising (other than some car maker) but it shows two identical cars (with identical drivers and passengers) driving parallel roads. When the two roads merge together, the cars magically merge together as well, and the two cars seamlessly become one car.
Admittedly, that’s not that big of a deal. Instead, what’s really cool about the commercial isn’t the commercial itself, but the disclaimer that accompanies it:
Professional driver on closed course. Do not attempt.
To be honest, that’s extremely good advice. For example, suppose you’ve had yourself cloned, and both you and your clone drive identical cars. Should you drive down parallel roads and then smash the cars into one another, assuming that the two cars will seamlessly merge into one? No, you should not.
Well, not unless both you and your clone happen to be professional drivers on a closed course. Then it’s OK.
Or at least that’s what they tell you on TV. To tell you the truth, though, it doesn’t look all that hard to us. Why don’t you give it a try and let us know how it goes?
Note. At the risk of giving the poor old Scripting Editor a heart attack, we take that back: don’t give this a try, even if you are a professional driver on a closed course. And no, not even if your clone is also a professional driver.
Oh, and a word of advice: to the best of our knowledge, no court in the world will accept “The Scripting Guys told me to do it” as a legitimate excuse for doing something stupid.
Nor should they.
But don’t worry; this doesn’t mean that only professional drivers on closed courses get to have any fun. Granted, you can’t smash two cars together to see if you can somehow get them to merge into one car. (Although, then again … no, never mind. Don’t even think about it. Or, at the least, don’t tell anyone that the Scripting Guys told you to think about it.) But who cares? Believe it or not, you can still have fun without smashing two cars together. For example, you don’t need to be a professional driver on a closed course in order to write a Windows PowerShell script that adds a domain user to a local group. Instead, all you have to do is put together something similar to this:
$objUser = [ADSI]("WinNT://fabrikam/kenmyer") $objGroup = [ADSI]("WinNT://atl-fs-001/Administrators") $objGroup.PSBase.Invoke("Add",$objUser.PSBase.Path)
So what’s going on here? Good question. In the first line, we’re using the [ADSI] type adapter to create an object reference to an Active Directory user account; more specifically, we’re creating an object reference to the fabrikam\kenmyer user account. So what is the [ADSI] type adapter? That’s also a good question. In order to make certain operations a little easier for, and a little more relevant to, system administrators, PowerShell includes several “type adapters” such as [ADSI]; in this case, the type adapter makes it easier to connect to ADSI, regardless of whether you’re using ADSI to manage Active Directory or to manage local accounts. If it wasn’t for the type adapter, you’d need to go through the .NET Framework and conjure up an instance of the System.DirectoryServices.DirectoryEntry class in order to work with ADSI. The type adapter simply creates that instance for you.
Hmmm, that’s another good question: if this is Active Directory why did we use the WinNT provider; aren’t you supposed to use the LDAP provider when working with Active Directory?
Yes, you are, provided that you are working exclusively with Active Directory. In this case, however, we aren’t working exclusively with Active Directory; instead we’re working with local accounts as well. Local accounts require the use of the WinNT provider, and the WinNT provider doesn’t know how to work with OUs and other Active Directory components. Because of that, we have to use the old-fashioned domain/username syntax.
After we create an object reference to the user account we repeat the process to create a second object reference, this one to our local group. For our sample script, that’s the Administrators group on the computer atl-fs-001:
$objGroup = [ADSI]("WinNT://atl-fs-001/Administrators")
Once we’ve done that we can add the domain user Ken Myer to our local Administrators group by using the following line of code:
Yet another good question: what the heck is that PSBase stuff? Well, when you use Windows PowerShell to connect to an object, PowerShell will often decide, in advance, which methods and properties will be exposed to you. That’s usually fine, except that sometimes PowerShell guesses wrong; sometimes it fails to show the property or method you really need. That’s the case here; if you bind to a local group account (like the Administrator account) PowerShell exposes only a handful of methods and properties:
Name MemberType ---- ---------- ConvertDNWithBinaryToString CodeMethod ConvertLargeIntegerToInt64 CodeMethod Description Property groupType Property Name Property objectSid Property
That’s where the PSBase object comes in. PSBase lets you get at the “raw” object behind the object PowerShell exposes by default; in other words, PSBase lets you get at all the properties and methods of the object:
Name MemberType ---- ---------- Close Method CommitChanges Method CopyTo Method CreateObjRef Method DeleteTree Method Dispose Method Equals Method GetHashCode Method GetLifetimeService Method GetType Method InitializeLifetimeService Method Invoke Method InvokeGet Method InvokeSet Method MoveTo Method RefreshCache Method Rename Method ToString Method AuthenticationType Property Children Property Container Property Guid Property Name Property NativeGuid Property NativeObject Property ObjectSecurity Property Options Property Parent Property Password Property Path Property Properties Property SchemaClassName Property SchemaEntry Property Site Property UsePropertyCache Property Username Property
In this case, PSBase exposes the Invoke method. In turn, we can then call Invoke, passing it the Add parameter and the Path property containing the user account in Active Directory. What will that do for us? You got it: it will add the user in question (fabrikam\kenmyer) to the local Administrators group on atl-fs-001.
Note. By the way, you might notice that, when calling the Invoke method, we had to use the PSBase object of our user account: $objUser.PSBase.Path. That’s because, by default, we don’t have access to the object Path. The only way to get to that is to use PSBase.
Believe it or not, KE, that’s all you have to do. Run this script, and Ken Myer’s Active Directory account will be added to the local Admins account. Give it a try and see for yourself.
Remember, though, don’t try smashing two cars together for yourself. For that matter, don’t try smashing two anythings together. Several years ago, the Scripting Guys tried an experiment in which we had Peter Costantini and Dean Tsaltas run as fast as they can and then smash together. The hope was that, by combining the two, we would produce some sort of Super Scripting Guy. So what did we end up with instead? Peter and Dean, exactly as they are today.
Let that be a warning to you all.