Learn about Windows PowerShell
Hey, Scripting Guy! I am interested in using Windows PowerShell to query Active Directory. We have deployed Windows Server 2008 R2 on our domain, and upgraded one of our domain controllers. I would like to be able to query for a list of all users whose passwords do not expire. When I did a Bing search, I was able to find an old Hey, Scripting Guy! post that talks about finding users without expiring passwords, but it was written in VBScript and appears pretty complicated. Is there an easy way to migrate this script to Windows PowerShell 2.0?
Microsoft Scripting Guy Ed Wilson here. The Scripting Wife bought me a new stainless steel teapot. I, um, kept breaking the previous teapots she bought. Whoops. I decided to test my new tea pot in preparation for a meeting with the people from PoshCode.org about hosting the 2010 Scripting Games. I made a nice green tea with cinnamon stick and lemon grass. It worked out well, and the meeting went smoothly. We have already begun to announce the 2010 Scripting Games, and registration for the games will be open soon. We are returning the competitive aspect to the games, because it seems scripters are a bloodthirsty lot and want to know who is the best. There will be all kinds of cool prizes and a grand prize for the best scripter who enters the games. We will have certificates, and a script-off if the games are tied at the end. Our partnership with the Microsoft MVPs at PoshCode.org is really beginning to pay off. Stay tuned for more details. If you are on Twitter, create a filter for #2010sg to quickly find the 2010 Scripting Games details when they become available.
To find all of the users in an Organizational Unit (OU) as well as all of the users in child OUs, you need to specify the searchbase. A VBScript that will list all of the users in an OU and in all of the child OUs was the feature of a Hey, Scripting Guy! post several years ago. That script required 17 lines of code to perform the query. In Windows PowerShell 2.0, using the Active Directory module from the Windows Server 2008 R2 Remote Server Admin Tools (RSAT), it is a single line of code. After you have imported the ActiveDirectory module by using the Import-Module cmdlet, you can use the Get-ADUser cmdlet to search and retrieve all of the users from a specific organizational unit (OU).
For more information about installing the RSAT tools on Windows 7 and using the ActiveDirectory module, refer to last Monday’s Hey Scripting Guy! post.
To retrieve all of the users from a specific location, supply the wild card character (“*”) to the -filter parameter. To search recursively beginning at a particular location in Active Directory, use the –searchbase parameter. The beginning search location must be supplied to the –searchbase parameter as a distinguished name value. If you are unsure about what the distinguished name of a particular OU is, you can look it up in the ADSI Edit utility. Most of the time, you will find a link to ADSI Edit in the administrative tools folder. If, for some reason, the shortcut is unavailable, you can always create a custom Microsoft Management Console (MMC) that contains it. To open a blank MMC this, click Start, type mmc in the Search box, and then press ENTER. Or click Start, click Run, type mmc, and click OK.
After you have an empty MMC, you can click Add/Remove Snap-in on the File menu, select ADSI Edit on the Available Snap-ins list, click Add, and then click OK. After you have ADSI Edit added to your MMC, right-click ADSI Edit, click Connect-To, and choose the Default Naming Context. Use the arrows on the left to navigate to the OU you are interested in querying. After you have found the OU you want to query, right-click it and then click Properties in the shortcut menu. The dialog box appears that is shown in the following image.
When you have access to all the attributes associated with the organizationalunit object in AD, you can double-click the distinguishedName attribute to get a better look at the value. The dialog shown in the following image appears.
To avoid typing a long and convoluted distinguishedname (DN), I will often copy the DN value directly from the dialog box and paste it into the Windows PowerShell console by right-clicking. The completed command is seen here:
PS C:\> Get-ADUser -Filter * -SearchBase "ou=hsg_TestOU,DC=nwtraders,dc=com"DistinguishedName : CN=HSG_TestChild,OU=HSG_TestOU1,OU=HSG_TestOU,DC=NWTraders,DC=ComEnabled : FalseGivenName :Name : HSG_TestChildObjectClass : userObjectGUID : 4ffd4a5a-74f3-41ee-ae89-bbb8bd1bd915SamAccountName : HSG_TestChildSID : S-1-5-21-3746122405-834892460-3960030898-1161Surname :UserPrincipalName :DistinguishedName : CN=hsgUserGroupTest,OU=HSG_TestOU,DC=NWTraders,DC=ComEnabled : FalseGivenName :Name : hsgUserGroupTestObjectClass : userObjectGUID : b18e4543-36d5-48e3-b477-389a84ef2d1eSamAccountName : hsgUserGroupTestSID : S-1-5-21-3746122405-834892460-3960030898-1193Surname :UserPrincipalName :
By default, the Get-ADUser cmdlet will automatically recurse through all of the child OUs. If you do not want to recurse through the child OUs because you are only interested in users from a specific OU, you need to modify the –searchScope parameter. There are actually three values you can supply for the searchscope parameter: base, onelevel, and subtree. By default, the Get-ADUser cmdlet will use the searchscope of subtree (which means to recurse); therefore, there is no need to supply that value. A base searchscope means the query will not enter the searchBase, and in this example does not return any users. A searchScope of onelevel means the search will go into the SearchBase OU, and in this example, one user is returned:
PS C:\> Get-ADUser -Filter * -SearchBase "ou=hsg_TestOU,DC=nwtraders,dc=com" -searchscope onelevelDistinguishedName : CN=hsgUserGroupTest,OU=HSG_TestOU,DC=NWTraders,DC=ComEnabled : FalseGivenName :Name : hsgUserGroupTestObjectClass : userObjectGUID : b18e4543-36d5-48e3-b477-389a84ef2d1eSamAccountName : hsgUserGroupTestSID : S-1-5-21-3746122405-834892460-3960030898-1193Surname :UserPrincipalName :PS C:\>
The hierarchy we are searching, as viewed from Active Directory Users and Computers, is seen in the following image.
One of the really cool things you can do with the AD DS cmdlets is use the –LDAPFilter parameter. For example, with the Get-ADUser cmdlet, perhaps you would like to retrieve all of the users who have a password that does not expire. A Hey, Scripting Guy! post from several years ago included such a script. In the script, there was a rather complicated LDAP query that returned the users whose password does not expire. The applicable portion of the script is seen here:
objCommand.CommandText = _ "<LDAP://dc=fabrikam,dc=com>;" & _ "(&(objectCategory=User)(userAccountControl:1.2.840.113522.214.171.1243:=65536));" & _ "Name;Subtree"
An LDAP dialect query is made up of four portions. The first portion is the location of the LDAP query; the second is the LDAP filter; the third portion is the properties to return; and the last is the search scope. With the Get-ADUser cmdlet, we have parameters for each of those four parameters. We are therefore only interested in the LDAP filter query seen here:
When copying the LDAP filter query, do not include the trailing semicolon. That is an artifact of the previous query; each portion of an LDAP dialect query is separated by semicolons. The query goes directly into the parameter. There is no need to escape anything with extra quotation marks or anything else. The following command is a single-line command that will wrap when typed into the Windows PowerShell console. It accomplishes in one line the same thing the earlier Scripting Guy VBScript took 17 lines to do:
PS C:\> Get-ADUser -SearchBase "dc=nwtraders,dc=com" -searchscope subtree -ldapfilter "(&(objectCategory=User)(userAccountControl:1.2.840.1135126.96.36.1993:=65536))"DistinguishedName : CN=Guest,CN=Users,DC=NWTraders,DC=ComEnabled : FalseGivenName :Name : GuestObjectClass : userObjectGUID : 5b0b1ce3-808f-4976-b80e-356adc1dc7e6SamAccountName : GuestSID : S-1-5-21-3746122405-834892460-3960030898-501Surname :UserPrincipalName :DistinguishedName : CN=another User,OU=HSG_TestOU,DC=NWTraders,DC=ComEnabled : TrueGivenName : anotherName : another UserObjectClass : userObjectGUID : f8315b67-ee23-486d-9bfc-a5e118f0392aSamAccountName : anotherUserSID : S-1-5-21-3746122405-834892460-3960030898-1202Surname : UserUserPrincipalName : anotherUser@NWTraders.ComDistinguishedName : CN=AHappy Camper,OU=HSG_TestOU,DC=NWTraders,DC=ComEnabled : TrueGivenName : AHappyName : AHappy CamperObjectClass : userObjectGUID : 6852f2bc-37d5-46b5-b6da-4365c7009dfcSamAccountName : AHappyCamperSID : S-1-5-21-3746122405-834892460-3960030898-1203Surname : CamperUserPrincipalName : AHappyCamper@NWTraders.ComPS C:\>
VN, that is all there is to using the AD DS cmdlets to search User objects. Searching Active Directory Week will continue tomorrow.
If you want to know exactly what we will be discussing tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at firstname.lastname@example.org or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson and Craig Liebendorfer, Scripting Guys
Is it possible to set the searchbase to multiple OUs? I'm trying to filter users from 3 specific OUs only.