May 23rd, 2008

Hey, Scripting Guy! How Can I Use Windows PowerShell to Determine Whether a Local User Account Exists?

Hey, Scripting Guy! Question

Hey, Scripting Guy! How can I use Windows PowerShell to determine if a specified local user account exists on a computer?
— MM

SpacerHey, Scripting Guy! AnswerScript Center

Hey, MM. Have you ever heard the expression “good things come to those who wait”? Well, to tell you the truth, the Scripting Guys have never put much credence in that old saying; after all, the Scripting Guy who writes this column has been waiting for years for the New York Yankees to call him and ask him to come play outfield for them. So far he’s heard nothing, absolutely nothing.

Note to the New York Yankees front office. If it’s a question about money, then by all means please call this Scripting Guy collect.

After all, he’ll just charge the call to Microsoft anyway.

The point is, the Scripting Guy who writes this column has spent the past few years sitting around waiting for the New York Yankees to call. That’s pretty much the same thing many of you have been doing: maybe not waiting for the New York Yankees to call, but instead waiting for the Scripting Guys to write a Windows PowerShell version of the world-famous Scriptomatic. Well, to be honest, that’s not going to happen: the Scripting Guy who writes this column simply can’t afford to leave his desk, go down to the laboratory, and start tinkering with a PowerShell version of the Scriptomatic. After all, what if the Yankees call while he’s down there?

But you know what? As it turns out, sometimes good things do come to those who wait. Granted, the Scripting Guys aren’t going to write a PowerShell version of the Scriptomatic. But that’s OK, because Ed Wilson has.

For those of you don’t know him, Ed Wilson is a Microsoft scripting guru; a world-class educator; and a prolific author. (Prolific? The Scripting Guy who writes this column feels smug because he writes one measly little article each day. Ed Wilson pretty much writes a new book each day.) No sooner did Ed finish up with Windows PowerShell Step-by-Step then he turned around and wrote a new book, Windows PowerShell Scripting Guide. And, along with the new book, Ed also found the time to put together the Windows PowerShell Scriptomatic.

Note. We have this sinking feeling that the New York Yankees have also called Ed and asked him to take over in centerfield for them. But we’re too afraid to ask Ed about that.

At any rate, if you’re looking for a quick and easy way to write WMI scripts using Windows PowerShell (and we know that you are), it’s hard to top Ed’s new PowerShell Scriptomatic. For more information, and for a link to the download file, click here.

No, not here. Click up there.

That’s better.

So what does all this have to do with you, MM? Well, now that we think about it, not much. After all, the PowerShell Scriptomatic writes WMI scripts, and you don’t really need to (or want to) use WMI here; instead, you want to do ADSI. In this case, the PowerShell Scriptomatic won’t help you all that much.

However, this script should help a little (and, believe it or not, it’s a scripting-related item that Ed Wilson didn’t write!):

$objComputer = [ADSI]("WinNT://atl-ws-001,computer")

$colUsers = ($objComputer.psbase.children |
    Where-Object {$_.psBase.schemaClassName -eq "User"} |
        Select-Object -expand Name)

$blnFound = $colUsers -contains "kenmyer"

if ($blnFound)
    {"The user account exists."}
else
    {"The user account does not exist."}

Well, there you have it, MM. See you later.

Oh, right: you’d like an explanation of how this script works, wouldn’t you? (Trust us, we’d all like an explanation of how this script works; there’s some crazy-looking code in there, isn’t there?) Tell you what: let’s break the code down line-by-line and see what we can come up with.

And if we can’t come up with something we have no doubt that Ed Wilson will.

We actually start out in simple enough fashion, using the [ADSI] “type adapter” to make a connection to the computer atl-ws-001. What’s a type adapter? Well, in this case it’s a shortcut way to tell PowerShell that you want to work with ADSI. Could you write this same script without using the type adapter? Sure; however, to do that you’d have to delve into the .NET Framework and create – and use – an instance of the System.DirectoryServices.DirectoryEntry class. Needless to say, the type adapter approach is much easier.

Like we said, line 1 is simple enough. Line 2 is a different story, to say the least:

$colUsers = ($objComputer.psbase.children |
    Where-Object {$_.psBase.schemaClassName -eq "User"} |
        Select-Object -expand Name)

So what’s going on here? Good question. By default, any time you use Windows PowerShell and ADSI to connect to a computer you get back a limited set of properties and methods for that computer; in fact, all you get back are the following:

Name                        MemberType 
------                      ----------
ConvertDNWithBinaryToString CodeMethod 
ConvertLargeIntegerToInt64  CodeMethod 
Name                        Property   
OperatingSystem             Property   
OperatingSystemVersion      Property   
Owner                       Property   
Processor                   Property   
ProcessorCount              Property

That’s nice, but – as you can see – there’s nothing in the list that suggests we can retrieve a collection of all the user accounts on that computer. Have we already hit a dead end?

No, far from it. When you use Windows PowerShell to connect to an object (not just a computer but any object) PowerShell often makes a “best guess” as to which properties and methods you are interested in. (OK, it’s not really a guess; instead PowerShell follows a predetermined recipe for returning properties and methods.) Often times that works just fine, but sometimes it doesn’t; sometimes – like in this case – you don’t get back the properties and methods that you really need.

That’s where the PSBase object comes in. As the name implies (kind of, anyway) the PSBase object is the base on which the PowerShell object was built; more important, the PSBase object contains all the properties and methods of the object in question. If we look at the properties and methods of $objComputer.PSBase we see, among many properties and methods, the following:

Children                  Property   System.DirectoryServices.DirectoryEntries Children {get;}

As it turns out, the Children property contains all the child objects on the computer; that includes users, groups, printers, services, and what-have-you. In other words, this is the property we’ve been looking for.

Of course, the only children we’re interested in at the moment are user accounts. (Um, no offense, Scripting Son.) That’s why we pipe this data to the Where-Object cmdlet:

Where-Object {$_.psBase.schemaClassName -eq "User"}

And what are we asking Where-Object to do? We’re asking it to filter out anything that doesn’t have a SchemaClassName equal to User. In other words, all we want to get back are user accounts.

Note. Good question: what if we wanted only the group accounts? Well, in that case our Where-Object command would look like this:

Where-Object {$_.psbase.schemaclassname -eq "Group"}

And so on.

After we’ve run the data through Where-Object we’re left with a collection containing all the information available about all the user accounts on the computer atl-ws-001. That’s great, except we don’t want all the information available about all the user accounts on the computer; all we want to know is if a user account named kenmyer exists on that machine. How are we supposed to determine that?

No doubt there are a number of ways we could do this. For us the easiest approach was to take this collection and pipe it through the Select-Object cmdlet, like so:

Select-Object -expand Name

What’s the point of doing that? Well, all we really care about is the Name property; for this script none of the other user account properties matter to us in the least. Therefore, we use Select-Object to filter out all the properties and property values except for the user Name.

So then what’s the –expand thing for? Well, when you access the Name property each name comes back in the form of an array; if we just did Select-Object Name we’d get back something similar to this:

{Administrator}
{ASPNET}
{Guest}
{HelpAssistant}
{jonhaas}
{kenmyer}
{SUPPORT_388945a0}

Strange, but true.

Having each name come back as a little miniature array (each array containing a single name) complicates the process of determining whether a particular user account exists or not. Therefore, we use the –expand parameter to tell Select-Object to “expand” the property value; in the case of an array, that simply means that we want to extract each and every value inside that array. That means that, ultimately, $colUsers will contain the following values:

Administrator
ASPNET
Guest
HelpAssistant
jonhaas
kenmyer
SUPPORT_388945a0

At this point things become pretty easy. You say we need to determine whether or not a user account named kenmyer exists on atl-ws-001? That’s fine; after all, we can now use the –contains operator to determine whether or not the value kenmyer appears as an item in our collection:

$blnFound = $colUsers -contains "kenmyer"

The –contains operator will return True ($True) if the user account exists, and return False ($False) if the user account doesn’t exist. With that in mind, we can then use the following block of code to check the value of the variable $blnFound, and echo back the appropriate message:

if ($blnFound)
    {"The user account exists."}
else
    {"The user account does not exist."}

And that’s how this script works!

We hope that helps, MM. (Incidentally, we know you have a few other questions about PowerShell, and we’ll see what we can do about answering them as well.) In the meantime, we encourage everyone to check out the Windows PowerShell Scriptomatic. Oh, and we encourage the New York Yankees to give the Scripting Guy who writes this column a call. As you guys know better than anyone, you’re currently paying outfielder Johnny Damon $13 million a year; you’re also currently in last place in the American League East. We thought we should let you know that the Scripting Guy who writes this column is prepared to take Johnny Damon’s place, and for a piddling $3 million a year at that.

Oh: and that will almost guarantee that you’ll remain in last place in the American League East. Something to think about, eh?

Author

0 comments

Discussion are closed.