{"id":69073,"date":"2005-08-29T15:21:00","date_gmt":"2005-08-29T15:21:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2005\/08\/29\/how-can-i-get-a-list-of-all-the-users-whose-passwords-never-expire\/"},"modified":"2005-08-29T15:21:00","modified_gmt":"2005-08-29T15:21:00","slug":"how-can-i-get-a-list-of-all-the-users-whose-passwords-never-expire","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/how-can-i-get-a-list-of-all-the-users-whose-passwords-never-expire\/","title":{"rendered":"How Can I Get a List of All the Users Whose Passwords Never Expire?"},"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 get a list of all the users whose passwords never expire?<BR><BR>&#8212; NW<\/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, NW. As you probably know, Internet gambling is illegal in the USA. That\u2019s too bad, because if it wasn\u2019t we\u2019d be willing to bet $10 that the answer to your question will be this: search Active Directory.<\/P>\n<P>And as much as we hate to say \u201cWe told you so,\u201d well, guess what:<\/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:=65536));&#8221; &amp; _\n            &#8220;Name;Subtree&#8221;\nSet objRecordSet = objCommand.Execute<\/p>\n<p>objRecordSet.MoveFirst\nDo Until objRecordSet.EOF\n    Wscript.Echo objRecordSet.Fields(&#8220;Name&#8221;).Value\n    objRecordSet.MoveNext\nLoop\n<\/PRE>\n<P>You\u2019re right: we should have bet way more than $10. What were we <I>thinking<\/I>?<\/P>\n<P>Actually, it\u2019s just as well that we didn\u2019t bet on this. The truth is, we cheated: we had some inside information. After all, any time you\u2019re looking for Active Directory-related stuff the answer is going to be the same: search Active Directory. We knew the answer long before you even asked the question.<\/P>\n<P>What we <I>didn\u2019t<\/I> know, at least in this case, was how to actually conduct our search. We won\u2019t talk too much about the details of writing scripts that search Active Directory; that background information can be found in our two-part <I>Tales from the Script<\/I> article <A href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/tales\/sg0405.mspx\"><B>Dude, Where\u2019s My Printer?<\/B><\/A> However, we will take a minute of two to talk about the query we ended up using in order to conduct the search, a query that might look a bit different from the Active Directory queries you\u2019re used to working with: <\/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:=65536));&#8221; &amp; _\n            &#8220;Name;Subtree&#8221;\n<\/PRE>\n<P>If you\u2019re thinking, \u201cWhoa, that doesn\u2019t look like a SQL query to me,\u201d well, there\u2019s a good reason for that: this <I>isn\u2019t<\/I> a SQL query. Instead, this is an example of the LDAP query syntax, a query syntax that can retrieve the same information as a SQL query, albeit in a much more cryptic fashion.<\/P>\n<P>But if that\u2019s the case, then why did we decide to use this weird-looking syntax rather than the more familiar (and more comfortable) SQL query syntax? Well, as it turns out, the property that determines whether or not a password expires (ADS_UF_DONT_EXPIRE_PASSWD) is not a \u201cstand-alone\u201d property; that is, you can\u2019t get at the value using code similar to this:<\/P><PRE class=\"codeSample\">Wscript.Echo objUser.ADS_UF_DONT_EXPIRE_PASSWD\n<\/PRE>\n<P>Instead, this value is one of several \u201cflags\u201d found in the <B>userAccountControl<\/B> attribute. The userAccountControl attribute is an example of a bitmask attribute, a single attribute that houses multiple property values; this single attribute tells you whether or not a password will expire, whether or not a password <I>has<\/I> expired, whether or not an account is disabled, etc. In this case, if the flag with the value 65536 is switched \u201con,\u201d then the password never expires; if the flag is switched off, then the password <I>does<\/I> expire.<\/P>\n<TABLE id=\"EBE\" 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>. Yes, we know: many of you have no idea what we just said. For a brief introduction to bitmasks you might take a look at <A href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/guide\/sas_scr_tspz.mspx\" target=\"_blank\"><B>this section<\/B><\/A> of the <I>Microsoft Windows 2000 Scripting Guide<\/I>.<\/P><\/TD><\/TR><\/TBODY><\/TABLE>\n<DIV class=\"dataTableBottomMargin\"><\/DIV>\n<P>So what difference does that make to us? Well, it makes a big difference: you can\u2019t (or at least can\u2019t easily) use a SQL query to search for a single value inside a bitmask attribute. You <I>can<\/I> do this using the LDAP query syntax, however, so that\u2019s what we did. Like the cars driven by a couple of the Scripting Guys, it\u2019s a bit ugly, but it works just fine. (Sorry, guys, but the truth hurts.)<\/P>\n<P>As for the query itself, we\u2019ll briefly explain what the individual pieces do, to give you some idea of how the whole thing works. Thus:<\/P>\n<TABLE id=\"E2E\" class=\"dataTable\" cellSpacing=\"0\" cellPadding=\"0\">\n<THEAD><\/THEAD>\n<TBODY>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\"><B>Item<\/B><\/P><\/TD>\n<TD>\n<P class=\"lastInCell\"><B>Description<\/B><\/P><\/TD><\/TR>\n<TR class=\"evenRecord\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\">&lt;LDAP:\/\/dc=fabrikam,dc=com&gt;<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">The starting point for the search. We want to search all of Active Directory, so that means starting in the root (i.e., fabrikam.com).<\/P><\/TD><\/TR>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\">&amp;<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">Equivalent to the AND operator in a SQL query. We need this because we\u2019re searching for users <I>and<\/I> we\u2019re searching for a specific value in the userAccountControl attribute. Both of these criteria must be met for an object to be returned.<\/P><\/TD><\/TR>\n<TR class=\"evenRecord\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\">(objectCategory=User)<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">Limits the returned data to user accounts.<\/P><\/TD><\/TR>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\">(userAccountControl:1.2.840.113556.1.4.803:=65536)<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">Indicates that we want to return only those accounts where the userAccounControl flag for 65536 is switched on; that equates to user accounts where the password doesn\u2019t expire. We\u2019ll explain this werid-looking block of code in a little more detail down below.<\/P><\/TD><\/TR>\n<TR class=\"evenRecord\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\">Name<\/P><\/TD>\n<TD>\n<P>The Active Directory attributes we want reported back. We\u2019re asking to get back only a single attribute: <B>Name<\/B>. To report back additional attributes just tack them on to the end of Name, separating each one using commas:<\/P><PRE class=\"codeSample\">Name,cn,AdsPath\n<\/PRE><\/TD><\/TR>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\">Subtree<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">Indicates the type of search. Specifying <B>Subtree<\/B> causes the script to search all the OUs and containers found in the root of fabrikam.com. Because all the OUs and containers <I>have<\/I> to be found in the root this causes the script to search all of Active Directory.<\/P><\/TD><\/TR><\/TBODY><\/TABLE>\n<DIV class=\"dataTableBottomMargin\"><\/DIV>\n<P>As odd as this might look, most of it actually makes sense: once you know what the individual parts represent, it isn\u2019t too hard to read (or even to modify) an LDAP query. Well, OK, with one exception:<\/P><PRE class=\"codeSample\">(userAccountControl:1.2.840.113556.1.4.803:=65536)\n<\/PRE>\n<P>Yes, we know how weird it looks and, yes, we wish some of the werindess had been hidden from view. But, setting looks aside, this is really pretty simple stuff. The <B>1.2.840.113556.1.4.803<\/B> is nothing more than an example of an LDAP matching rule object identifier (OID); in turn, this is just a somewhat-clunky way of saying: \u201cShow me all the objects where the userAccountControl flag with the value 65536 is on.\u201d That\u2019s really all there is to it. That also means that you can search for other attribute values found in <A href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms680832.aspx\" target=\"_blank\"><B>userAccountControl<\/B><\/A> simply by substituting the desired value for 65536. Need a list of all the disabled accounts? Here you go:<\/P><PRE class=\"codeSample\">(userAccountControl:1.2.840.113556.1.4.803:=2)\n<\/PRE>\n<P>Cool.<\/P>\n<P>And yes, we know your next question: can you modify our original query to find all the users who <I>don\u2019t<\/I> have non-expiring passwords? Sure, although the syntax is, again, a bit odd-looking. Without bothering to explain it all, here\u2019s modified code that searches for user accounts where the password <I>does<\/I> expire:<\/P><PRE class=\"codeSample\">(!(userAccountControl:1.2.840.113556.1.4.803:=65536))\n<\/PRE>\n<P>In this case, the <B>!<\/B> indicates \u201cdoes not equal.\u201d Bet you 10 bucks that you didn\u2019t know <I>that<\/I>, did you?<\/P>\n<P>Oh. Well, OK then, just let us know where the send the check. This is <I>not<\/I> going to look good on our expense account. (Or at least it wouldn\u2019t if any of us actually <I>had<\/I> an expense account.)<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey, Scripting Guy! How can I get a list of all the users whose passwords never expire?&#8212; NW Hey, NW. As you probably know, Internet gambling is illegal in the USA. That\u2019s too bad, because if it wasn\u2019t we\u2019d be willing to bet $10 that the answer to your question will be this: search Active [&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,20,5],"class_list":["post-69073","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-active-directory","tag-scripting-guy","tag-user-accounts","tag-vbscript"],"acf":[],"blog_post_summary":"<p>Hey, Scripting Guy! How can I get a list of all the users whose passwords never expire?&#8212; NW Hey, NW. As you probably know, Internet gambling is illegal in the USA. That\u2019s too bad, because if it wasn\u2019t we\u2019d be willing to bet $10 that the answer to your question will be this: search Active [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/69073","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=69073"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/69073\/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=69073"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=69073"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=69073"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}