{"id":55883,"date":"2008-04-01T00:33:00","date_gmt":"2008-04-01T00:33:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2008\/04\/01\/hey-scripting-guy-how-can-retrieve-a-list-of-all-my-computer-accounts-grouped-by-ou\/"},"modified":"2008-04-01T00:33:00","modified_gmt":"2008-04-01T00:33:00","slug":"hey-scripting-guy-how-can-retrieve-a-list-of-all-my-computer-accounts-grouped-by-ou","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/hey-scripting-guy-how-can-retrieve-a-list-of-all-my-computer-accounts-grouped-by-ou\/","title":{"rendered":"Hey, Scripting Guy! How Can Retrieve a List of All My Computer Accounts, Grouped By OU?"},"content":{"rendered":"<p><img decoding=\"async\" class=\"nearGraphic\" title=\"Hey, Scripting Guy! Question\" height=\"34\" alt=\"Hey, Scripting Guy! Question\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" width=\"34\" align=\"left\" border=\"0\" \/> <\/p>\n<p>Hey, Scripting Guy! How can I get a list of all the computers in my domain, grouping those computers by the OU in which the computer account resides?<\/p>\n<p>&#8212; TA<\/p>\n<p><img decoding=\"async\" height=\"5\" alt=\"Spacer\" src=\"https:\/\/devblogs.microsoft.com\/scripting\/wp-content\/uploads\/sites\/29\/2019\/05\/spacer.gif\" width=\"5\" border=\"0\" \/><img decoding=\"async\" class=\"nearGraphic\" title=\"Hey, Scripting Guy! Answer\" height=\"34\" alt=\"Hey, Scripting Guy! Answer\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" width=\"34\" align=\"left\" border=\"0\" \/><a href=\"http:\/\/go.microsoft.com\/fwlink\/?linkid=68779&amp;clcid=0x409\"><img decoding=\"async\" class=\"farGraphic\" title=\"Script Center\" height=\"288\" alt=\"Script Center\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/ad.jpg\" width=\"120\" align=\"right\" border=\"0\" \/><\/a> <\/p>\n<p>Hey, TA. Well, today is Monday, and the Scripting Guy who writes this column is actually <i>glad<\/i> it\u2019s Monday. Not that he\u2019s all that excited about coming back to work, mind you; it\u2019s just that last week was such an absolutely <i>miserable<\/i> week that he\u2019s been waiting for a chance to start all over again and see if this week goes any better.<\/p>\n<p>So how bad <i>was<\/i> it last week? Let\u2019s put it this way: on Friday, the day that kicked off the second week of Spring, it snowed all day \u2013 hard. Some of that snow even stuck, which might not be all that surprising considering the fact that the temperature at the time was a whole 34 degrees Fahrenheit. The truth is, unless you live at the North Pole or the top of Mt. Everest you don\u2019t expect to see blizzard-like conditions on March 28<sup>th<\/sup>. Nor do you expect the temperature at 2:00 PM to be 34 degrees Fahrenheit.<\/p>\n<table class=\"dataTable\" id=\"END\" cellSpacing=\"0\" cellPadding=\"0\">\n<thead><\/thead>\n<tbody>\n<tr class=\"record\" vAlign=\"top\">\n<td class=\"\">\n<p class=\"lastInCell\"><b>Note<\/b>. For the record, the average high in Seattle on March 28<sup>th<\/sup> is 56 degrees Fahrenheit. In 1994 the thermometer peaked at \u2013 sigh \u2013 70 degrees on March 28<sup>th<\/sup>.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div class=\"dataTableBottomMargin\"><\/div>\n<p>So was Friday the worst day in Seattle history? Well, with the possible exception of the day the Mariners traded Randy Johnson \u2013 who went on to win the next four Cy Young Awards as the best pitcher in the National League \u2013 yes, definitely. Granted, back in February, 1916 21.5 inches of snow fell on Seattle in a single 24-hour period. However, a) that <i>was<\/i> in early February, when you might expect a little snow; and, b) people were a lot tougher back in 1916 than they are today. Would we modern-day Seattleites survive if 21.5 inches of snow fell in a 24-hour period? Are you kidding? Half of us would probably die if 21.5 inches of snow fell during a 24-<i>year<\/i> period!<\/p>\n<p>Anyway, this is a new day and a new week, and hopefully things will go a little better this week than they did last week. With that in mind, let\u2019s see what we can do about writing a script that can retrieve a list of all the computers in a domain, grouping those computers by organizational unit.<\/p>\n<p>Interestingly enough, this is a trickier problem than you might expect it to be. At first glance you might think, \u201cWell, that should be easy enough: I just get back a list of all the computers and the OUs they belong to, then sort that list by OU.\u201d That\u2019s actually a pretty good idea, except for one thing: there is no Active Directory property that tells you which OU an object resides in. (No really; take a peek at <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/oct04\/hey1021.mspx\"><b>this column<\/b><\/a> for information on the roundabout method you need to use to determine the OU an object belongs to.) <\/p>\n<p>But that\u2019s OK. The logical way to attack this problem is to retrieve a list of all the computers and then determine which OU they belong to. In this case, however, the logical way doesn\u2019t do us any good. Therefore, we need to do things the illogical (i.e., the Scripting Guys) way; we\u2019ll get back a list of all the OUs, and then determine which computer accounts can be found in each OU.<\/p>\n<p>In other words, we\u2019re going to use the following script:<\/p>\n<pre class=\"codeSample\">On Error Resume Next\n\nConst ADS_SCOPE_SUBTREE = 2\n\nSet objConnection = CreateObject(\"ADODB.Connection\")\nSet objCommand =   CreateObject(\"ADODB.Command\")\nobjConnection.Provider = \"ADsDSOObject\"\nobjConnection.Open \"Active Directory Provider\"\nSet objCommand.ActiveConnection = objConnection\n\nobjCommand.Properties(\"Page Size\") = 1000\nobjCommand.Properties(\"Searchscope\") = ADS_SCOPE_SUBTREE \n\nobjCommand.CommandText = _\n    \"SELECT ADsPath FROM 'LDAP:\/\/dc=fabrikam,dc=com' WHERE \" &amp; _\n        \"objectCategory='organizationalUnit'\"  \n\nSet objRecordSet = objCommand.Execute\n\nobjRecordSet.MoveFirst\n\nDo Until objRecordSet.EOF\n    Set objOU = GetObject(objRecordSet.Fields(\"ADsPath\").Value)\n    Wscript.Echo objOU.distinguishedName\n\n    objOU.Filter = Array(\"Computer\")\n    \n    For Each objItem in objOU\n        Wscript.Echo \"  \" &amp; objItem.CN\n    Next\n\n    Wscript.Echo\n    Wscript.Echo\n    objRecordSet.MoveNext\nLoop\n<\/pre>\n<p>As you can see, the first half of this script involves a search of Active Directory. Per our usual policy, we won\u2019t discuss Active Directory searching in any great detail today; you can find more information about that in our two-part Tales From the Script series <i>Dude, Where\u2019s My Printer?<\/i> (You\u2019ll find Part 1 of that series <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/tales\/sg0405.mspx\"><b>right here<\/b><\/a>, and Part 2 of the series <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/tales\/sg0505.mspx\"><b>over here<\/b><\/a>.) We will, however, take just a second to look at the query required to conduct our search:<\/p>\n<pre class=\"codeSample\">objCommand.CommandText = _\n    \"SELECT ADsPath FROM 'LDAP:\/\/dc=fabrikam,dc=com' WHERE \" &amp; _\n        \"objectCategory='organizationalUnit'\"\n<\/pre>\n<p>Actually there\u2019s nothing especially fancy about this query; we\u2019re simply asking the script to search the fabrikam.com domain and return the <b>ADsPath<\/b> attribute for each object that has an <b>objectCategory<\/b> equal to <i>organizationalUnit<\/i>. And yes, you\u2019re way ahead of us: anything with an objectCategory equal to <i>organizationalUnit<\/i> is going to be, well, an organizational unit.<\/p>\n<p>After we execute the query we\u2019ll get back a recordset consisting of all the OUs in that domain; what we have to do now is try to figure out which computers reside in each of those OUs. To do that, we set up a Do Until loop that runs until we\u2019ve looped through each and every OU. (Or, more technically, until the recordset\u2019s <b>EOF<\/b> \u2013 end-of-file \u2013 property is True.)<\/p>\n<p>In order to retrieve a list of all the computers that belong to a given OU we need to first bind to that OU; that\u2019s something we can do by using the ADsPath attribute, the very same attribute we requested when configuring our search query:<\/p>\n<pre class=\"codeSample\">Set objOU = GetObject(objRecordSet.Fields(\"ADsPath\").Value)\n<\/pre>\n<p>After we make the connection we echo back the value of the OU\u2019s <b>distinguishedName<\/b> attribute; that\u2019s going to be something similar to this:<\/p>\n<pre class=\"codeSample\">OU=Administration,DC=fabrikam,DC=com\n<\/pre>\n<p>By default connecting to an OU brings back a collection of all the objects contained in that OU; that means we can echo back a list of those objects simply by setting up a For Each loop similar to this one:<\/p>\n<pre class=\"codeSample\">For Each objItem in objOU\n    Wscript.Echo \"  \" &amp; objItem.CN\nNext\n<\/pre>\n<p>However, we don\u2019t want to do that just yet. Why not? Because that would list <i>all<\/i> the objects in the OU, and we aren\u2019t interested in all the objects; we\u2019re interested in only the computer accounts. Therefore, before we do anything else we set a <b>Filter<\/b> that restricts the returned data to computer objects: <\/p>\n<pre class=\"codeSample\">objOU.Filter = Array(\"Computer\")\n<\/pre>\n<p><i>Now<\/i> we can execute our For Each loop, indenting two spaces (for aesthetic purposes) and then echoing back the value of each computer\u2019s <b>CN<\/b> attribute. That\u2019s going to result in output similar to this:<\/p>\n<pre class=\"codeSample\">OU=Administration,DC=fabrikam,DC=com\n  atl-ws-001\n  atl-ws-002\n  atl-ws-003\n  atl-ws-004\n<\/pre>\n<p>Which is exactly the kind of output we wanted.<\/p>\n<p>After we\u2019ve dispensed with the first OU in the recordset we use a pair of <b>Wscript.Echo<\/b> statements to insert a couple of blank lines in our output, then call the <b>MoveNext<\/b> method to move on to the next record in the recordset. From there we simply repeat the process with the next OU.<\/p>\n<p>Like we said, this approach might seem a little backwards. In the end, though, it works: we wind up with a list of each OU in the domain as well as a list of all the computer accounts residing in each of those OUs.<\/p>\n<p>In case you\u2019re wondering, it wasn\u2019t just the weather that bedeviled the Scripting Guy who writes this column last week. Friday afternoon he went to the bathroom, and when he came out he, well, discovered that he <i>couldn\u2019t<\/i> come out; that\u2019s because there was a crew working on the lights or something and they had parked several big dollies full of equipment right in front of the bathroom door. For a brief moment it looked like the Scripting Guy who writes this column might have to spend the rest of the day in the bathroom.<\/p>\n<p>Which, considering the way the rest of the week went, might not have been all that bad.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey, Scripting Guy! How can I get a list of all the computers in my domain, grouping those computers by the OU in which the computer account resides? &#8212; TA Hey, TA. Well, today is Monday, and the Scripting Guy who writes this column is actually glad it\u2019s Monday. Not that he\u2019s all that excited [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[7,46,43,3,8,5],"class_list":["post-55883","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-active-directory","tag-computer-accounts","tag-ous","tag-scripting-guy","tag-searching-active-directory","tag-vbscript"],"acf":[],"blog_post_summary":"<p>Hey, Scripting Guy! How can I get a list of all the computers in my domain, grouping those computers by the OU in which the computer account resides? &#8212; TA Hey, TA. Well, today is Monday, and the Scripting Guy who writes this column is actually glad it\u2019s Monday. Not that he\u2019s all that excited [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/55883","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=55883"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/55883\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=55883"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=55883"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=55883"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}