Introduction

This article describes a PowerShell script that can be used to retrieve information from your Active Directory. The script prompts for three things. First, it prompts for the "base" of the query. Second, it prompts for an LDAP syntax filter. Finally, the script prompts for the Active Directory attributes to be retrieved. Results can be output in list or csv (comma delimited) format and redirected to a text file. Or you can request just the count of the number of objects retrieved by the query. The script is in the TechNet Gallery here: Generic Search of Active Directory

↑ Return to Top


Purpose of the Script

The primary purpose of the script is to query Active Directory. The script can be used to test various LDAP syntax filters in your AD. The script outputs the actual values of the attributes (where feasible). But in many cases it also displays a "friendly" format of the value. For example, the value of the pwdLastSet attribute is a large integer. The script converts this number into a datetime in the time zone of the local client.

↑ Return to Top


Why Use LDAP Syntax Filters to Query AD

Active Directory is an LDAP compliant database. All queries of AD must be converted into LDAP syntax. For example, suppose you use a PowerShell AD cmdlet such as Get-ADUser to query AD. These cmdlets support an extended form of the PowerShell Expression Language. You use the -Filter parameter to specify a PowerShell syntax filter. But Active Directory does not support this syntax directly. The AD cmdlets convert the PowerShell filter into LDAP syntax before sending it to a domain controller. All of the AD cmdlets also support LDAP syntax filters directly, with the -LDAPFilter parameter.

LDAP syntax filters can be used in many situations. For example:

  • VBScript programs using ADSI
  • PowerShell scripts that use the DirectorySearcher object
  • PowerShell AD module cmdlets, like Get-ADUser, that support the -LDAPFilter parameter
  • The dsquery * command line utility
  • Joe Richards' free adfind command line tool
  • The Active Directory Users and Computers advanced custom filter feature
  • The ldp command line utility

↑ Return to Top


How to Use the Script

The script is designed to be run at a PowerShell prompt. The script displays results at the prompt, or you can redirect the output to a text file. The script accepts three optional parameters. If you specify -csv, the script outputs in a comma delimited format. Otherwise, the script outputs in a list format where each value is on a separate line. The csv format can be read into Excel and converted into a spreadsheet. The image below shows the result when you specify csv format. Note that all values that could conceivably include commas are quoted. The value of objectSID could never include a comma. The image shows what the user entered at each prompt, followed by what the script output. If the output had been redirected to a text file, only the output from the script would be included in the file. In this case, the lines output by the script are the heading line that specifies the attributes, plus one line of values (word wrapped).

If you specify -OneLevel, the query only considers objects that are direct children of the "base". Otherwise, the default is "SubTree", meaning that the query considers objects in the "base" and all child OU's and containers of the "base". If you specify -Count, the script simply outputs the number of objects retrieved by the query. In the image below we retrieve the number of groups in the domain.

↑ Return to Top


Specify the Base of the Query

When you run the script at a PowerShell prompt it first requests the "base" of the query. This is the distinguished name of the partition, organizational unit, or container where the query will start. If you enter nothing, the script will default to the current domain. The query will start at the base and include all nested OU's and containers within the base, unless you have specified the -OneLevel parameter.

If you enter an OU, container, or partition, you can skip the domain portion of the distinguished name. If the script does not find the string "dc=" in the base you provide, it will append the distinguished name of the current domain to the base.

Examples for the "base" of a query are listed below. These assume that your domain is "dc=mydomain,dc=com".

  • Default naming context: dc=mydomain,dc=com
      Enter: <nothing>
  • Configuration partition: cn=Configuration,dc=mydomain,dc=com
      Enter: cn=Configuration
  • Schema partition: cn=Schema,cn=Configuration,dc=mydomain,dc=com
      Enter: cn=Schema,cn=Configuration
  • Application partition: cn=MyApplication,dc=mydomain,dc=com
      Enter: cn=MyApplication
  • Any organizational unit: ou=Sales,ou=West,dc=mydomain,dc=com
      Enter: ou=Sales,ou=West
  • Any container: cn=Users,dc=mydomain,dc=com
      Enter: cn=Users
  • Other trusted domains: dc=mychild,dc=mydomain,dc=com
      Enter: dc=mychild,dc=mydomain,dc=com

↑ Return to Top


Target a Specific Domain Controller

Some AD attributes are not replicated to all domain controllers. Instead, for each object a different value for the attribute is saved on each DC. For example, the lastLogon, logonCount, badPasswordTime, and whenChanged attributes are not replicated. If you want to view the values of any of these on a particular DC, you must include the NetBIOS name of the DC in the base of the query. To target the domain controller MyDC, the base of the query should be similar to "MyDC/dc=mydomain,dc=com". The script will process the query on the specified DC. Note that you can also specify just "MyDC/" and the script will append the distinguished name of the domain to the base.

↑ Return to Top


Specify the LDAP Filter

After prompting for the "base" of the query, the script next prompts for the LDAP syntax filter to be used. LDAP syntax filters consist of clauses in parentheses combined by operators. Each clause consists of the lDAPDisplayName of an attribute, an operator, and a value. For example, to query for all user objects the filter would be: "(&(objectCategory=person)(objectClass=user))". For complete documentation of LDAP syntax filters, see Active Directory: LDAP Syntax Filters.

↑ Return to Top


Specify the Attributes to Retrieve

Finally, the script prompts for a comma delimited list of attribute values to retrieve for each AD object that meets the conditions of the filter in the base. Use the lDAPDisplayName of the attributes. The script will always retrieve the distinguished names of all objects retrieved, so do not include that attribute.

↑ Return to Top


Examples

Attributes that do not need Conversion

Most attributes can be displayed directly and do not require any conversion into a "friendly" format. These include attributes with datatypes String, DN (distinguished names), Boolean, Generalized Time, and 32-bit numbers that are not flag attributes. The image below demonstrates a query to retrieve several such attributes for a user.

Note above that the domain controller "Wisconsin" has been specified in the base of the query, because whenChanged and uSNChanged are not replicated. Also, the filter is (anr=Kenneth Mueller). This uses Ambiguous Name Resolution (ANR) to find all objects where the first name is "Kenneth" and the last name is "Mueller". This feature is explained in detail in references in the "Other Resources" and "See Also" sections below.

↑ Return to Top


Byte Arrays

Some Active Directory attributes are stored as byte arrays (also called OctetStrings). We could display the values as a series of decimal bytes, but that is not useful. The script converts many byte array attributes into a "friendly" format. The image below shows how all types of byte arrays described below are output.

↑ Return to Top


Logon Hours

The 21 bytes of the logonHours attribute are converted into bits by the script function OctetToHours. This function displays the bits in 7 rows of 24 bits each, representing the 24 hours in each day of the week. A "0" means the user is not allowed to logon during that hour. A "1" means the user is allowed to logon. The 168 bits are adjusted for the time zone of the local client. In the image above the logonHours attribute is displayed for a user.

↑ Return to Top


SID and GUID Attributes

If the byte array consists of 16 bytes, it assumed to be a GUID value. This could be objectGUID, netbootGUID, siteGUID, or any of several other attributes. The script function OctetToGUID is used convert the bytes into the friendly format, including the curly braces and dashes.

The table below documents how SID attributes are recognized. If the number of bytes in the array and the values of the first two bytes match an entry in the table, then the attribute is a SID value.

Number of Bytes First Byte Second Byte
12 1 1
16 1 2
24 1 4
28 1 5

The value of SID attributes is converted into the well known "friendly" form.

↑ Return to Top


Flag Attributes

Flag attributes are integer values that indicate settings or options. In some cases, each value has a different meaning. For most, however, each bit of the integer value indicates a different setting. Each bit of the value must be tested with a bit mask to check if the setting is on or off. Separate functions are used in the script to evaluate the following attributes.

Attribute Script Function
userAccountControl UAC
msDS-User-Account-Control-Computed UAC
groupType GroupType
searchFlags SearchFlags
systemFlags SystemFlags
sAMAccountType SAMType
instanceType InstanceType

The following image shows how the script displays the userAccountControl, sAMAccountType, and instanceType attributes of users.

The following image shows how the script displays the searchFlags and systemFlags attributes of attribute objects in the Schema container.

And the image below shows how the groupType attribute is displayed for group objects.

↑ Return to Top


Large Integer Attributes

Many attributes in Active Directory are Large Integers. The syntax is sometimes referred to as Integer8, meaning it has 8 bytes. This is a 64-bit value. Such large integers can represent several types of values.

↑ Return to Top


Maximum Values

If the value of the attribute exceeds the maximum positive number allowed in PowerShell, or is less than the minimum negative number allowed, then the value displays as "<never>". The maximum positive ticks is 3,155,378,975,999,999,999, which corresponds to the datetime Friday, December 31, 9999 11:59:59 PM. The minimum negative ticks is -9,223,372,036,854,775,808, which corresponds to a timespan greater than 29,227 years.

↑ Return to Top


Datetime Values

If the value of the Large Integer attribute exceeds 120,000,000,000,000,000 ticks, but is less than the maximum value allowed, then it is assumed to represent a datetime. Attributes such as lastLogonTimeStamp, accountExpires, and pwdLastSet are Large Integer values that represent dates. The dates are represented as the number of 100-nanosecond intervals (ticks) since 12:00 AM January 1, 1601. In addition, the dates are in UTC (after the French acronym for Coordinated Universal Time), which used to be referred to as GMT. The script converts these values into a datetime value in the time zone of the local client. 120,000,000,000,000,000 ticks corresponds to April 07, 1981, 9:20 PM UTC. The image below shows how the lastLogon and pwdLastSet attributes are displayed. The uSNChanged, logonCount, and maxStorate attributes are also Large Integer, but their values are displayed as large numbers, as described later.

Note that the domain controller "Wisconsin" is specified because lastLogon, uSNChanged, and logonCount are not replicated.

↑ Return to Top


Timespan Values

If the value of the LargeInteger attribute is negative, but greater than the minimum negative value allowed, then the value is assumed to represent a timespan. Large Integer attributes represent time spans in ticks. A tick is a 100-nanosecond interval, or 1 x 10^7 (0.0000001) second. Examples include maxPwdAge, minPwdAge, lockoutDuration, and lockoutThreshold. The image below shows how these values are displayed.

↑ Return to Top


Other Large Values

Other Large Integer attributes are simply large numbers. The script assumes that any LargeInteger value between 0 and 120,000,000,000,000,000 is simply a large number. For example, the maxStorage attribute is the number of bytes of disk space a user is allocated. This value is displayed as a number, with culture-specific separators (such as commas or periods) between the thousands to improve readability, as shown in the "Datetime Values" section above.

↑ Return to Top


Multi-Valued Attributes

In the default list output mode, multi-valued attributes are displayed with each value on a separate line. In CSV mode, the values are concatenated into one string with the individual values delimited by semicolons, the ";" character. The image below shows the directReports attribute, a multi-valued DN attribute, for a user.

↑ Return to Top


Limitations of the Script

A few attributes, like unicodePwd, ntPwdHistory, and userPassword, cannot be evaluated by the script. Others, like userParameters, can be displayed, but are difficult to interpret. ntSecurityDescriptor displays as an array of decimal bytes.

Multi-valued operational attributes (also called constructed attributes) raise errors. This includes the attributes tokenGroups, tokenGroupsGlobalAndUniversal, tokenGroupsNoGCAcceptable, and the objectClasses attribute of attributeSchema objects in the Schema container. These attributes cannot be used in a filter or attribute list. Single valued operational attributes, like canonicalName, createTimeStamp, and modifyTimeStamp, can be specified in an attribute list. Their values can be displayed. But if they are used in filters an error will be raised.

↑ Return to Top


Errors

The following errors are trapped and an error message is displayed.

  • Invalid Base for the Query
  • Invalid LDAP Syntax Filter
  • Attempt to retrieve the value of a multi-valued operational attribute
  • LDAP Syntax Filter that includes an operational attribute
  • Specified Domain Controller cannot be contacted
  • Domain cannot be contacted

If a non-existent attribute is requested, the value of the attribute is simply shown as "<no value>". The script cannot tell the difference between an attribute that has no value assigned and a misspelled attribute name.

↑ Return to Top


See Also

↑ Return to Top


Other Resources

↑ Return to Top