There are several things to consider for this type of query:
- How many objects is this query likely to return?
- Do you want to expand on groups that are members of groups?
- Do you have to handle any "large" groups (greater 1500 members)?
You should always include "(ObjectCategory=person)" if your query will have to search a large database of users. There are several reasons you want to do this. ObjectCategory is an indexed attribute where objectClass is not, this will significantly increase your query speed on large AD databases. Also, using both the objectCategory and objectClass attributes will prevent "contact" objects from being returned in your query.
If your result set is going to return more than 1000 results, you need to be aware of performance issues. In the ADUC GUI there is a 2000 item limit that you can change via the "Options" dialogue, increasing can dramatically slow down your query. If you are going to use VBScript and enumerate over a GetObject result, this will also be VERY VERY slow, for large groups. For the Quest Powershell cmdlets you need to include the "-sizelimit" parameter to override the 1000 item limit:
get-qadgroupmember somegroup -sizelimit 0
If you are using code (VBScript, JScript, .Net) to create a connection object and add a LDAP query to it, you will need to set the ".pageSize" property on the connection object to get a paged result as the default is to not return a paged result, but to limit it to 1000 items. I usually set .pageSize to 1000 as that is the max.
Expanding nested groups is the tricky bit. The simplest way to get nested group info is to use the Quest Powershell cmdlets:
get-qadgroupmember somegroup -indirect -sizelimit 0
From a VBscript/JScript script you can use "GetObject" and enumerate over the members collection, test each member for "user or group" and then recurse into nested groups. This is slow and you shouldn't do it, except as an exercise in VBScript programming.
Ultimately you will probably want to learn to do it via a direct LDAP query. This is accomplished via the LDAP_MATCHING_RULE_IN_CHAIN operator. This operator can be difficult to use, and it can be VERY EXPENSIVE on the DC if you have a deep nesting structure for your groups. The bonus for this method is that for very large groups (over 1500 members by default) you will be able to do a query for users that are a member of the group (even indirectly), rather than retrieving the group and trying to read the member attribute (which has to be handled in a special method for "large" groups. i.e. You get a report of User objects, ratehr than a single group object where you are trying to read a large attrbiute array.
If you are having trouble with "large" groups you can also increase the limit that AD uses when restricting access to the .member attribute.