How Can I Return a List of All the Users Whose User Account Never Expires?

How Can I Return a List of All the Users Whose User Account Never Expires?

  • Comments 1
  • Likes

 

Hey, Scripting Guy! Question

Hey, Scripting Guy! How can I return a list of all the users whose user account never expires?

-- PG

SpacerHey, Scripting Guy! AnswerScript Center

Hey, PG. You know, no one - not even his own mother! - has ever claimed that the Scripting Guy who writes this column is a genius. Needless to say, then, the first time he read this question he thought it said, “How can I return a list of all the users who never expire?” Having once served as a system administrator, the idea of having users who never expired, users who would continue to live - and to torment - their system administrators forever and ever was truly frightening. Fortunately, though, you were just talking about user accounts and not the users themselves.

Note. We should clarify that this Scripting Guy never wanted any of his users to die, not even the one who reformatted her hard drive because she read somewhere that doing so would remove all the old files that she didn’t use anymore. Well, it did.

For some reason, this calls to mind an episode of The Simpsons where Bart - saying his evening prayers - asks God to kill Sideshow Bob. “Bart!” said his mother. “You don’t ask God to kill people!” “That’s right,” added Homer, “you do your own killing.”

Fortunately, no one has to be harmed in any way in order for us to get back a list of all the user accounts that never expire. In fact, all you have to do is run a script similar to this:

On Error Resume Next

Const ADS_SCOPE_SUBTREE = 2

Set objConnection = CreateObject("ADODB.Connection")
Set objCommand =   CreateObject("ADODB.Command")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
Set objCommand.ActiveConnection = objConnection

objCommand.Properties("Page Size") = 1000
objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE 

objCommand.CommandText = _
    "SELECT AdsPath FROM 'LDAP://dc=fabrikam,dc=com' WHERE objectCategory='user'"
Set objRecordSet = objCommand.Execute

objRecordSet.MoveFirst
Do Until objRecordSet.EOF
    Set objUser = GetObject(objRecordSet.Fields("AdsPath").Value)
  
    If objUser.AccountExpirationDate = "1/1/1970" Or Err.Number = -2147467259 Then
        Wscript.Echo objUser.Name
    End If

    objRecordSet.MoveNext
Loop

We should point out that we took a bit of a shortcut here, a shortcut that makes the script easier to write and easier to understand, but one that does make the script a tad bit slower and a little less efficient. Although there are two different properties that can be used to determine whether a user account expires or not - accountExpires and accountExpirationDate - neither one is a simple yes/no kind of value: yes the user account will expire someday, no the user account will never expire. Instead, each property contains a value that either indicates the date that the account expires or the fact that the account will not expire. Because accountExpires is a large integer value that tracks time via the number of nanoseconds that have expired since January 1, 1601, we decided to skip that whole mess and use accountExpirationDate instead.

Not that this totally solves all our problems, mind you. Technically, accountExpirationDate is not even an attribute of a user account; instead, it’s an Active Directory property method. When you ask for the accountExpirationDate, ADSI will actually grab the value of the accountExpires attribute and convert it to a real date. That’s the good news: when you ask for accountExpirationDate you’ll get back a real date-time value, not the number of nanoseconds that have expired since January 1, 1601. The bad news? Because accountExpirationDate isn’t a hard-coded user account attribute you can’t use ADO to search for accounts based on that attribute.

Having fun yet? Now, we could create a somewhat complicated script and do a search using the accountExpires attribute. Being the Scripting Guys, however, we opted to take the easy way out. Instead of retrieving only those user accounts where accountExpires is equal to a certain value, we decided to retrieve all the user accounts, then bind to each individual account and use accountExpirationDate to determine whether the account expires. Like we said, this is not the optimal way to do things; your script will take a bit longer to run. (Although, depending on the number of users you have, “a bit longer” might only be a few seconds.) However, this script is much easier to write, and thus much easier for you to use and modify.

Wait a second: we aren’t done just yet. There’s one more issue we have to deal with. How do we know whether or not a user account will expire? Well, if accountExpirationDate is set to January 1, 1970 then the account will never expire. (Weird, but true.) Unfortunately, though, that’s not the only way to determine whether or not a user account is configured to never expire. An account also doesn’t expire if no accountExpirationDate has been set. You find out accountExpirationDate has no value when you try to access this attribute and get back error number -2147467259 (the much-beloved “Unspecified error” error). That means for each account we need to determine whether accountExpirationDate is set to January 1, 1970 or whether we get back error number -2147467259 when trying to get the value of accountExpirationDate. That’s what this block of code is for:

If objUser.AccountExpirationDate = "1/1/1970" Or Err.Number = -2147467259 Then
    Wscript.Echo objUser.Name
End If

If either of our criteria is true, we echo back the user name; we do that because this is an account that does not expire. If neither criteria is true then we do nothing at all; that’s because this account does expire, and all we care about here are those accounts that never expire.

Well, at least that part is simple.

There’s obviously more to the script than just this simple block of code, but the remainder is pretty much a run-of-the-mill Active Directory search script. If you need more information about searching Active Directory, take a look at our two-part Tales from the Script series Dude, Where’s My Printer? That should give you a good explanation of what the rest of the script does.

Disclaimer. No users were harmed during the making of today’s column. We aren’t saying we weren’t tempted (like, say, the user who uninstalled Microsoft Word because he was finished using it for the day) but ….


Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • I've experienced that objectCategory=user breaks down to CN=Person ... - but contacts are part of objectCategory=user, too! Since contacts don't have an expiration date, wouldn't it be better to use objectClass=user instead of objectCategory?