{"id":64943,"date":"2007-05-03T12:59:00","date_gmt":"2007-05-03T12:59:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2007\/05\/03\/hey-scripting-guy-how-can-i-determine-the-home-directory-for-all-the-disabled-user-accounts-in-active-directory\/"},"modified":"2007-05-03T12:59:00","modified_gmt":"2007-05-03T12:59:00","slug":"hey-scripting-guy-how-can-i-determine-the-home-directory-for-all-the-disabled-user-accounts-in-active-directory","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/hey-scripting-guy-how-can-i-determine-the-home-directory-for-all-the-disabled-user-accounts-in-active-directory\/","title":{"rendered":"Hey, Scripting Guy! How Can I Determine the Home Directory For All the Disabled User Accounts in Active Directory?"},"content":{"rendered":"<p><IMG class=\"nearGraphic\" title=\"Hey, Scripting Guy! Question\" border=\"0\" alt=\"Hey, Scripting Guy! Question\" align=\"left\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" width=\"34\" height=\"34\"> \n<P>Hey, Scripting Guy! How can I determine the home directory for all the disabled user accounts in Active Directory?<BR><BR>&#8212; DB<\/P><IMG border=\"0\" alt=\"Spacer\" src=\"https:\/\/devblogs.microsoft.com\/scripting\/wp-content\/uploads\/sites\/29\/2019\/05\/spacer.gif\" width=\"5\" height=\"5\"><IMG class=\"nearGraphic\" title=\"Hey, Scripting Guy! Answer\" border=\"0\" alt=\"Hey, Scripting Guy! Answer\" align=\"left\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" width=\"34\" height=\"34\"><A href=\"http:\/\/go.microsoft.com\/fwlink\/?linkid=68779&amp;clcid=0x409\"><IMG class=\"farGraphic\" title=\"Script Center\" border=\"0\" alt=\"Script Center\" align=\"right\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/ad.jpg\" width=\"120\" height=\"288\"><\/A> \n<P>Hey, DB. You know, it\u2019s not like the Scripting Guy who writes this column is addicted to TV or anything, but last night he was watching TV (as usual) when he saw a brief feature on a new device that will scramble eggs for you. As the commentator noted, scrambling eggs is a \u201cdifficult and tiring chore.\u201d But you\u2019ll never have to scramble your own eggs again; instead, you can just let this new egg scrambler thing scramble those eggs for you.<\/P>\n<P>Now, if you want to know the truth, the Scripting Guy who writes this column has never really found egg scrambling to be all that difficult or all that tiring. Admittedly, he usually lies down and takes a nap after scrambling eggs, but that\u2019s due more to him being lazy than it is to egg scrambling being particularly difficult or tiring. Apparently, however, egg scrambling <I>is<\/I> a major problem for people. Or \u2013 check that \u2013 egg scrambling <I>used<\/I> to be a major problem for people. Now, thanks to the wonders of modern technology, you\u2019ll \u201cnever use that whisk again.\u201d Instead, the next time you want scrambled eggs all you have to do is take an egg, slide it onto this little needle-like thing, then turn on the egg scrambler. When you take the egg out and crack it open \u2013 voila! the egg has already been scrambled! Right in its very own shell!<\/P>\n<P>Will wonders never cease?<\/P>\n<P>At any rate, DB, we almost didn\u2019t answer your question today; in fact, we toyed with the idea of never bothering to answer a question ever again. We\u2019re sure you can understand why. After all, a script that determines the home directory for all the disabled user accounts in Active Directory looks pretty lame when compared to an automatic egg scrambler. But the show must go on, right? The following script won\u2019t scramble eggs for you (and for that we apologize), but maybe someone can get <I>some<\/I> use out of it:<\/P><PRE class=\"codeSample\">On Error Resume Next<\/p>\n<p>Set objConnection = CreateObject(&#8220;ADODB.Connection&#8221;)\nSet objCommand = CreateObject(&#8220;ADODB.Command&#8221;)\nobjConnection.Provider = &#8220;ADsDSOObject&#8221;\nobjConnection.Open &#8220;Active Directory Provider&#8221;\nSet objCommand.ActiveConnection = objConnection<\/p>\n<p>objCommand.Properties(&#8220;Page Size&#8221;) = 1000<\/p>\n<p>objCommand.CommandText = _\n    &#8220;&lt;LDAP:\/\/dc=fabrikam,dc=com&gt;;&#8221; &amp; _\n    &#8220;(&amp;(objectCategory=User)(userAccountControl:1.2.840.113556.1.4.803:=2));&#8221; &amp; _\n            &#8220;Name,homeDirectory;Subtree&#8221;\nSet objRecordSet = objCommand.Execute<\/p>\n<p>objRecordSet.MoveFirst<\/p>\n<p>Do Until objRecordSet.EOF\n    Wscript.Echo objRecordSet.Fields(&#8220;Name&#8221;).Value\n    Wscript.Echo objRecordSet.Fields(&#8220;homeDirectory&#8221;).Value\n    Wscript.Echo \n    objRecordSet.MoveNext\nLoop\n<\/PRE>\n<P>As usual, we won\u2019t discuss each and every line of code in this script; if you could use a refresher course on writing Active Directory search scripts we recommend you take a look at our two-part <I>Tales from the Script<\/I> series <A href=\"http:\/\/null\/technet\/scriptcenter\/resources\/tales\/sg0405.mspx\"><B>Dude, Where\u2019s My Printer?<\/B><\/A> However, we will spend a miute or two looking at the query we use in this script, a query that retrieves the values of the <B>Name<\/B> and <B>homeDirectory<\/B> attributes for all the users whose user accounts have been disabled:<\/P><PRE class=\"codeSample\">objCommand.CommandText = _\n    &#8220;&lt;LDAP:\/\/dc=fabrikam,dc=com&gt;;&#8221; &amp; _\n    &#8220;(&amp;(objectCategory=User)(userAccountControl:1.2.840.113556.1.4.803:=2));&#8221; &amp; _\n            &#8220;Name,homeDirectory;Subtree&#8221;\n<\/PRE>\n<P>Why does our query look like <I>this<\/I>? Well, in order to determine whether or not a user account has been disabled we need to check the value of the <B>userAccountControl<\/B> attribute. As it turns out, userAccountControl is a bitmask attribute, an attribute that actually contains a number of properties and their values. (For a list of those properties, see the <A href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/ms680832.aspx\" target=\"_blank\"><B>Active Directory Schema Reference<\/B><\/A> on MSDN.) Because userAccountControl <I>is<\/I> a bitmask attribute, we can\u2019t use a standard SQL query (e.g., \u201cSelect Name, homeDirectory From \u2026.\u201d) in order to retrieve information based on that attribute; it just won\u2019t work. Instead, we have to use the LDAP query syntax. And <I>that\u2019s<\/I> why this query looks so weird.<\/P>\n<P>Without going into too much detail (for that, see our <A href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkId=25562\" target=\"_blank\"><B>Scripting Guys webcast<\/B><\/A> on writing Active Directory search scripts) here\u2019s an explanation of the various parts of the query (note that the individual parts in the query itself are separated using semicolons):<\/P>\n<P><B>&lt;LDAP:\/\/dc=fabrikam,dc=com<\/B>&gt;. This one you can probably figure out yourself: it\u2019s simply the domain we want to search.<\/P>\n<P><B>(&amp;(objectCategory=User)(userAccountControl:1.2.840.113556.1.4.803:=2))<\/B>. This, believe it or not, is our query filter, the part where we specify the criteria for the search. In this case, we have two criteria that must be met: the object must have an <B>objectCategory<\/B> equal to <I>user<\/I>, and the flag for a disabled user account (which has a value of 2) must be set. Granted, none of that is particularly obvious just from looking at the query, but it\u2019s true. For example, the <B>1.2.840.113556.1.4.803<\/B> is the \u201cLDAP bit matching rule,\u201d something which is essentially equal to the AND keyword in a Boolean command like <B>If objUserAccountControl AND 2<\/B>. <\/P>\n<P>So why didn\u2019t the LDAP query folks just use the word AND? We don\u2019t know; we can only speculate that it really wouldn\u2019t be an LDAP bit matching rule if they didn\u2019t. But don\u2019t worry about that; just use the syntax exactly as we\u2019ve shown and you\u2019ll be fine.<\/P>\n<P>Incidentally, the ampersand (<B>&amp;<\/B>) at the beginning of the filter string simply ties together the two parts of our query: the objectCategory must be equal to user <I>and<\/I> the appropriate flag in the userAccountControl must be set. Again, take a look at <A href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkId=25562\" target=\"_blank\"><B>our webcast<\/B><\/A> if you\u2019d like more information on LDAP queries. If you\u2019d just as soon <I>not<\/I> have more information, well, that\u2019s fine. In that case, just copy the code shown here and tweak it if you ever want to query on a different property found in the userAccountControl. For example, if you\u2019re a user who has a password that never expires, the flag with the value 65536 will be enabled in your userAccountControl. That means we can search for you (and your fellow non-expiring password types) using this filter:<\/P><PRE class=\"codeSample\">(&amp;(objectCategory=User)(userAccountControl:1.2.840.113556.1.4.803:=65536))\n<\/PRE>\n<P>All we did was change the value that we\u2019re looking for, substituting 65536 for the value 2 we used in our original filter.<\/P>\n<TABLE id=\"EDG\" class=\"dataTable\" cellSpacing=\"0\" cellPadding=\"0\">\n<THEAD><\/THEAD>\n<TBODY>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\"><B>Note<\/B>. You might have noticed that our LDAP query seems kind of jammed together; for example, we have <B>objectCategory=user<\/B> instead of spacing that out a bit nicer: <B>objectCategory = user<\/B>. Is that important? Surprisingly enough, it is. If you put blank spaces between the attribute, the equals sign, and the value (e.g., objectCategory = user) your script will fail. In other words, jam everything together as closely as you can.<\/P><\/TD><\/TR><\/TBODY><\/TABLE>\n<DIV class=\"dataTableBottomMargin\"><\/DIV>\n<P><B>Name,homeDirectory<\/B>. This filter piece is a bit easier to understand: it\u2019s just a list of the property values we want the script to return. Note that we separate the individual values using commas. The semicolon (as noted) is used to separate the individual filter pieces.<\/P>\n<P><B>Subtree<\/B>. This is the search scope, and simply tells the script to start the search in the domain root and then search every container found in that domain.<\/P>\n<P>Yes, that <I>is<\/I> kind of crazy. However, it <I>does<\/I> work, and once we define the query all we have to do is call the <B>Execute<\/B> method to kick off the search and get back a recordset containing the Name and homeDirectory for all the disabled user accounts. As soon as we have that recordset we can set up a Do Until loop that loops through the returned data, echoing back the appropriate values for each account:<\/P><PRE class=\"codeSample\">Do Until objRecordSet.EOF\n    Wscript.Echo objRecordSet.Fields(&#8220;Name&#8221;).Value\n    Wscript.Echo objRecordSet.Fields(&#8220;homeDirectory&#8221;).Value\n    Wscript.Echo \n    objRecordSet.MoveNext\nLoop\n<\/PRE>\n<P>Two things to pay attention to here. First, note the syntax for referring to an item in the recordset:<\/P><PRE class=\"codeSample\">objRecordSet.Fields(&#8220;Name&#8221;).Value\n<\/PRE>\n<P>Again, don\u2019t worry about the why. Just copy the code as-is and your script should work just fine.<\/P>\n<P>Second, remember to call the <B>MoveNext<\/B> method in order to move on to the next record in the recordset. Leave out MoveNext and your script will echo back information for the first record, return to the beginning of the loop, and then \u2013 alas \u2013 echo back information for the first record all over again. This will continue forever and ever and ever. Any time you work with an Active Directory recordset you must specifically tell the script to move to the next record. If you don\u2019t, it won\u2019t.<\/P>\n<P>And that should do it, DB. Like we said, this won\u2019t help you much with your scrambled egg problems; fortunately, though, there are entire teams of researchers and engineers working around the clock to help alleviate those problems. <\/P>\n<TABLE id=\"ERH\" class=\"dataTable\" cellSpacing=\"0\" cellPadding=\"0\">\n<THEAD><\/THEAD>\n<TBODY>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P><B>Note<\/B>. In the interests of full disclosure we should note that the Scripting Guys actually took a whack at this ourselves: we shook a chicken really, really hard right before she laid her eggs, hoping that she would go ahead and lay a pre-scrambled egg. <I>(<\/I><I>Ed<\/I><I>itor\u2019s Note: No, we really didn\u2019t. And don\u2019t <\/I>you<I> try it<\/I><I>,<\/I><I> either.)<\/I> How did that go? Let\u2019s put it this way: not quite as well as we had hoped.<\/P>\n<P>And the chicken wasn\u2019t all that happy with the process either.<\/P><\/TD><\/TR><\/TBODY><\/TABLE><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey, Scripting Guy! How can I determine the home directory for all the disabled user accounts in Active Directory?&#8212; DB Hey, DB. You know, it\u2019s not like the Scripting Guy who writes this column is addicted to TV or anything, but last night he was watching TV (as usual) when he saw a brief feature [&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,3,8,20,5],"class_list":["post-64943","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-active-directory","tag-scripting-guy","tag-searching-active-directory","tag-user-accounts","tag-vbscript"],"acf":[],"blog_post_summary":"<p>Hey, Scripting Guy! How can I determine the home directory for all the disabled user accounts in Active Directory?&#8212; DB Hey, DB. You know, it\u2019s not like the Scripting Guy who writes this column is addicted to TV or anything, but last night he was watching TV (as usual) when he saw a brief feature [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/64943","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=64943"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/64943\/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=64943"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=64943"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=64943"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}