Windows PowerShell Tip of the Week

Here’s a quick tip on working with Windows PowerShell. These are published every week for as long as we can come up with new tips. If you have a tip you’d like us to share or a question about how to do something, let us know.

Find more tips in the Windows PowerShell Tip of the Week archive.

Getting Information About the Logged-On User

One of the most useful – and most overlooked – COM objects found in Windows is ADSystemInfo. ADSystemInfo returns a wealth of information about the logged-on user; the computer he or she is using; and the domain that the computer belongs to. Perhaps most important, ADSystemInfo returns the distinguished name of the logged-on user. Why is that so important? Because, in VBScript, it enables you to bind to the logged-on user’s Active Directory user account using code no more complicated than this:

Set objADSysInfo = CreateObject("ADSystemInfo")
strUser = objADSysInfo.UserName

Set objUser = GetObject("LDAP://" & strUser)

For Each objGroup in objUser.memberOf
    Wscript.Echo objGroup
Next

In case you’re wondering, this script connects to the logged-on user’s Active Directory account and then echoes back the distinguished name of each group that user belongs to. A very handy, and oft-requested, hunk of code to have in a logon script.

Of course, considering the fact that this is the Windows PowerShell Tip of the Week, you might also be wondering why we said “in VBScript” and then showed you some VBScript code for retrieving the list of groups that the logged-on user belongs to. Why didn’t we show you a PowerShell solution that uses ADSystemInfo? That’s easy: there is no PowerShell solution that uses ADSystemInfo. In fact, you can’t use ADSystemInfo with Windows PowerShell at all.

Well, OK, that’s not entirely true; it is possible to use ADSystemInfo from within PowerShell; however, the process is far from easy and even farther from being intuitive. That’s because ADSystemInfo is lacking a “wrapper” that makes it easy to access the object from a .NET language like Windows PowerShell. That results in a lot of gyrations involving .NET Reflection classes, the InvokeMember method, and, as near as we can tell, a lot of prayer.

Admittedly, we could show you the .NET workaround for this problem and wish you the best of luck. That would solve your problem (sort of), but wouldn’t be very sporting of us. Therefore, we decided to show you the following approach instead. It’s a bit more code, but it’s way easier to understand (and to modify):

$strName = $env:username
$strFilter = "(&(objectCategory=User)(samAccountName=$strName))"

$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.Filter = $strFilter

$objPath = $objSearcher.FindOne()
$objUser = $objPath.GetDirectoryEntry()
$objUser.memberOf

What are we doing here? Well, for starters, we’re retrieving the logon name for the logged-on user (a value stored in the UserName environment variable). That’s what we do in the first line of code:

$strName = $env:username

In line 2, we then use that value to construct an LDAP search filter. Why an LDAP search filter? That’s another easy one to answer: because we’re going to track down the logged-on user’s Active Directory user account by searching Active Directory for the user with that particular logon (samAccountName).

Note. Yes, we know: there’s a good chance that you don’t know anything about LDAP search filters, and even less about writing Active Directory search scripts. But that’s OK; after all, that’s why we wrote the article Searching Active Directory with Windows PowerShell.

After we construct our LDAP search filter we create an instance of the System.DirectoryServices.DirectorySearcher object; it should come as no great surprise that this is the .NET object we use to search Active Directory. After we create the object we then use the following line of code to assign our LDAP search filter to the DirectorySearcher’s Filter property:

$objSearcher.Filter = $strFilter

Note. You might have noticed that we didn’t specify any additional options for the DirectorySearcher, like which domain to search and which container to start the search in. Because of that, DirectorySearcher will default to searching the local domain, it will begin its search in the domain root, and it will dutifully search each and every container in the domain.

At this point we’re ready to search Active Directory. Because logon names (samAccountNames) must be unique within a domain, we can use the FindOne method to locate the first and only user account with the specified logon name:

$objPath = $objSearcher.FindOne()

After FindOne does its thing, it returns a System.DirectoryServices.SearchResult object that contains several properties and methods for the user account it just discovered. One of those methods is GetDirectoryEntry; if we call that method, it will bind us directly to the user account in Active Directory. So guess what? In our next line of code we call GetDirectoryEntry and, well, bind directly to the Active Directory user account for the logged-on user:

$objUser = $objPath.GetDirectoryEntry()

Once we’ve made the connection we can then display any (or all) of the properties of that user account. In our sample script we’ve elected to display just the memberOf attribute, which returns the distinguished names for all the Active Directory groups that the user belongs to. But now that you know how to bind to the logged-on user’s Active Directory user account, well, you can display any attribute values you want.

What’s that? Could you use the same approach to retrieve information about the local computer? Of course you can. Here’s a revised script that connects to the computer account in Active Directory and reports back the name of the operating system installed on the machine:

$strName = $env:computername
$strFilter = "(&(objectCategory=Computer)(Name=$strName))"

$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.Filter = $strFilter

$objPath = $objSearcher.FindOne()
$objComputer = $objPath.GetDirectoryEntry()
$objComputer.operatingSystem

As you can see, we made only a few minor changes here. First, we assigned $strName the value of the ComputerName environment variable:

$strName = $env:computername

In addition, we also modified our search filter, tweaking it so it will search for computers with a Name equal to the local computer name:

$strFilter = "(&(objectCategory=Computer)(Name=$strName))"

And then, of course, we displayed the value of the operatingSystem attribute:

$objComputer.operatingSystem

That should carry you through from now until next week. See you then!