Through the past few columns, I’ve been demonstrating Windows Server 2008 R2's quite useful AD-related PowerShell cmdlet Get-ADUser. So far, though, my sample Get-ADUser queries have been pretty trivial. Now it’s time to build a more interesting query by asking Active Directory (AD) the question, Which accounts have never logged on? In the process, you’ll improve your PowerShell skills.

Whenever you're trying to pick out a subset of your users according to some criterion, Get-ADUser is the place to start. But to make it work, you have to build a filter, and that filter will almost certainly refer to one or more of the 100-plus attributes that every AD user's account contains. The tough part is usually figuring out which of those attributes can assist you, so let's explore what the list of attributes offers. You can see the attributes by typing 

get-aduser -filter *-properties *|get-member

Searching through the attributes that get-member provides for ones that have logon in their names, you'd find the most promising ones to be LastLogon, LastLogonDate, LastLogonTimestamp, and LogonCount. (Someday, I’ll show you how the where command can simplify this process.) MSDN's AD schema documentation shows that LastLogon and LastLogonDate won't be useful, as they're not replicated amongst DCs. LastLogonDate isn't even in the schema (more on that next month), so we'll focus on LastLogonTimestamp. (In truth, I'm cheating a bit. Most AD geeks have known about LastLogonTimeStamp since 2003, but that's how I'd have researched it, had I just come to the AD party.) Having found this candidate attribute, then, the next step would be to see a few of its typical values with this command:

get-aduser -f * -pr lastlogontimestamp | ft samaccountname,lastlogontimestamp -auto

Here, I'm telling Get-ADUser to retrieve all users but to pipe the output to ft (short for format-table), which I instruct to show only the samaccountname and lastlogontimestamp values, while making the table as compact as possible with the -auto parameter. Its output, when run on a little test AD environment, looks like:

samaccountname lastlogontimestamp
-------------- ------------------
Administrator 129699195295312500
Guest
krbtgt
julesm 129699193327187500

This shows us something interesting about lastlogontimestamp: It shows up either as a huge number or as nothing at all. Guessing that "nothing at all" equals "has never logged on," I tested that by creating a new user account without logging it on, ran the Get-ADUser query again, and it too had no lastlogontimestamp. My query, then, need only look for accounts with empty lastlogontimestamp values.

Unfortunately, PowerShell doesn't make that easy. You can't just use a filter like {lastlogontimestamp -eq ""} or {lastlogontimestamp -eq $null}; instead, you have to use -like (which we've met already) and -not (which we haven't) to write the filter this way:

get-aduser -f {-not ( lastlogontimestamp -like "*")}

The lastlogontimestamp -like "*" parameter matches any record where there's anything at all in the lastlogontimestamp attribute, essentially finding only the "non-empty-LastLogonTimestamp" records. The -not operator negates whatever you feed it—in this case, converting the "find all the non-empty attribute records" query to a "find all the empty attribute records" query.

Run that query, and you'll see all of the never-logged-on folks, but you'll see that the query also returns built-in accounts such as krbtgt and Guest—things that you’ll never want to mess with and don't want in your query results—so you'll need to refine the query filter. A look at krbtgt and Guest reveal that they’re disabled (and have been since their creation). Thus, you need to change the query from "choose all users with an empty lastlogontimestamp" to "choose those users with that empty attribute and that aren't disabled." That query looks like

get-aduser -f {-not ( lastlogontimestamp -like "*") -and (enabled -eq $true)}

The only difference is that I've added -and—the logical "and" operator (another PowerShell quirk is that just about everything useful is prefixed by a dash)—and another filter, enabled -eq $true. That filter means "isn't disabled" because AD's PowerShell tools give users an attribute named enabled rather than disabled, and because PowerShell represents the logical value true with the phrase $true.

In this example, you’ve seen how to explore an AD object's structure and common values. You then built more complex queries by detecting empty attributes, flipping query filters on their head with -not logical values and using -and to join multiple filters. But we're just getting started. See you next month!