Back on Jan 21 I tipped my hand on a new capability in AdFind which for many people is extremely exciting based on the feedback in my inbox. To refresh your memory, that was the ability for AdFind to take in a list of Base DN’s to execute queries against. For short we will say piping AdFind into AdFind though you could, if for whatever reason gripped you, pipe dsquery into AdFind.
Well in a follow up conversation with my friend and co-Author Brian Desmond, he asked “How do I pipe DNs from AdFind into AdFind and then get counts for the number of users under each of those DN’s?” My response was… well you can’t. The whole counting mechanism is based on the number of objects AdFind returns period… But then I thought, I hacked in CSV when I didn’t think I could… then I hacked in the piping in multiple DNs when I didn’t think I could, let me give this a try before totally saying no… So voila, a new switch because you just know there aren’t enough switches in AdFind yet…. The new switch is -ic… where -c stands for count, -ic stands for intermediate count… That lets you do something like….
G:\>adfind -default -f ou=* -dsq | adfind -sc adobjcnt:user
AdFind V01.40.00cpp **BETA** Joe Richards (joe@joeware.net) February 2009
Using server: r2dc1.test.loc:3268
Directory: Windows Server 2003BaseDN: OU=CharTests,OU=TestOU,DC=test,DC=loc
1 intermediate objects returnedBaseDN: OU=createtest,OU=TestOU,DC=test,DC=loc
10 intermediate objects returnedBaseDN: OU=Deleted,OU=XXXTest,DC=test,DC=loc
0 intermediate objects returnedBaseDN: OU=XXXTest,DC=test,DC=loc
0 intermediate objects returnedBaseDN: OU=Domain Controllers,DC=test,DC=loc
0 intermediate objects returnedBaseDN: OU=Email,OU=My,DC=test,DC=loc
1 intermediate objects returnedBaseDN: OU=GPOTest,OU=TestOU,DC=test,DC=loc
0 intermediate objects returnedBaseDN: OU=Groups,OU=My,DC=test,DC=loc
0 intermediate objects returnedBaseDN: OU=Groups,OU=TestOU,DC=test,DC=loc
0 intermediate objects returnedBaseDN: OU=HideTest,OU=TestOU,DC=test,DC=loc
0 intermediate objects returnedBaseDN: OU=joeperm,OU=TestOU,DC=test,DC=loc
0 intermediate objects returnedBaseDN: OU=My,DC=test,DC=loc
0 intermediate objects returnedBaseDN: OU=oneleveldown,OU=createtest,OU=TestOU,DC=test,DC=loc
1 intermediate objects returnedBaseDN: OU=Outlook,OU=TestOU,DC=test,DC=loc
0 intermediate objects returnedBaseDN: OU=permtest,OU=TestOU,DC=test,DC=loc
2 intermediate objects returnedBaseDN: OU=PoSTest,DC=test,DC=loc
700001 intermediate objects returnedBaseDN: OU=Skip,OU=TestXXX,OU=XXXTest,DC=test,DC=loc
1 intermediate objects returnedBaseDN: OU=TestXXX,OU=XXXTest,DC=test,DC=loc
4 intermediate objects returnedBaseDN: OU=TestDisable,OU=XXXTest,DC=test,DC=loc
0 intermediate objects returnedBaseDN: OU=TestOU,DC=test,DC=loc
6 intermediate objects returnedBaseDN: OU=Users,OU=My,DC=test,DC=loc
2 intermediate objects returnedBaseDN: OU=Users,OU=TestOU,DC=test,DC=loc
10 intermediate objects returned700039 Objects returned
Anyone think that is pretty handy??? If so, you can thank Brian, he asked the right question at the right time… I modified the adobjcnt shortcut such that when it detects it is in multi-DN mode it will also insert the -ic switch as well as set the search scope to one-level. If you wanted counts of all of the users in each OU but you wanted the counts to roll up to the higher OU’s as well you would simply add the -s sub or -s subtree switch to your command.
But then I thought, while that is useful, it would be even more useful if I could somehow get that in a CSV format so I could use this more easily from scripts when trying to get a snapshot of an environment… I looked and there was just no way I could get it into the CSV code path. When you do CSV the whole counting section isn’t used and even if it were, it is outside of the location where the CSV code is and in order to try and get it in there would cause me to use some wholly unnatural global variables and other things that just made me go, no, I will not go there, that is too ugly, too inelegant… I know I do some bad things in code, but I don’t want to do THAT bad of things in code.
All hope is not lost however. I decided to add… yes… another switch. I know I know, another one truly isn’t needed but I wanted this functionality and if you don’t like it, just ignore the fact that it is there. This switch is not a very flexible switch, there are no modifiers for it. It is called -ictsv and it simply takes the -ic output and makes it into a TAB Delimited format output. This isn’t going to be tweaked to allow different delimiters or anything like that. It is a hack completely outside the normal CSV routines which have all that flexibility. I chose tab delimited because DNs have commas and it is unlikely (impossible? I don’t know, didn’t test) to see a tab in a DN and to be honest, I like tab delimited output. I usually use TABs for my delimiters for CSV output. Anyway that output looks like…
G:\>adfind -default -f ou=* -dsq | adfind -sc adobjcnt:user -ictsv
OU=CharTests,OU=TestOU,DC=test,DC=loc 1
OU=createtest,OU=TestOU,DC=test,DC=loc 10
OU=Deleted,OU=XXXTest,DC=test,DC=loc 0
OU=XXXTest,DC=test,DC=loc 0
OU=Domain Controllers,DC=test,DC=loc 0
OU=Email,OU=My,DC=test,DC=loc 1
OU=GPOTest,OU=TestOU,DC=test,DC=loc 0
OU=Groups,OU=My,DC=test,DC=loc 0
OU=Groups,OU=TestOU,DC=test,DC=loc 0
OU=HideTest,OU=TestOU,DC=test,DC=loc 0
OU=joeperm,OU=TestOU,DC=test,DC=loc 0
OU=My,DC=test,DC=loc 0
OU=oneleveldown,OU=createtest,OU=TestOU,DC=test,DC=loc 1
OU=Outlook,OU=TestOU,DC=test,DC=loc 0
OU=permtest,OU=TestOU,DC=test,DC=loc 2
OU=PoSTest,DC=test,DC=loc 700001
OU=Skip,OU=TestXXX,OU=XXXTest,DC=test,DC=loc 1
OU=TestXXX,OU=XXXTest,DC=test,DC=loc 4
OU=TestDisable,OU=XXXTest,DC=test,DC=loc 0
OU=TestOU,DC=test,DC=loc 7
OU=Users,OU=My,DC=test,DC=loc 2
OU=Users,OU=TestOU,DC=test,DC=loc 10
Pretty cool huh… Anyone think that is handy? If so… well you are welcome… But something still bothers me about that output… Anyone else bothered by it? It could be just me but I kind of like seeing things that normally have a hierarchical form to be displayed that way. I don’t mean in the white space, but instead, I mean I don’t want to see something like
OU=Outlook,OU=TestOU,DC=test,DC=loc 0
OU=permtest,OU=TestOU,DC=test,DC=loc 2
OU=TestOU,DC=test,DC=loc 7
I want to see the TestOU first and then its sub-OU’s after… I tried modifying my search to see if I could force AD to return the info in that order but quite frankly, AD truly isn’t hierarchical, it just appears that way. It is actually a flat database. The idea of hierarchy is imposed on it for LDAP purposes. So I thought, I really need to do something about this… This will drive me nuts. However, trying to retrieve all of the information and maintain it in memory so I can then sort it is ridiculous, might as well just write this thing in PowerShell or .NET… (count it…) if I do it in such a silly way… Then I thought, wait, I already have what I need in memory to sort it hierarchically after the DNs have been piped in so I added, yes thankyou, another switch called -stdinsort. Why did I do this with a switch? Because I didn’t want to assume someone would want it sorted like I would and I didn’t want to assume I would always want it sorted. Also I wanted to give myself the ability to sort it hierarchically as well as alphabetically both case sensitive and case insensitive, so it is a switch with a default sort order of hierarchical but you can add cialpha or csalpha to get the other types of sort… Now that output looks like
G:\>adfind -default -f ou=* -dsq | adfind -sc adobjcnt:user -ictsv -stdinsort
OU=Domain Controllers,DC=test,DC=loc 0
OU=My,DC=test,DC=loc 0
OU=Email,OU=My,DC=test,DC=loc 1
OU=Groups,OU=My,DC=test,DC=loc 0
OU=Users,OU=My,DC=test,DC=loc 2
OU=PoSTest,DC=test,DC=loc 700001
OU=TestOU,DC=test,DC=loc 7
OU=CharTests,OU=TestOU,DC=test,DC=loc 1
OU=createtest,OU=TestOU,DC=test,DC=loc 10
OU=oneleveldown,OU=createtest,OU=TestOU,DC=test,DC=loc 1
OU=GPOTest,OU=TestOU,DC=test,DC=loc 0
OU=Groups,OU=TestOU,DC=test,DC=loc 0
OU=HideTest,OU=TestOU,DC=test,DC=loc 0
OU=joeperm,OU=TestOU,DC=test,DC=loc 0
OU=Outlook,OU=TestOU,DC=test,DC=loc 0
OU=permtest,OU=TestOU,DC=test,DC=loc 2
OU=Users,OU=TestOU,DC=test,DC=loc 10
OU=XXXTest,DC=test,DC=loc 0
OU=Deleted,OU=XXXTest,DC=test,DC=loc 0
OU=TestDisable,OU=XXXTest,DC=test,DC=loc 0
OU=TestXXX,OU=XXXTest,DC=test,DC=loc 4
OU=Skip,OU=TestXXX,OU=XXXTest,DC=test,DC=loc 1
Much better… ;o)
This and more in the new version of AdFind… AdFind V01.40.00 is expected to emerge from the cocoon on Feb 13, 2009.
joe