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