Hey, Scripting Guy! Question

Hey, Scripting Guy! How can I determine whether or not my local users are required to have a password?

-- GT

SpacerHey, Scripting Guy! AnswerTechNet Script Center

Hey, GT. You know, rather than beat around the bush we decided to come right out and admit it: yes, the rumors that have been circulating around the Internet are true. One of the Scripting Guys, his brothers, his son, and various Scripting Nephews have been playing a strange, customized version of Wiffle Ball in the Scripting Parents’ front yard for more than 20 years. To say that this one-on-one game leans towards the defensive side of things would be an understatement: after all, it’s not unusual to have a game that goes 15 innings before anyone scores. The unofficial record for most runs scored in a game is 5 and, in 20 years of continual, oft-times cut-throat playing, no one has ever hit back-to-back home runs in a game.

Until now. Yes, it’s true: on a recent visit home one of the Scripting Guys (Greg is far too modest to admit that it was he) smacked back-to-back home runs in the fourth inning, and off the Scripting Son, no less. It was, without a doubt, the happiest day of this Scripting Guy’s life. And probably the greatest sporting achievement of the 21st century. (OK, sure, Barry Bonds hit 73 home runs in one season. But home runs are a dime-a-dozen in the big leagues. We’d like to see Barry hit 73 homers in the Scripting Parents’ front yard.)

Disclaimer. In all fairness, we should note that in the fifth inning one of the balls disappeared forever into the neighbor’s yard while another split in half; consequently, the game ended a bit prematurely. The Scripting Son claims that, because the game did not go five innings, the two home runs do not count. The Scripting Son is wrong.

Of course, by now you’re probably wondering, “What the heck does this have to do with determining whether or not local users are required to have a password?” Well, to tell you the truth, GT, absolutely nothing. It’s just that, for the Scripting Guy in question, the high points in his life continue to get fewer and farther between. He has to take a moment to revel in whatever glory he can, whenever he can.

OK; that’s enough reveling. Here’s a script that can determine whether or not local users are required to have a password:

Const ADS_UF_PASSWD_NOTREQD = &H0020 
 
Set colUsers = GetObject("WinNT://atl-fs-01")
colUsers.Filter = Array("User")

For Each objUser in colUsers
   If objUser.UserFlags AND ADS_UF_PASSWD_NOTREQD Then
        Wscript.Echo objUser.Name & ": Password not required."
    Else
        Wscript.Echo objUser.Name & ": Password required."
    End If
Next

You’re right: it’s nowhere near as exciting as back-to-back home runs, is it? (But, then again, not everyone can hit back-to-back homers, if you know what we mean.) However, while the script might not be very exciting, it is kind of useful. With that in mind, let’s sit down and see if we can figure out how it works.

As you can see, we start out by defining a constant named ADS_UF_PASSWD_NOTREQD and setting the value to &H0020; we’ll explain why we do that in just a moment. After that we use this line of code to connect to the System Account Management (SAM) database on the computer atl-fs-01:

Set colUsers = GetObject("WinNT://atl-fs-01")

By default, any time we connect to the SAM database we get back information for all sorts of things, including local users, local groups, installed services, installed printers, etc. Because we’re only interested in user accounts we then use the Filter property to filter out everything in the returned collection except those self-same user accounts:

colUsers.Filter = Array("User")

Got all that? Good. Now that we have a collection of all the user accounts (and only the user accounts) found on the computer atl-fs-01, our next step is to examine each of those accounts and determine whether or not a password is required. That’s what we do here:

If objUser.UserFlags AND ADS_UF_PASSWD_NOTREQD Then

Yes, that is kind of an odd-looking line of code, isn’t it? As it turns out, information about whether or not a user requires a password is stored in the UserFlags attribute. UserFlags is a “bitmask” attribute; that means it’s an attribute that actually contains information about a number of properties, including such things as whether or not a user password expires, whether or not the account itself expires, and whether the user is allowed to change his or her password. For a complete list of properties stored in the UserFlags attribute see the topic ADS_USER_FLAG_ENUM in the ADSI SDK.

Oh, what the heck: here are some of the other more useful properties (as well as their constants and values) that can be managed using UserFlags:

Property

Constant

Value

Logon script will be executed

ADS_UF_SCRIPT

&H0001

Account is disabled

ADS_UF_ACCOUNTDISABLE

&H0002

Account requires a home directory

ADS_UF_HOMEDIR_REQUIRED

&H0008

Account is locked out

ADS_UF_LOCKOUT

&H0010

Account does not require a password

ADS_UF_PASSWD_NOTREQD

&H0020

User cannot change password

ADS_UF_PASSWD_CANT_CHANGE

&H0040

Encrypted text password allowed

ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED

&H0080

Account password never expires

ADS_UF_DONT_EXPIRE_PASSWD

&H10000

Smartcard required for logon

ADS_UF_SMARTCARD_REQUIRED

&H40000

Password has expired

ADS_UF_PASSWORD_EXPIRED

&H800000

Explaining how bitmask values work lies a bit outside the scope of this column; for more details, see this Scripting Guys webcast. For now, we’ll simply note that a bitmask is like a control panel with a series of switches, switches that can either be on or off. When we execute a line of code like If objUser.UserFlags AND ADS_UF_PASSWD_NOTREQD Then what we’re really doing is checking to see if the switch labeled ADS_UF_PASSWD_NOTREQD (the switch that tells us whether or not the user is required to have a password) is on. If it is, that means that user does not have to have a password; in turn, we then echo back the user Name and the fact that no password is required:

script.Echo objUser.Name & ": Password not required.

Note. Be careful not to get mixed up here. Remember, the attribute is Password is not required, not Password is required. If the switch is on the user doesn’t have to have a password. (Because it’s true that no password is required.)

If ADS_UF_PASSWD_NOTREQD is off that means a password is required (again, that seems backwards, but think about it for a second). Thus we echo back the fact that this user requires a password:

Wscript.Echo objUser.Name & ": Password required.

When all is said and done we should get back information similar to this:

Administrator: Password required.
ASPNET: Password not required.
Ken Myer: Password required.
pilarackerman: Password required.
Guest: Password not required.
HelpAssistant: Password required.
Test: Password required.

Very nice.

Admittedly, working with bitmasks can be a bit confusing, especially when it comes time to modify these values. However, simply reading the values isn’t too bad: you just need to use the proper constant and value. For example, would you like to know whether or not a user’s password will ever expire? Well, to do that, define a constant named ADS_UF_DONT_EXPIRE_PASSWD, set the value to &H10000, and modify the output section of the script accordingly:

Const ADS_UF_DONT_EXPIRE_PASSWD = &H10000 
 
Set colUsers = GetObject("WinNT://atl-fs-01")
colUsers.Filter = Array("User")

For Each objUser in colUsers
   If objUser.UserFlags AND ADS_UF_DONT_EXPIRE_PASSWD Then
        Wscript.Echo objUser.Name & ": Password does not expire."
    Else
        Wscript.Echo objUser.Name & ": Password expires."
    End If
Next

See? That’s not so bad.

And now for something really cool: after hitting back-to-back home runs, on the very next pitch the Scripting Guy lined a double off the birdbath. And then he followed that by - wait, we’re not done yet; we still have lots more to tell you. In fact, we … guys? Hello? Guys?

Hmmm; the network must have gone down again.