Share via


嗨,Scripting Guy!

嗨,Scripting Guy!

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

還有,別忘了瞧瞧全新經過改良的《嗨,Scripting Guy!過往文件》。

今天的問題:如何取得所有密碼從未過期的使用者清單?


如何取得所有密碼從未過期的使用者清單?

嗨,Scripting Guy!我該如何取得所有密碼從未過期的使用者清單?

-- NW

NW,您好。您可能知道網路賭博在美國是非法的。真可惜。如果可以的話,真想打賭 10 塊美金您會這麼回答:搜尋 Active Directory。

雖然很我們不喜歡說「不是跟您說嗎」,但是猜猜看怎樣:

On Error Resume Next
Set objConnection = CreateObject("ADODB.Connection")
Set objCommand =   CreateObject("ADODB.Command")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
Set objCommand.ActiveConnection = objConnection
objCommand.Properties("Page Size") = 1000
objCommand.CommandText = _
    "<LDAP://dc=fabrikam,dc=com>;" & _
        "(&(objectCategory=User)(userAccountControl:1.2.840.113556.1.4.803:=65536));" & _
            "Name;Subtree"
Set objRecordSet = objCommand.Execute
objRecordSet.MoveFirst
Do Until objRecordSet.EOF
    Wscript.Echo objRecordSet.Fields("Name").Value
    objRecordSet.MoveNext
Loop

您答對啦!唉,剛才應該多下注一點,真是的。

事實上,還好我們沒有下賭注。因為我們其實有作弊啦,我們有些內線消息。不管何時您查看與 Active Directory 相關的作業時,都會得到相同的答案:搜尋 Active Directory。在您發問之前,我們早就知道答案了。

但我們不知道 (至少在此案例中) 要如何真正進行搜尋。我們不會詳細討論怎麼編寫指令碼搜尋 Active Directory ,您可以在《指令碼物語》(Tales from the Script) 中的〈老兄,我的列表機咧?〉(Dude, Where's My Printer?) 文章中取得詳細資訊。但是,我們想花幾分鐘來討論我們最後決定要使用哪種查詢來進行搜尋,和您慣用的 Active Directory 查詢有點不同:

objCommand.CommandText = _
    "<LDAP://dc=fabrikam,dc=com>;" & _
        "(&(objectCategory=User)(userAccountControl:1.2.840.113556.1.4.803:=65536));" & _
            "Name;Subtree"

如果您正在想「哇,這看起來不像是 SQL 查詢」,其實挺合理的。因為這根本就不是 SQL 查詢。這其實是一個 LDAP 查詢語法的範例。這種語法相較下語意不明,但仍然可以像 SQL 查詢一樣擷取相同資訊。

我們為何決定使用這個看起來有點詭異的語法,而不用較熟悉 (用起來更自在) 的 SQL 查詢語法呢?是這樣的,其實取決密碼是否過期的屬性 (ADS_UF_DONT_EXPIRE_PASSWD) 並不是「獨立」屬性。也就是說,您無法使用類似這樣的程式碼來擷取值:

Wscript.Echo objUser.ADS_UF_DONT_EXPIRE_PASSWD

因為這個值是在 userAccountControl 屬性中找到的幾個「旗標」之一。userAccountControl 屬性是位元遮罩屬性的一個範例,是可以儲存許多屬性值的單一屬性。這個單一屬性可以告訴我們密碼是否即將或已經過期,或是帳戶是否已停用等等。在此案例中,如果值為 65536 的旗標被切換為「使用」,那麼密碼就永遠不會過期。


附註:好啦!我知道很多人聽得一頭霧水。如果您想進一步瞭解位元遮罩,請參閱 《Microsoft Windows 2000 指令碼指南》(英文) 中的此節


那麼這樣有什麼差別呢?其實並沒有太大差別。您沒有辦法 (或不能輕易的) 使用 SQL 查詢來搜尋位元遮罩屬性內的單一值。但是 LDAP 查詢語法可以,所以我們才這麼做。就像有幾位 Scripting Guy 開的車子雖然有點醜,但是還是可以開上路的。(不好意思,兄弟們,但是事實總是很傷人的。)

至於關於查詢本身,我們將會簡略解說每個單獨組件的功用,給您一些關於整個架構是如何運作的概念。請看:


項目 說明
<LDAP://dc=fabrikam,dc=com> 搜尋的起始點。我們想要搜尋整個 Active Directory,這表示從根 (例如 fabrikam.com) 開始。
& 相等於 SQL 查詢中的 AND 運算子。我們需要這個,因為我們要搜尋使用者以及 userAccountControl 屬性中的特定值。這兩個條件都必須符合,物件才會回傳。
(objectCategory=User) 限制回傳資料至使用者帳戶。
(userAccountControl:1.2.840.113556.1.4.803:=65536) 指明我們只要回傳 userAccounControl 旗標 65536 已啟用的帳戶,也就是使用者帳戶密碼尚未過期。我們將會在以下更詳盡地解說這個看似詭異的程式碼區塊。
名稱

我們想要回報的 Active Directory 屬性。我們要求只回傳單一屬性:Name。如要回報其他屬性,只要將它們加到 Name 的後面,屬性間用逗號分隔即可:

Name,cn,AdsPath

樹狀子目錄 指明搜尋種類。指定樹狀子目錄使得指令碼搜尋在 fabrikam.com 根中的所有 OU 及容器。因為必須要從根中找出所有的 OU 及容器,這使得指令碼搜尋所有的 Active Directory。


或許這看起來很古怪,但大部分有其道理:一但您知道各個部份所代表的東西,要讀懂 (或是甚至修改) LDAP 查詢就不是難事了。好吧,其實有一個例外:

(userAccountControl:1.2.840.113556.1.4.803:=65536)

我們知道這看起來很詭異,真希望可以把一些奇怪的地方藏起來免得礙眼。但略過外觀不談,這真的是挺簡單的東西。1.2.840.113556.1.4.803 只不過是 LDAP 對應規則物件識別元 (OID) 的一個範例,就好像短話長說:「給我瞧瞧所有 userAccountControl 旗標值為 65536 的所有物件」。就是這麼簡單!同理可證,只要將想要的值替換成 65536,您也可以搜尋 userAccountControl 中的其他屬性值。需要所有停用帳戶的清單嗎?方法如下:

(userAccountControl:1.2.840.113556.1.4.803:=2)

不賴吧?

我們也猜到您接下來想問的問題囉:可不可以修改原來的查詢,找出不具有未到期密碼的所有使用者嗎?當然沒問題,雖然語法看起還是會有點奇怪。不再囉唆,以下就是搜尋所有密碼已經到期使用者帳戶的修改程式碼:

(!(userAccountControl:1.2.840.113556.1.4.803:=65536))

在此案例中,! 表示「並不等於」。跟您賭 $10 美金,這個您就不知道了吧?

什麼,您知道喔。嗯,好吧,讓我們知道支票要寄到哪個地址就行了。看來,這對開支帳戶裡的餘額會很傷 (不過那等我們真的開了支票帳戶再說吧)。


如需詳細資訊

請參閱嗨,Scripting Guy! - 過往文件

 

回到頁首 回到頁首