Mark Minasi's Windows Networking Tech Page
Issue #95 February 2012

Document copyright 2012 Mark Minasi; please see below for info on subscribing, unsubscribing or copying portions of this text.

What's Inside

  • News
    • Learn with My Seminars, Audio Recordings and More!
  • Tech Section
    • Getting Things Done in PowerShell, Even if You're Not a PowerShell Expert:  A Plan
  • Conferences
  • To Subscribe, Unsubscribe, Read Old Newsletters or Change Your Email Address

News

Hi all —

This month, I'm passing along what I think is the shortest path from "gee, I wonder if I can get that problem solved in PowerShell" to "hot diggity, I did it!," and I hope I wrote it so that it's useful even to folks who only know a smidgen of PowerShell.  Oh, and in passing it lets me solve a problem that may be of value to the AD admins out there -- an easy way to see who's not logged on since a given date -- but before I tell you about it, however, a word from our sponsor...

Don't Miss ITEdge Intersection!

Windows Server 2016 is coming, are you ready? Server 2016 and many other important topics are the focus of ITEdge Intersection at the MGM Grand in Las Vegas, Oct 25-29. Your favorite speakers are there, including Scott Guthrie, Brad Anderson, Jeffrey Snover and of course, Mark Minasi. Register today for the conference and a workshop and you can go home with an XBOX One S, Surface 3 table or MS Band 2. Register at www.itedgeintersection.com.

Bring Mark's Windows 10 Support Class and Our PowerShell Classes to Your Site

Mark has delivered his new "Deploying, Managing and Securing "the Last Windows: Working with Windows 10" class to nearly a dozen clients, and the reviews are uniformly great.  Designed for the Windows 7 support pro, this course tells you everything you need to know to support, deploy, or manage Windows 10 systems.  Fast-paced, lecture-based and entertaining, this course gives you the shortest path to Windows 10 expertise. Learn about Windows 10's completely new licensing approach.  See how to enable the new "parallel universe" security tools.  Discover the cloud-y new tools in Windows 10 like joining a system not to an Active Directory but instead to a cloud.  Find out what you can learn at our course outline at http://www.minasi.com/w10class.htm. 

To bring this class to your site, just drop us a line at assistant@minasi.com.

And while you're at it, are your folks PowerShell adepts?  There really isn't a productivity-enhancer available for Windows support people like PowerShell.  Bring Mark's "Learning PowerShell: Hands-On with AD, Networking, and More" class to your site and he'll make your command-line-hating techies into PowerShell fans.  Outline at http://www.minasi.com/Posh2day/. 

Navigating PowerShell:  How to Get from Problem to Solution in the Shortest Time

Don't do much PowerShell, or only find it useful if you can see how to do something at a class or after some Googling?  Tried it but there's too gosh-darn much to understand all at once?  In this article, I'd like to explain my strategy for getting something done in PowerShell.

I can't seem to learn administrative or programming tools (and PowerShell can be either) except by trying to accomplish something useful with them.  So when I first turned my attention to R2's new Active Directory-aware PowerShell cmdlets nearly three years ago, I set my sights on being able to find all of the users who hadn't logged in since a given date.  Now, just to eliminate the suspense, let me immediately reveal the answer, which that looks like this:

get-aduser -filter {lastlogondate -le "January 1,2012"}

The point of this story isn't to explain that command -- see my "Windows Power Tools" articles in recent issues of Windows IT Pro magazine at www.windowsitpro.com for that story --  but instead to recount how I got from the "how do I..." part to the solution, as it lets me lay out a strategy about how to solve PowerShell problems by exploiting PowerShell's structure, its Help system and a few PowerShell cmdlets.  (That's the PowerShell word for "command."  Among other reasons, PowerShell's creator, Jeff Snover, wanted a short, easy-to-remember word that no one else used.  As he explained to me, "search the Internet for 'cmdlet' and pretty much the only thing that comes up is PowerShell-related information."  And by the way, "cmdlet" is pronounced "commandlet.")  PowerShell commands initially look ugly, but in truth the key to loving PoSH (as it's known to PowerShell experts and no, I'm not kidding) is in understanding that its greatest strengths are in its consistency and its "discoverability."  That's all a long-winded way of saying, "the climb to PowerShell paradise is actually not a very high one, but watch that first step... it's a lulu!"

Here's how I attack a PowerShell puzzler, and let me warn you that none of this is intended to be read as any sort of evidence of genius on my part... it's just me passing along some experience and a few useful PowerShell commands. (Remember that you needn't have R2 to use PowerShell on your AD -- see Newsletter #86 to see how even 2003 domains can use the R2 tools.)

Step One: Find the Noun

As you may already know, PowerShell commands are basically all two-word combinations in verb-noun form, where they use the word "verb" a bit loosely.  For example, "get-process" tells PowerShell, "go get a list of all of the running processes on the system" -- somewhat like what Task Manager does.  Once it retrieves those processes, it displays them on the screen, but you could alternatively tell PowerShell to do something else with it... but let's not worry about that right now.  What is important is understanding how helpful knowing that command is, as the word after the dash is "process."  That means that anything PowerShell-related that works with the list of currently-running processes will end with "-process."  Thus, we'll see in the next step that it's a snap to then find that PowerShell's process-related cmdlets are debug-process, get-process, start-process, stop-process, and wait-process, and knowing that gives us a start on figuring out whether or not PowerShell can solve a given problem.

Just to give you a taste of this, some of PowerShell's built-in nouns are ACL, Date, Event, EventLog, ComputerRestorePoint, AuthenticodeSignature, Service and Random.  Notice that they're always singular -- it's never get-processes, it's get-process.  So how do you find the noun?  One way is to use a command called get-command to, well, get information about all of the currently-loaded PowerShell cmdlets.  You can even tell get-command to extract just the nouns, sort them and show them:

get-command -type cmdlet|sort noun|select noun|get-unique -asstring

In case you're wondering, "get-command -type cmdlet" retrieves all cmdlets, "sort noun" sorts that list of cmdlets by their nouns -- and yes, PowerShell knows how to extract just a part of things like commands, processes, or user objects -- and then putting all of the ones that have "ACL" as their noun up top and putting all of the ones with "XML" as their noun on the bottom.  I only want to see the nouns, though, and get-command shows me more that just the nouns, so "select noun" pulls out just the nouns.  That works, but, as there are often multiple cmdlets with the same noun, we'd end up with a list of nouns that was sorted, but that contained a lot of duplicates of any given noun.  "get-unique -asstring" solves that problem.

So what PowerShell nouns refer to Active Directory user objects?  Well, if you run the get-command cmdlet above, you won't see any.  Fortunately, though, I had a bit of "inside information," as I knew that PowerShell doesn't get AD-smart until you load a file that contains R2's 76 AD-related cmdlets."  That's, a file called in PowerShell-ese a "module."  I knew that because I'd been told that none of the AD cmdlets would work until I typed

import-module activedirectory

If you know that the cmdlets (and their nouns) are in some given module, then load the module with import-module and then type get-command -module modulename, so I typed

get-command -module activedirectory

Or I could combine our previous set of commands to pull out just the nouns:

get-command -module activedirectory|sort noun|select noun|get-unique -asstring

That returned not one or two nouns, but 31 nouns, including ones with names like ADAccount, ADObject, and ADUser.  They all look promising, so it's time to run them through Step Two.  But before we do that, let me offer in passing that I don't want you all to think that I actually counted 31 nouns, 76 cmdlets or whatever.  (I'm okay with having a reputation for being a trifle eccentric, but..) PowerShell's got a command "measure-object" that you can use like this:

gcm -module activedirectory|sort noun|select noun|get-unique -asstring | measure-object

It can even do some basic statistics if you feed it a pile of numbers.  But back to our story...

Step Two:  Use Get-Command to See What You Can Do With It

With a PowerShell noun in hand, let's see what it'll do.  To see that, just type "get-command *-noun," so I'd try

get-command *-aduser

After doing that, I'd find that there are four commands:  new-aduser, get-aduser, set-aduser, and remove-aduser.  PowerShell tries to stay within a fairly small range of verbs, and that's nice because once you've found one of PowerShell's nouns, you'll often see four commands using that noun with verbs "new," "get," "set," and "remove."  In the case of ADuser,

  • "New" means "create."  (No, "new" isn't a verb, but it's verb-ish in this case.)  To create a new AD user account, you wouldn't try a command like "create-aduser," as PowerShell doesn't use "create" in general.  To guess the command to create a new AD user, try "new-aduser" first.
  • "Remove" means "delete."  Same story... try to delete a user account with "delete-aduser" and you'll get an error.  "Remove-aduser" would zap a user account.
  • "Get" retrieves information about a PowerShell object.  Just as we've seen that "get-command" retrieves information about PowerShell's commands (hmmm, shouldn't it have been "get-cmdlet?"), "get-aduser" can show (1) what users exist in your AD, and (2) information about them like name, location, AD attributes and the rest.  At this point, finding out that there's a get-aduser will get me thinking that clearly this is the command that has the best chance of telling me something about how long it's been since someone's logged in.
  • "Set" lets you modify one or more things about an object.  You'd use set-aduser to change a user's Displayname, office location, description, whether the account is disabled and the like.

Doing get-command against ADUser, ADAccount and ADObject shows that there's only a set-aduser and set-adobject, so clearly those would be the target-rich environments.

Step Three:  Use Help to Get More Information

Now that you've got a candidate cmdlet or two, use PowerShell's get-help to find out more.  If you've not used it before, it's got parameters named "-examples" and "-full."  Even better, you'll find that the examples are about 95% correct, at least for the AD cmdlets.  To find out more about get-aduser and get-adobject, I'd type

get-help get-aduser -full

get-help get-adobject -full

A look at the examples on those cmdlets shows that get-aduser has an option "-filter" that looks promising, so let's see what it does in Step Four. Before we do that, though, let me make a short, important suggestion:  read the Help.  Honest, it's not like most Help, it's actually, well, helpful.  This is a discussion for another day -- and I promise to write up a guided tour of PowerShell help that will, I think, convince you that it's worth your time -- but this is actually a case where reading the Help could be a better use of your time than what most of us will logically do first -- Google.  Yes, Googling "how do I do [something] with PowerShell?" will yield answers, but they may not be good answers because (1) PowerShell's complex and so there may be five different ways to get something done, and the way that you find on some web page may not be the best fit for your needs, and (2) at least in its first three versions, PowerShell has changed, grown, and improved radically -- and it's far too easy to end up reading a page about how solve a PowerShell problem with PoSH version 1.

Step Four:  Look at Some Objects

Reading the examples in the get-aduser help led me to whip up a little test Active Directory to try this command out:

get-aduser -filter *

(Please always try AD tools out on a test network... get-aduser's not likely to actually modify an AD but you'd hate to find that running that command violated some internal privacy policy and got you fired.)  Some of the output on my AD looks like this:

DistinguishedName : CN=TJefferson,CN=Users,DC=apple-orchard,DC=net
Enabled : True
GivenName : Thomas
Name : TJefferson
ObjectClass : user
ObjectGUID : e0f5f77e-1d65-4c59-83f4-0164eba1eebb
SamAccountName : TJ
SID : S-1-5-21-2302930610-509929598-2600203374-1606
Surname : Jefferson
UserPrincipalName : thomas@minasi.com

So now we know that get-aduser does indeed show users, but not in a way that's going to make me very happy.  That output seems to be showing me that all PowerShell's going to cough up about my user TJ is a handful of information, none of which refers to when he last logged on.  At this point, I'd return to Help to find that the AD folks at Microsoft didn't think that you'd normally want to see everything about users when you employed get-aduser, but that if you did want more information, you can add a parameter -properties either with a list of desired properties or just "*," as in these examples:

get-aduser -filter * -properties *

Which shows all properties (there are over 100 of them before you add Exchange!), or

get-aduser -filter * -properties displayname

Which looks like this:

DisplayName : Thomas Jefferson
DistinguishedName : CN=TJefferson,CN=Users,DC=apple-orchard,DC=net
Enabled : True
GivenName : Thomas
Name : TJefferson
ObjectClass : user
ObjectGUID : e0f5f77e-1d65-4c59-83f4-0164eba1eebb
SamAccountName : TJ
SID : S-1-5-21-2302930610-509929598-2600203374-1606
Surname : Jefferson
UserPrincipalName : thomas@minasi.com

Again, -properties * shows a lot of properties, and unless you've got a lot of time on your hands, you'll probably want to get a little help looking for logon-related properties, and that'd be Step Five.

Step Five:  Use Get-Member, Where-Object and Name to Filter the Results

So we're looking for an AD user attribute that'll tell us when a user last logged on so that we can write a query of some kind with get-aduser that'll tell us who hasn't logged on since some arbitrary date.  One never knows whether the phrase "logon" or "login" is more correct, so I'd like to just see any user attribute with "log" in its name.  To do that, let's meet another "get" command, "get-member."  Get-aduser gets PowerShell objects that are user objects, and objects have attributes with names like DisplayName, DistinguishedName, Enabled and the like.  I'm searching here not for an attribute with "log" in it, but rather an attribute with "log" in its name.  I can retrieve the names of an object's attributes, as well as some descriptive information on those attributes, using get-member.  That looks like

get-aduser -filter * | get-member

That produces some output that looks like this:

Again, this isn't information about any given user account, it's information about what kind of information you can get from any given user account.  Notice that every AD user attribute (Contains, DistinguishedName, ObjectClass, SID, etc) has, in turn, three attributes -- Name, MemberType, and Definition.  "Name" is interesting because it's the thing that we're going to search on.  "MemberType" is interesting because we're only looking for ones named "Property," rather than "Method."  (That's a story for another day.)  "Definition" tells us what kind of data it contains, and in what you can see above,  "Bool" means a value that's either true or false and "String" is just text.  What we're hoping to find is an attribute of some type with word "date" or "time in it.  Also, the list of attributes is pretty short because -- whacks self on head -- I forgot to ask get-aduser to show me all the properties.  Typing "get-aduser -properties * | get-member" yields a much longer list and, again, if you feel like picking through over 100 different attributes, then you're welcome to, but I'm lazy, so I'll want to take the output of the get-member command and filter out just the "Property" items with "log" in their Name.

Unlike get-aduser, get-member doesn't have a "-filter" option.  It does have a "-membertype" parameter, so we can add "-membertype Property" to tell get-member to skip the "Method" type attributes.  To pick out just the items with "log" in their names, we'll use where-object, which looks like

where-object { some criterion }

Explaining where-object in detail also a story for another day, but here's the syntax that'll do the job for us in this case:

where-object { $_.name  -like  "*log*" }

Picked apart, "$_.name"  removes just the "Name" attributes from each line that get-member produces.  "*log*" is a pattern that matches any string with the letters "log" in it -- as is often the case in Windows, the asterisk ("*") means "match this to anything."  "-like" is just one of PowerShell's "comparison operators."  If you've ever looked at other programming-like languages you'd probably expect to see something like {$_.name = "*log*"} with an equals sign in it, but PowerShell instead uses "-like" for a variety of reasons.  (In case you're wondering, PowerShell is case-insensitive when doing text comparisons.)

Glue the three commands together and you get this:

get-aduser -filter * -properties * | get-member -membertype "Property" | where-object { $_.name -like "*log*" }

That produces something like this:

Of the five attributes listed, "lastLogonDate" not only has a promising-sounding name, but it's also of type "System.DateTime," which sounds similarly promising.  From there, I'd probably do one more command to see what the actual values look like for various users:

get-aduser -filter * -properties lastlogondate | select name,lastlogondate

Notice there that you type "name,lastlogondate" without any spaces!

Step Six: Try Out the Query

Armed with the knowledge that LastLogonDate appears to be what we've been looking for, it's easy to assemble the query for get-aduser's filter.  Once I tell you that the PowerShell operator for "is less than or equal to" is written "-le," which essentially means "is earlier than" when we're talking dates, I could ask my AD for all of the user accounts that haven't logged on since 1 February 2012 with this query:

get-aduser -filter { LastLogonDate -le "1 February 2012"}

That basically answers the query that I first asked.  There's more tweaking that could go on from this point -- prettier output, different ways of expressing the "find all people who've not logged on since..," that sort of thing, but again my Windows IT Pro mag articles cover that in more detail.  I intended this only as an example of how to use get-command, get-unique, get-noun, and get-member to work your way through solving a problem with PowerShell.  Use what I've done here as a template and you'll be pleasantly surprised at what PowerShell can do for you!

Upcoming Conferences


  • I will be keynoting the free Tampa IT Pro Camp 2016 on 20 August 2016. Great speakers, sign up now to learn great stuff from some terrific speakers.  https://www.eventbrite.com/e/tampa-it-pro-camp-2016-tickets-24569378673 and tell 'em Mark sent you.
  • I will be presenting at IT/Dev Connections in Vegas the week of 10 October. Http://www.itdevconnections.com/dc16/Public/Enter.aspx for more info.
  • I will also be speaking at the Intersection show also in Vegas the week of 24 October.  Visit https://next.devintersection.com for more info. 
  • December TechMentor is in Orlando this year and I'm keynoting about Server 2016. https://techmentorevents.com/Home.aspx for all the scoop!
I hope to see some of you at one of these shows!

To Subscribe/Unsubscribe, Read Old Newsletters or Change Your Email Address

To subscribe, visit http://www.minasi.com/nwsreg.htm. To change e-mail or other info, link to http://www.minasi.com/edit-newsletter-record.htm.  To unsubscribe, link to http://www.minasi.com/unsubs.htm. Visit the Archives at http://www.minasi.com/archive.htm. Please do not reply to this mail; for comments, please link to http://www.minasi.com/gethelp.

All contents copyright 2012 Mark Minasi.  I encourage you to quote this material, SO LONG as you include this entire document; thanks.