嗨,Scripting Guy!

Hey,Scripting Guy!

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

今天的問題:如何傳回所有電腦的清單,但排除指定組織單位 (OU) 中的電腦?


如何傳回所有電腦的清單,但排除指定組織單位 (OU) 中的電腦?

嗨,Scripting Guy!我需要取得網域中所有電腦的清單,但「不包括」測試實驗室 OU 中的電腦。我該如何做到這點呢?

-- AM

AM,您好。這似乎是 IT 領域中的一項熱門新趨勢:在前兩個星期裡我們收到了三封類似的電子郵件,都是有關系統管理員希望在列報網域中電腦清單時排除特定的 OU。在這個案例中,您似乎對於位在測試實驗室的電腦不感興趣;在其他兩個案例中,有一位系統管理員希望排除開發人員電腦,而另一位系統管理員則不希望在清單中列出網域控制站。不論想要排除哪個單位,基本概念都是一樣的:您需要所有電腦的清單,但是要排除特定的 OU。

如果您是 <嗨,Scripting Guy!> 的忠實讀者 (誰不是呢?),那麼您一定知道,我們對問題的回答,十次中有九次都是:「請搜尋 Active Directory」。我們今天的回答也是這樣,不過我們也必須承認,我們對於這個回答並不是百分之百滿意 (等一會兒您就會知道原因)。乍看之下,您或許會覺得這是一項很容易在 Active Directory 中執行的搜尋;畢竟,您要做得只不過是加上一個 WHERE 子句,告訴指令碼略過「測試實驗室 OU」中的任何電腦。換句話說:

objCommand.CommandText = _
    "SELECT Name FROM 'LDAP://dc=fabrikam,dc=com' WHERE OU <> 'TestLab OU' AND " _
        & "objectCategory='computer'"

這個辦法的確很不錯,只不過有點問題:Active Directory 電腦帳戶中並沒有名叫 OU 的屬性;換言之,您不能在 WHERE 子句中使用 OU。A 計畫講了半天,可是行不通。

當然,您這時可能會想「那麼,可不可以用辨別名稱 (DN) 呢」?畢竟,電腦的 DN (例如 CN=atl-ws-01、OU=Test LabOU、DC=fabrikam 以及 DC=com) 會包含電腦帳戶所屬 OU 的名稱。難道我們不能像下面這樣對 DN 進行萬用字元搜尋嗎?

objCommand.CommandText = _
    "SELECT Name FROM 'LDAP://dc=fabrikam,dc=com' WHERE distinguishedName <> "  _
        " '*OU=Finance' AND objectCategory='computer'"

非常遺憾,這樣也行不通。因為 DN 是一種「建構的」屬性。DN 並不是實際儲存在 Active Directory 中,它是在您要求的時候臨時建構出來的。因此,您不能對 distinguishedName 屬性進行萬用字元搜尋。而且,您也不用再問同樣含有 OU 名稱的 ADsPath 屬性了;您也不能對 ADsPath 進行萬用字元搜尋。

事實上,就我所知,唯一的辦法是執行一次搜尋,傳回「所有」電腦的清單。當您取得這個清單之後,您可能還要對它做一些處理 (我們暫時假設您只是要在螢幕上列出電腦的名稱)。不過,在對清單中的電腦進行任何處理之前,我們的指令碼必須先檢查 DN 中有沒有目標字串 (例如 OU=Test Lab OU)。如果有,指令碼會略過這部電腦,然後跳到清單中的下一部電腦繼續進行。如果找不到目標字串,指令碼便會呼叫這部電腦的名稱。

仔細看看下面的實際指令碼,可能會讓您更有概念:

On Error Resume Next
Const ADS_SCOPE_SUBTREE = 2
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.Properties("Searchscope") = ADS_SCOPE_SUBTREE 
objCommand.CommandText = _
    "SELECT Name, distinguishedName FROM 'LDAP://dc=fabrikam,dc=com' " _
        & "WHERE objectCategory='computer'"  
Set objRecordSet = objCommand.Execute
objRecordSet.MoveFirst
Do Until objRecordSet.EOF
    intTestOU = InStr(objRecordSet.Fields("distinguishedName").Value, "OU=Test Lab OU")
    If intTestOU = 0 Then
        Wscript.Echo objRecordSet.Fields("Name").Value
    End If
    objRecordSet.MoveNext
Loop

我們要特別注意的是下面這幾行程式碼:

intTestOU = InStr(objRecordSet.Fields("distinguishedName").Value, "OU=Test Lab OU")
If intTestOU = 0 Then
    Wscript.Echo objRecordSet.Fields("Name").Value
End If

我們在這裡要做的,就是用 InStr 函式檢查電腦的 DN 中是否有字串 OU=TestLab OU。如果有這個字串,intTestOU 便會設定成這個字串開始的字元位置。例如,在 DN CN=atl-ws-01、OU=Test LabOU、DC=fabrikam 以及 DC=com 中,intTestOu 會設定為 14,因為我們的目標子句是從字串中的「第 14 個」字母開始。

但是,萬一 intTestOU 等於零怎麼辦?這種情況只表示一件事情:找不到目標子句。換言之,也就表示這部特定電腦「不是」在「測試實驗室 OU」中,所以我們可以放心地呼叫這部電腦的名稱。如此一來,我們就可以呼叫所有電腦的名稱,但「排除」了「測試實驗室 OU」中的電腦。

就如同我先前說過的,這種方式可以完成工作,但是並不是很高明的辦法。不過,即使不高明卻仍然很好用,不是嗎?

或者,您也可以將 OU 名稱儲存在某個未使用的電腦帳戶屬性中;例如,您可以將 OU 名稱記錄在描述屬性中。這樣一來,您就「可以」利用類似下面的查詢搜尋這個屬性了:

objCommand.CommandText = _
    "SELECT Name FROM 'LDAP://dc=fabrikam,dc=com' WHERE objectCategory='computer' " & _
        "AND description<>'Test Lab OU'"

這種方式非常好用;不過萬一您要將電腦移到新的 OU,可別忘了變更這個描述屬性的數值。


如需詳細資訊

查看嗨,Scripting Guy!- 過往文件

 

回到頁首 回到頁首