{"id":65043,"date":"2007-04-19T21:18:00","date_gmt":"2007-04-19T21:18:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2007\/04\/19\/how-can-i-get-a-list-of-all-the-local-users-who-have-an-expiring-password\/"},"modified":"2007-04-19T21:18:00","modified_gmt":"2007-04-19T21:18:00","slug":"how-can-i-get-a-list-of-all-the-local-users-who-have-an-expiring-password","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/how-can-i-get-a-list-of-all-the-local-users-who-have-an-expiring-password\/","title":{"rendered":"How Can I Get a List of All the Local Users Who Have an Expiring Password?"},"content":{"rendered":"<p><IMG 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\"> \n<P>Hey, Scripting Guy! How can I get a list of all the local users who have an expiring password?<BR><BR>&#8212; JM<\/P><IMG height=\"5\" alt=\"Spacer\" src=\"https:\/\/devblogs.microsoft.com\/scripting\/wp-content\/uploads\/sites\/29\/2019\/05\/spacer.gif\" width=\"5\" border=\"0\"><IMG 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 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> \n<P>Hey, JM. Thanks for the question. Before we answer it, however, we have a question for <I>you<\/I>: you don\u2019t by any chance have any spare dice lying around, do you? That\u2019s right, dice, the kind you use when playing board games and craps and stuff like that. As it turns out, the Scripting Guys have an urgent need for dice; in fact, we could use 1,000 individual die ASAP. Unfortunately, we\u2019re having a little bit of trouble locating that many dice at a price we can afford. <\/P>\n<P>Mainly because if something actually <I>has<\/I> a price, we can\u2019t afford it.<\/P>\n<P>Why do the Scripting Guys need so many dice? Oh, no reason, really. After all, we <I>are<\/I> technical writers at Microsoft, and 1,000 dice is pretty much standard equipment for a technical writer. It\u2019s hard enough being a Scripting Guy as it is. But just try being a Scripting Guy who doesn\u2019t have any dice.<\/P>\n<P>See? What did we tell you: you really <I>can\u2019t<\/I> be a Scripting Guy without plenty of dice, can you?<\/P>\n<P>Anyway, JM, if you happen to have any dice you aren\u2019t using we\u2019d be happy to take them off your hands. And we\u2019d like to extend that offer to anyone else who might be reading this column. Got boxes and boxes of dice filling up your attic or your basement? Then send them to this address:<\/P>\n<P>The Microsoft Scripting Guys<BR>Microsoft Corporation<BR>Building 42\/4039<BR>One Microsoft Way<BR>Redmond, WA 98052<\/P>\n<P>We\u2019ll be happy to take them, in all shapes, sizes and colors.<\/P>\n<P>Except maybe the fuzzy ones.<\/P>\n<P>Of course, the Scripting Guys would never be so rude as to ask for dice without offering a little something in return. Let\u2019s see what we got here \u2026 hey, how about a script that lists all the local users who have an expiring password:<\/P><PRE class=\"codeSample\">Const ADS_UF_DONT_EXPIRE_PASSWD = &amp;H10000<\/p>\n<p>strComputer = &#8220;atl-ws-01&#8221;<\/p>\n<p>Set colAccounts = GetObject(&#8220;WinNT:\/\/&#8221; &amp; strComputer &amp; &#8220;&#8221;)<\/p>\n<p>colAccounts.Filter = Array(&#8220;user&#8221;)<\/p>\n<p>For Each objUser In colAccounts\n    If Not objUser.UserFlags AND ADS_UF_DONT_EXPIRE_PASSWD Then\n        Wscript.Echo objUser.Name\n    End If\nNext\n<\/PRE>\n<P>OK, let\u2019s see if we can figure out how this baby works. As you can see, we start out by defining a constant named ADS_UF_DONT_EXPIRE_PASSWD (a delightful name, don\u2019t you think?) and setting the value to &amp;H10000. And you\u2019re right, this constant will help us determine whether or not a user has a password that expires. We next assign the name of the computer (the computer where the local accounts are to be found) to a variable named strComputer, then use this line of code to bind to the computer\u2019s System Account Manager (SAM):<\/P><PRE class=\"codeSample\">Set colAccounts = GetObject(&#8220;WinNT:\/\/&#8221; &amp; strComputer &amp; &#8220;&#8221;)\n<\/PRE>\n<P>By default, binding to the SAM in this fashion returns information about all the local user accounts on the computer; it also returns information about all the local groups on that computer, all the services installed on that computer, all the printers installed on that computer, etc. The truth is, we don\u2019t care about those other things; all we care about are user accounts. (As his colleagues will attest, the Scripting Guy who writes this column is definitely a people person.) Therefore, our next step is to apply a <B>Filter<\/B> that limits the returned data to user accounts:<\/P><PRE class=\"codeSample\">colAccounts.Filter = Array(&#8220;user&#8221;)\n<\/PRE>\n<P>You know that\u2019s a good question: why did we return a collection of <I>all<\/I> the user accounts? Wouldn\u2019t it have been faster and easier to write a SQL query that searches the local computer and returns a collection consisting of only those users who have an expiring password? To tell you the truth, yes, that would have been much faster and much easier. Unfortunately, though, there\u2019s a bit of a problem with that approach: it won\u2019t work. Unlike Active Directory, we can\u2019t run SQL queries against the SAM. Consequently, any time we need to work with local accounts we need to do things the old-fashioned way: return a collection of all the accounts, and then individually check each one to see whether or not that user has a password that expires.<\/P>\n<P>Which, coincidentally enough, brings us to the next part of our script. After setting up a For Each loop to walk through each account in the collection we then execute this line of code:<\/P><PRE class=\"codeSample\">If Not objUser.UserFlags AND ADS_UF_DONT_EXPIRE_PASSWD Then\n<\/PRE>\n<P>Believe it or not, what we\u2019re doing here is checking to see whether the user\u2019s password ever expires. Password expiration information happens to be stored in a bitmasked attribute named <B>UserFlags<\/B>. If you aren\u2019t familiar with bitmasked attributes, this type of attribute is a single property that can hold multiple values. (For a complete list of the values stored in UserFlags, see the <A href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/aa772300.aspx\" target=\"_blank\"><B>WinNT User Object documentation<\/B><\/A> on MSDN.) How can a single property contain multiple values? Well, without going into too much detail, a bitmask consists of a series of \u201cswitches,\u201d each with a unique value (in this case, the switch for expiring passwords has a value of &amp;H10000). If a given switch is on, that means the property is enabled; if the switch is off, the property is disabled. The syntax for checking the value of a switch is a bit clumsy, but this portion of the code tells us whether or not the user has a <I>non-expiring <\/I>password (that is, it tells us whether the ADS_UF_DONT_EXPIRE_PASSWD switch is on):<\/P><PRE class=\"codeSample\">objUser.UserFlags AND ADS_UF_DONT_EXPIRE_PASSWD\n<\/PRE>\n<P>Good point: we <I>aren\u2019t<\/I> interested in users with non-expiring passwords, are we? That\u2019s why we also used the equally-clumsy syntax <B>If Not<\/B>:<\/P><PRE class=\"codeSample\">If Not objUser.UserFlags AND ADS_UF_DONT_EXPIRE_PASSWD Then\n<\/PRE>\n<P>This is simply VBScript\u2019s way of saying, \u201cIf the user <I>doesn\u2019t<\/I> have a non-expiring password then run the next block of code.\u201d<\/P>\n<TABLE class=\"dataTable\" id=\"EJF\" 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>. So then why didn\u2019t VBScript just <I>say<\/I> that rather than making us use a crazy coding convention like If Not? We don\u2019t know. Chalk that up as one of life\u2019s great unanswered questions, alongside \u201cWhat is the meaning of life?\u201d and \u201cWhy doesn\u2019t Microsoft just give the Scripting Guys dice any time they say they need dice?\u201d<\/P><\/TD><\/TR><\/TBODY><\/TABLE>\n<DIV class=\"dataTableBottomMargin\"><\/DIV>\n<P>If it turns out that the user does, indeed, have a password that expires we then use this line of code to echo back the user name:<\/P><PRE class=\"codeSample\">Wscript.Echo objUser.Name\n<\/PRE>\n<P>And then it\u2019s back to the top of the loop where we repeat the process with the next item in the collection.<\/P>\n<P>Didn\u2019t someone already ask that question, the one about why we need 1,000 dice? Well, to tell you the truth, we don\u2019t; we actually need 2,000 dice. However, we decided to start with 1,000 and then go from there. <\/P>\n<P>Other than that, well, we can\u2019t really tell you anything; that would spoil the surprise. (Besides, there\u2019s a good chance we\u2019ll never actually implement what we have in mind anyway.) So, in other words, you\u2019ll just have to trust that the Scripting Guys will put those dice to good use. But that\u2019s no big deal; after all, have the Scripting Guys ever let you down before?<\/P>\n<P>Oh, right; we forgot about that. But this time we won\u2019t let Peter anywhere near the project. Promise!<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey, Scripting Guy! How can I get a list of all the local users who have an expiring password?&#8212; JM Hey, JM. Thanks for the question. Before we answer it, however, we have a question for you: you don\u2019t by any chance have any spare dice lying around, do you? That\u2019s right, dice, the kind [&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":[23,24,3,5],"class_list":["post-65043","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-local-accounts-and-windows-nt-4-0-accounts","tag-other-directory-services","tag-scripting-guy","tag-vbscript"],"acf":[],"blog_post_summary":"<p>Hey, Scripting Guy! How can I get a list of all the local users who have an expiring password?&#8212; JM Hey, JM. Thanks for the question. Before we answer it, however, we have a question for you: you don\u2019t by any chance have any spare dice lying around, do you? That\u2019s right, dice, the kind [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/65043","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=65043"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/65043\/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=65043"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=65043"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=65043"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}