Scripting Guy 為您解答問題

Hey, Scripting Guy!

歡迎使用 TechNet 專欄,Microsoft Scripting Guy 會在此為您解答有關系統管理指令碼的常見問題。您有關於系統管理指令碼方面的問題嗎?請將電子郵件傳送到 scripter@microsoft.com。我們無法保證能夠逐一回答每個問題,不過我們會盡力而為。

資源

如何取得擁有過期密碼的所有本機使用者清單?

Hey, Scripting Guy! Question

嗨,Scripting Guy!如何取得擁有過期密碼的所有本機使用者清單?

        -- JM

Hey, Scripting Guy! Answer

JM,您好。謝謝您提出的問題。不過,在回答問題之前,我們有個問題要問:您手邊不會剛好有多的骰子吧,有嗎?沒錯,骰子,就是在玩棋盤遊戲和其他類似遊戲時使用的那種東西。其實,Scripting Guy 非常需要骰子。事實上,我們要盡快取得 1,000 個骰子。不幸的是,在能夠負擔的價格下,要找出這麼多骰子真的有點困難。

主要是因為如果某些東西真的價格,我們就負擔不起了。

為什麼 Scripting Guy 需要這麼多骰子呢?喔,真的沒有特別的原因。畢竟,我們 Microsoft 的技術文件作家,所以 1,000 骰子對於技術文件作家而言是相當標準的配備。當一個 Scripting Guy 已經有夠困難了。但是,您可以試試看當個手上沒有骰子的 Scripting Guy。

知道了吧?不是告訴過您了:沒有足夠的骰子,您真的沒辦法當一個 Scripting Guy,對吧?

反正,JM,如果您剛好有沒有在用的骰子,我們會很樂意接手的。而且我們想要提供這個機會給閱讀本專欄的任何讀者。您的閣樓或地下室堆滿了成箱的骰子嗎?那麼,請將它們寄到這個地址:

Microsoft Scripting Guy
Microsoft Corporation
Building 42/4039
One Microsoft Way
Redmond, WA 98052

我們將很樂意接受它們,形狀、大小和顏色都不拘。

絨毛骰子可能就免了。

當然,Scripting Guy 絕對不會如此無禮地要求骰子,卻不提供一點回報。我們來看看有什麼可以提供好了...對了,列出擁有過期密碼之所有本機使用者的指令碼如何?

Script Center
Const ADS_UF_DONT_EXPIRE_PASSWD = &H10000
 
strComputer = "atl-ws-01"

Set colAccounts = GetObject("WinNT://" & strComputer & "")

colAccounts.Filter = Array("user")

For Each objUser In colAccounts
    If Not objUser.UserFlags AND ADS_UF_DONT_EXPIRE_PASSWD Then
        Wscript.Echo objUser.Name
    End If
Next

好吧,我們來看看這個指令碼如何運作。如您所見,我們一開始會定義一個名為 ADS_UF_DONT_EXPIRE_PASSWD 的常數 (很可愛的名字,對吧?),然後將其值設定為 &H10000。沒錯,這個常數將協助我們判斷使用者是否擁有過期的密碼。接著,我們會將電腦的名稱 (要在其中尋找本機帳戶的電腦) 指派給名為 strComputer 的變數,然後使用下面這行程式碼來繫結至電腦的「系統帳戶管理員」(SAM):

Set colAccounts = GetObject("WinNT://" & strComputer & "")

根據預設,以這種方式繫結至 SAM 就會傳回電腦上所有本機使用者帳戶的相關資訊。此外,它也會傳回該電腦上所有本機群組、安裝在該電腦上的所有服務、安裝在該電腦上的所有印表機等項目的相關資訊。老實說,我們根本不在乎這些項目,我們只在乎使用者帳戶 (如同事們所證實,撰寫本專欄的 Scripting Guy 肯定是位受歡迎的人)。因此,下一步就是要套用將傳回資料限制為使用者帳戶的篩選器

colAccounts.Filter = Array("user")

這實在是個好問題:為什麼我們要傳回所有使用者帳戶的集合?撰寫一個搜尋本機電腦並傳回僅包含擁有過期密碼之使用者的 SQL 查詢不會比較快而且容易嗎?老實說,沒錯,這樣做會比較快而且容易。不過,這種方法有一點問題:它無法運作。與 Active Directory 不同的是,我們無法針對 SAM 執行 SQL 查詢。因此,每當我們需要處理本機帳戶時,就必須採用這種老式做法:傳回所有帳戶的集合,然後分別檢查每一個帳戶,以便查看該使用者是否擁有過期的密碼。

恰巧這樣做會讓我們得到指令碼的下一個部分。在設定 For Each 迴圈以便逐一處理集合中的每個帳戶之後,我們接著要執行下面這行程式碼:

If Not objUser.UserFlags AND ADS_UF_DONT_EXPIRE_PASSWD Then

信不信由您,我們在此所做就是查看使用者的密碼是否過期。密碼過期資訊剛好會儲存在名為 UserFlags 的遮罩屬性中。如果您對遮罩屬性不熟悉,這種屬性是可保存多個值的單一屬性 (如需 UserFlags 中儲存之值的完整清單,請參閱 MSDN 上的 WinNT 使用者物件說明文件 (英文))。單一屬性如何包含多個值?基本上,簡單來說,遮罩包含一系列「參數」,而每個參數都具有唯一的值 (在此情況下,代表過期密碼的參數具有 &H10000 的值)。如果指定的參數已開啟,就表示該屬性已啟用。如果參數已關閉,就表示該屬性已停用。雖然檢查參數值的語法有點笨拙,但是這一部分的程式碼會告訴我們使用者是否擁有未過期的密碼 (也就是說,它會告訴我們 ADS_UF_DONT_EXPIRE_PASSWD 參數是否已開啟):

objUser.UserFlags AND ADS_UF_DONT_EXPIRE_PASSWD

您說得沒錯:我們對於擁有未過期密碼的使用者感興趣,對吧?因此,我們也使用了一樣笨拙的語法 If Not

If Not objUser.UserFlags AND ADS_UF_DONT_EXPIRE_PASSWD Then

這只是以 VBScript 的方式來表示:「如果使用者並非擁有未過期的密碼,那麼請執行下一個程式碼區塊」。

請注意:那麼,為什麼 VBScript 不乾脆這樣表示,卻讓我們使用 If Not 這種奇怪的程式碼慣例呢?我們也不知道。請在「人生的意義為何?」和「為什麼 Microsoft 不在 Scripting Guy 說他們需要骰子時提供骰子?」旁邊,記下這個問題當做其中一個人生的重大未解問題。

如果結果顯示使用者確實擁有過期的密碼,我們接著就要使用下面這行程式碼來回應使用者名稱:

Wscript.Echo objUser.Name

然後我們要回到迴圈的頂端,針對集合中的下一個項目重複此程序。

不是有人已經問過為什麼我們需要 1,000 個骰子的原因嗎?老實說,我們實際需要 2,000 個骰子。不過,我們決定以 1,000 個為起點。

除了這一點外,我們真的不能透露任何消息,否則就沒有驚喜了 (此外,我們很可能永遠不會履行目前的規劃)。因此,換言之,您必須相信 Scripting Guy 會將這些骰子用在正途。但是,這也沒什麼大不了。畢竟,Scripting Guy 以前曾經讓您失望過嗎?

喔,對了,差點忘記。這次我們不會讓 Peter 參與這個專案。我們保證!