Summary: Learn how to use Windows PowerShell to search Active Directory for GIDs.
So what is a GID? To the UNIX (and Linux) operating systems, group identifiers (GIDs) and user identifiers (UIDs) are what security identifiers (SIDs) are to the Windows operating system. Each user in the UNIX system has a UID and a primary GID to identify the primary group he belongs to. Users can also belong to multiple secondary groups to inherit security settings.
In a pure Windows world, these fields are not used, even though the uidNumber and gidNumber attributes are available for compliance to LDAP protocol. The following image shows that the gidNumber attribute is not set.
In a mixed Windows and UNIX environment, however, the uidNumber and gidNumber attributes provide a mechanism to map UNIX users and groups to Windows names. For example, after Services for NFS are installed, Active Directory lookup services utilize these attributes for identity management.
So how can we query groups that have GIDs assigned? As always, there are multiple ways to accomplish this in the Windows operating system. Windows PowerShell has made searching through LDAP much easier by implementing the [adsisearcher] type accelerator, which instantiates a System.DirectoryServices.DirectorySearcher .NET object from a search filter.
As a start, we can use "(objectCategory=group)" as the filter string to enumerate Active Directory groups as shown here.
$searcher=[adsisearcher]"(objectCategory=group)"
$result = $searcher.FindAll()
$result | Select-Object
This script and its associated output are shown here.
This simple search filter uses the "(<attribute><operator><value>)" format that is mentioned in the Services for NFS Step-by-Step Guide. Now to find all groups that have the gidNumber attribute set, we need another filter that checks this attribute, and "(gidNumber=*)" will do the trick. By combining the two filters with the "(<operator><filter1><filter2>)" format, we get the following:
"(&(objectCategory=group)(gidNumber=*))"
This script and its associated output follow.
$searcher=[adsisearcher]"(&(objectCategory=group)(gidNumber=*))"
$result = $searcher.FindAll()
$result | Select-Object @{Name="DN";Expression={$_.properties.distinguishedname}},@{Name="gid";Expression={$_.properties.gidnumber }}
The previous script would have worked just fine if objectClass was used in place of objectCategory. An interesting comparison between the two is documented in this Windows Dev Center topic.
One can easily append an Export-Csv cmdlet at the end of the previous command to save the result as a .csv file for reading or processing in the future.
With the Active Directory module that’s available for Windows 7 or Windows 2008 R2, there are multiple ways to achieve the same goal by using a single cmdlet. Two options are shown here.
#Use LDAP search filter syntax
Get-ADObject -LDAPFilter "(&(objectCategory=group)(gidNumber=*))" -Properties gidNumber | Select @{Name="DN";Expression={$_.DistinguishedName}},@{Name="gid";Expression={$_.gidNumber}}
#Or, use AD filter syntax
Get-ADGroup -filter 'gidNumber -like "*"' -Properties gidNumber | Select @{Name="DN";Expression={$_.DistinguishedName}},@{Name="gid";Expression={$_.gidNumber}}
This command output is shown here.
Note: The Properties parameter is needed in both commands to include the gidNumber property (which is not exported by default) in the resultant objects.
We can actually query Active Directory objects through WMI. The DS_GROUP class under the "root\directory\ldap" namespace instantiates all group objects from Active Directory. So with a WQL filter, we can retrieve a list of groups with the gidNumber attribute defined as shown here.
Get-WmiObject -namespace root\directory\ldap -class ds_group -filter 'DS_gidNumber IS NOT NULL' | Select-Object @{Name="DN";Expression={$_.DS_distinguishedName}},@{Name="gid";Expression={$_.DS_gidnumber }}
Notice how WMI uses the "DS_" prefix for both the distinguishedName and gidNumber attributes.
Now with all these options available, which route has the best performance? This question will be left to the readers to test in a real environment. As a hint, Measure-Command is the cmdlet to assess the execution time of script blocks.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy