Learn about Windows PowerShell
Hey, Scripting Guy! I often need to search Active Directory Domain Services (AD DS) to find information about various computers. I may need to identify all of the computers in a particular organizational unit or all the computers who happen to reside in a particular office. Whatever the reason, using Active X Data Objects (ADO) like I did in the old VBScript days is a major pain. When I learned how to use the Directory Searcher Object, it was a little better—in fact quite a bit better, but I am curious if the new Active Directory cmdlets have anything to make it easier to retrieve users?
-- NN
Hello NN,
Microsoft Scripting Guy Ed Wilson here. Today is a special day because I finally received my copies of the Windows PowerShell 2.0 Best Practices book that I spent 18 months of my life writing. For me, the big thing about writing books is the enjoyment I receive when people tell me they have read my book and it helped them. On Facebook this morning, I had two notes from people who stated they had enjoyed my books. Speaking of Facebook, send me a friend request. I enjoy the interaction. In addition to the Best Practices book, the morning mail also showed up with a tin of Genmaicha tea. This delicate green tea is best brewed for 3–5 minutes, and tastes great with a cinnamon stick in it. The beautiful blue sky, cool temperature, a special pot of green tea, and an excellent Windows PowerShell book make a wonderful way to spend the day. But I also need to check the e-mail sent to scripter@microsoft.com.
It is a bit damp outside today, but the sky is clear. It reminds me of Lima, Peru, when I was down there teaching a VBScript class a few years ago. My friend Omar took me around, and I was able to take some great pictures such as the following one.
NN, I agree with you completely. Using the Directory Searcher .NET Framework classes is easier to use than the old fashioned ADO or even new fangled ADO.NET scripts. In the end I feel it is a best practice to use what you are comfortable with, and to use what will be easiest for you to modify, to troubleshoot, and to maintain.
An example of using ADO to query AD DS is the QueryAD.Ps1 script I wrote several years ago.
QueryAD.ps1
param( $ou, $domain, $query, [switch]$help )function funHelp(){ $helpText=@" DESCRIPTION: NAME: QueryAD.ps1 Queries Active Directory on a local or remote machine. PARAMETERS: -ou the organizational unit to query -domain the domain to query -query the query to use. Queries for objects such as: < User, Group, Computer, OrganizationalUnit, printqueue, grouppolicycontainer, ipsecpolicy, pkicertificatetemplate, sitelink, subnet, site > -help prints help file SYNTAX: QueryAD.ps1 Generates message of missing parameter and displays help QueryAD.ps1 -domain "nwtraders.com" -ou "mytestou" -query computer Displays a listing of every computer object in the mytestou organizational unit of the nwtraders.com domain QueryAD.ps1 -help Prints the help topic for the script"@ $helpText exit} #end funHelpFunction funQueryAD(){ $domain = $domain -replace("^","dc=") #replace first character $domain = $domain -replace("\.",",dc=") #replace the period if(!$ou) { if(!$query) { $strQuery = "<LDAP://$domain>;;name;subtree" } ELSE { $strQuery = "<LDAP://$domain>;(objectcategory=$query);name;subtree" } } ELSE { $ou = $ou -replace("^","ou=") #replace first character $ou = $ou -replace("\,",",ou=") #replace a comma if(!$query) { $strQuery = "<LDAP://$ou,$domain>;;name;subtree" } ELSE { $strQuery = "<LDAP://$ou,$domain>;(objectcategory=$query);name;subtree" } } $objConnection = New-Object -comObject "ADODB.Connection" $objCommand = New-Object -comObject "ADODB.Command" $objConnection.Open("Provider=ADsDSOObject;") $objCommand.ActiveConnection = $objConnection $objCommand.CommandText = $strQuery $objRecordSet = $objCommand.Execute() Do { $objRecordSet.Fields.item("name") |Select-Object name,Value $objRecordSet.MoveNext() } Until ($objRecordSet.eof) $objConnection.Close()} #end funQueryADif($help) { "calling help ..." ; funhelp }if(!$domain) { "missing the domain name" ; funhelp }if(!$domain -or !$ou -or !$query) { "a parameter is required" ; funhelp }funqueryAD
By using the Directory Searcher object, you can reduce significantly the amount of work that is involved in querying Active Directory. The SearchAllComputersInDomain.ps1 script was used for a Hey, Scripting Guy! Blog post in March 2009 when we spent a week talking about searching Active Directory.
SearchAllComputersInDomain.ps1
$Filter = "ObjectCategory=computer"$Searcher = New-Object System.DirectoryServices.DirectorySearcher($Filter)$Searcher.Findall() | Foreach-Object ` -Begin { "Results of $Filter query: " } ` -Process { $_.properties ; "`r"} ` -End { [string]$Searcher.FindAll().Count + " $Filter results were found"
In Windows PowerShell 2.0, you can shorten the script a bit by using the [adsisearcher] type accelerator. The [adsisearcher] type accelerator saves you the trouble of creating an instance of the DirectoryServices.DirectorySearcher .NET Framework class. This is seen here.
SearchComputersUseAdsiSearcher.ps1
$Filter = "ObjectCategory=computer"$Searcher = [adsiSearcher]($Filter)$Searcher.Findall() | Foreach-Object ` -Begin { "Results of $Filter query: " } ` -Process { $_.properties ; "`r"} ` -End { [string]$Searcher.FindAll().Count + " $Filter results were found" }
If you have at least one Windows Server 2008 R2 domain controller and the Remote Server Administration Tools for Windows 7 (RSAT) tools installed, you can use the Get-ADComputer cmdlet to retrieve information about a computer account in AD DS. The –identity parameter will accept the samAccountName, the DistinguishedName, the security identifier (SID), or the object GUID:
PS C:\> Get-ADComputer -Identity hypervDistinguishedName : CN=HYPERV,OU=Domain Controllers,DC=NWTraders,DC=ComDNSHostName : HyperV.NWTraders.ComEnabled : TrueName : HYPERVObjectClass : computerObjectGUID : 2a76b1bd-80cb-4546-a8f2-ea46d474e06aSamAccountName : HYPERV$SID : S-1-5-21-3746122405-834892460-3960030898-1000UserPrincipalName :PS C:\> Get-ADComputer -Identity 'CN=HYPERV,OU=Domain Controllers,DC=NWTraders,DC=Com'DistinguishedName : CN=HYPERV,OU=Domain Controllers,DC=NWTraders,DC=ComDNSHostName : HyperV.NWTraders.ComEnabled : TrueName : HYPERVObjectClass : computerObjectGUID : 2a76b1bd-80cb-4546-a8f2-ea46d474e06aSamAccountName : HYPERV$SID : S-1-5-21-3746122405-834892460-3960030898-1000UserPrincipalName :PS C:\> Get-ADComputer -Identity S-1-5-21-3746122405-834892460-3960030898-1000DistinguishedName : CN=HYPERV,OU=Domain Controllers,DC=NWTraders,DC=ComDNSHostName : HyperV.NWTraders.ComEnabled : TrueName : HYPERVObjectClass : computerObjectGUID : 2a76b1bd-80cb-4546-a8f2-ea46d474e06aSamAccountName : HYPERV$SID : S-1-5-21-3746122405-834892460-3960030898-1000UserPrincipalName :PS C:\> Get-ADComputer -Identity 2a76b1bd-80cb-4546-a8f2-ea46d474e06aDistinguishedName : CN=HYPERV,OU=Domain Controllers,DC=NWTraders,DC=ComDNSHostName : HyperV.NWTraders.ComEnabled : TrueName : HYPERVObjectClass : computerObjectGUID : 2a76b1bd-80cb-4546-a8f2-ea46d474e06aSamAccountName : HYPERV$SID : S-1-5-21-3746122405-834892460-3960030898-1000UserPrincipalName :
Because the identity parameter is the default parameter for Get-ADComputer, you can leave it out and just supply the name of the computer you wish to query. This is seen here:
PS C:\> Get-ADComputer win7-pcDistinguishedName : CN=WIN7-PC,CN=Computers,DC=NWTraders,DC=ComDNSHostName : WIN7-PC.NWTraders.ComEnabled : TrueName : WIN7-PCObjectClass : computerObjectGUID : 3e802bb2-702a-4039-90dd-d7b624c97440SamAccountName : WIN7-PC$SID : S-1-5-21-3746122405-834892460-3960030898-1103UserPrincipalName :
One strange thing is the use of the property parameter from the Get-ADComputer cmdlet. You would expect that piping the computer object that is returned by the cmdlet to the Format-List cmdlet would provide you the opportunity to work with computer object properties. When working with other objects, you can use the wildcard character “*” and the force switch with the Format-List cmdlet and retrieve all properties and values of an object. As seen here, when working with the Get-ADComputer cmdlet, that is not the case:
PS C:\> Get-ADComputer win7-pc | format-list * -ForceDistinguishedName : CN=WIN7-PC,CN=Computers,DC=NWTraders,DC=ComDNSHostName : WIN7-PC.NWTraders.ComEnabled : TrueName : WIN7-PCObjectClass : computerObjectGUID : 3e802bb2-702a-4039-90dd-d7b624c97440SamAccountName : WIN7-PC$SID : S-1-5-21-3746122405-834892460-3960030898-1103UserPrincipalName :PropertyNames : {DistinguishedName, DNSHostName, Enabled, Name...}PropertyCount : 9PS C:\>
To obtain all of the information available from a computer object, you must use the property parameter from the Get-ADComputer cmdlet, as seen here:
PS C:\> Get-ADComputer -Identity win7-pc -Properties *AccountExpirationDate :accountExpires : 9223372036854775807AccountLockoutTime :AccountNotDelegated : FalseAllowReversiblePasswordEncryption : FalseBadLogonCount : 0badPasswordTime : 0badPwdCount : 0CannotChangePassword : FalseCanonicalName : NWTraders.Com/Computers/WIN7-PCCertificates : {}CN : WIN7-PCcodePage : 0countryCode : 0Created : 9/8/2009 9:48:38 PMcreateTimeStamp : 9/8/2009 9:48:38 PMDeleted :Description :DisplayName :DistinguishedName : CN=WIN7-PC,CN=Computers,DC=NWTraders,DC=ComDNSHostName : WIN7-PC.NWTraders.ComDoesNotRequirePreAuth : FalsedSCorePropagationData : {12/3/2009 6:32:30 PM, 12/3/2009 6:32:29 PM, 12/2/2009 7:18:22 AM, 12/2/2009 7:18: 22 AM...}Enabled : TrueHomedirRequired : FalseHomePage :instanceType : 4IPv4Address : 192.168.1.110IPv6Address :isCriticalSystemObject : FalseisDeleted :LastBadPasswordAttempt :LastKnownParent :lastLogoff : 0lastLogon : 129084954052650603LastLogonDate : 1/15/2010 12:28:29 PMlastLogonTimestamp : 129080501096745399localPolicyFlags : 0Location :LockedOut : FalselogonCount : 255ManagedBy :MemberOf : {}MNSLogonAccount : FalseModified : 1/18/2010 8:41:55 AMmodifyTimeStamp : 1/18/2010 8:41:55 AMmsDS-SupportedEncryptionTypes : 28msDS-User-Account-Control-Computed : 0Name : WIN7-PCnTSecurityDescriptor : System.DirectoryServices.ActiveDirectorySecurityObjectCategory : CN=Computer,CN=Schema,CN=Configuration,DC=NWTraders,DC=ComObjectClass : computerObjectGUID : 3e802bb2-702a-4039-90dd-d7b624c97440objectSid : S-1-5-21-3746122405-834892460-3960030898-1103OperatingSystem : Windows 7 EnterpriseOperatingSystemHotfix :OperatingSystemServicePack :OperatingSystemVersion : 6.1 (7600)PasswordExpired : FalsePasswordLastSet : 1/18/2010 8:41:55 AMPasswordNeverExpires : FalsePasswordNotRequired : FalsePrimaryGroup : CN=Domain Computers,CN=Users,DC=NWTraders,DC=ComprimaryGroupID : 515ProtectedFromAccidentalDeletion : FalsepwdLastSet : 129082957152111233SamAccountName : WIN7-PC$sAMAccountType : 805306369sDRightsEffective : 15ServiceAccount : {}servicePrincipalName : {WSMAN/win7-PC, WSMAN/win7-PC.NWTraders.Com, TERMSRV/WIN7-PC, TERMSRV/win7-PC.NWTr aders.Com...}ServicePrincipalNames : {WSMAN/win7-PC, WSMAN/win7-PC.NWTraders.Com, TERMSRV/WIN7-PC, TERMSRV/win7-PC.NWTr aders.Com...}SID : S-1-5-21-3746122405-834892460-3960030898-1103SIDHistory : {}TrustedForDelegation : FalseTrustedToAuthForDelegation : FalseUseDESKeyOnly : FalseuserAccountControl : 4096userCertificate : {}UserPrincipalName :uSNChanged : 271739uSNCreated : 12748whenChanged : 1/18/2010 8:41:55 AMwhenCreated : 9/8/2009 9:48:38 PMPS C:\>
NN, that is all there is to using Windows PowerShell to search Active Directory. Searching Active Directory Week will continue tomorrow.
If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson and Craig Liebendorfer, Scripting Guys