简介:对域中的所有计算机运行脚本
一个脚本专家被经常问及的问题是:我如何对域中的所有计算机运行脚本?好的,如果你正在运行Windows PowerShell(我们假设你在阅读本文时肯定会这么做)下面就是你能够完成这个任务 ...
关键字:
Windows
PowerShell
脚本
对域中的所有计算机运行脚本
一个脚本专家被经常问及的问题是:我如何对域中的所有计算机运行脚本?好的,如果你正在运行Windows PowerShell(我们假设你在阅读本文时肯定会这么做)下面就是你能够完成这个任务的一个方法:
$strFilter = "computer"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.SearchScope = "Subtree"
$objSearcher.PageSize = 1000
$objSearcher.Filter = "(objectCategory=$strFilter)"
$colResults = $objSearcher.FindAll()
foreach ($i in $colResults)
{
$objComputer = $i.GetDirectoryEntry()
Get-WMIObject Win32_BIOS -computername $objComputer.Name
}
|
如你所见,我们在这里所做的是使用一个活动目录搜索脚本来返回一个域中所有计算机的集合;接着,我们调用Get-WMIObject cmdlet来提取其中每一台计算机的BIOS信息。在你自己尝试运行这个脚本前,需要记住的是这个脚本需要花上一段时间才能运行完毕;这是真的,如果
a) 你的域中很多计算机
b) 这些计算机中的一部分处于离线状态
理想上来说,你可以在尝试连接每一台计算机前使用ping来检测连通性。但这是另一个话题了。
现在,我们从将值“computer”赋值给名为$strFilter的变量开始说起。如果你在想这个值来源于何处,这仅仅是因为这是指定计算机的objectCategory属性的官方方法(这没什么好奇怪的)。如果你在想为什么我们需要这个值,好的,保持这个状态,我们将马上为你解释。
在赋值给$strFilter后,我们建立.NET Framework类 System.DirectoryServices.DirectoryEntry的一个新实例。(也许你感到疑惑,但是,当处理和Active Directory相关的问题时,我们确实使用.NET Framework。)因为我们不这样指定的话,我们的对象引用$objDomain会自动把我们连接到活动目录域的根。是的,我们认为那样也很酷。
从这里开始,我们创建了System.DirectoryServices.DirectorySearcher类的一个实例;这是我们用来处理活动目录搜索的对象。一旦得到了这个对象,我们就将值赋给DirectorySearcher的三个属性:
l SearchRoot. SearchRoot属性就是告诉DirectorySearcher从哪里开始搜索。我们想要搜索整个域所以我们将SearchRoot设置为对象引用$objDomain。
l SearchScope. 这决定我们的搜索深度。在本例中,我们不仅想要搜索域根目录而且也要搜索域根目录下的OU及容器(包括子OU及子容器)。因此我们指定SearchScope的值为Subtree。
l PageSize. 默认情况下一次活动目录搜索最大能返回1000个对象。这很好,除非碰巧在你的域中有1001台计算机。因为我们想要得到域中的所有计算机所以我们设置PageSize为1000.这意味着脚本将返回首先找到的1000个对象,然后再提取下1000个对象,然后这个过程直到最后一个计算机帐户被返回。
下面我们将一个值赋给一个多值属性,Filter:
$objSearcher.Filter = "(objectCategory=$strFilter)"
|
Filter属性被用来定位在活动目录中找到的对象的子集。因为我们所关心的只是计算机,我们告诉Filter属性只需带回objectCategory属性等于变量$strFilter(也就是等于computer)的对象即可。
然后,终于,我们调用FindAll方法来确实开始我们的搜索,最后符合搜索条件的计算机帐户的完整集合将被返回及储存在名为$colResults的变量中:
$colResults = $objSearcher.FindAll()
|
我们得到了我们所需要的数据了么?好的,我们需要注意,默认情况下,这个搜索返回我们的计算机帐户的所有属性值。如果你有很多计算机,这个脚本将返回超出你所需的数据,也就是说,将会阻塞网络并降低脚本的运行速度。因此,你想也许想要限定返回的属性值仅限于我们想要的:Name。但是这是我们将要在未来的提示中涉及的内容。
一旦得到所需的集合,我们就建立起一个foreach循环来遍历集合中的所有计算机。在这个循环内,我们使用这行代码及GetDirectoryEntry方法来为集合中的第一台计算机创建一个引用自活动目录账户的对象:
$objComputer = $i.GetDirectoryEntry()
|
然后我们就将Name属性的值传递给Get-WMIObject cmdlet及-computername参数:
Get-WMIObject Win32_BIOS -computername $objComputer.Name
|
接着我们简单地通过循环并对集合中的下一台计算机重复这个过程。无论相信与否,这就是我们要对域中所有计算机运行脚本所要做的。
对OU中的所有计算机运行脚本
你说的对:这很酷。然而,你可能还想要做的更好;很多时候你不想要对所有的计算机运行脚本,你只想要对特定OU中的计算机运行脚本。那么你打算怎么做呢?这确实问的不错,回答起来也比较费力。事实上,这有可能是VBScript比PowerShell优秀的一个领域;使用类似以下的VBScript代码你能返回OU中所有计算机的集合:
Set colItems = GetObject _
("LDAP://ou=Servers, dc=fabrikam, dc=com")
colItems.Filter = Array("Computer")
|
而使用PowerShell的话就没有那么简单。当你使用Windows PowerShell绑定一个OU时,你会得到关于OU本身的基本信息;而你不会得到储存在OU中对象的信息。为了得到这个信息你需要使用PSBase对象,然后使用一些编程技巧来限制计算机对象的信息。这能够完成么?当然,事实上,这里就有一种方法:
$objOU = [ADSI]"LDAP://OU=Workstations,DC=fabrikam,DC=com"
$colItems = $objOU.psbase.children
$colItems | ForEach-Object
{
if ($_.objectCategory -eq "CN=Computer,CN=Schema,CN=Configuration,DC=fabrikam,DC=com")
{Get-WMIObject Win32_BIOS -computername $_.Name}
}
|
那么这里发生了什么?好的,第一行我们使用标准的PowerShell语法来连接活动目录中的WorkstationsOU。注意“类型适配器”[ADSI];这是PowerShell内置的速记方法使得人们能够简单的连接到.NET Framework及DirectoryServices类。
第二行,我们引用PSBase对象来获得我们目标OU的“子对象”;如你所期待的,Children属性返回储存在OU中所有对象的集合。
|
注意 PSBase类是什么?好的,暂且不深入任何技术细节(主要是因为我们真的不知道所有的技术细节)PSBase类允许我们访问.NET Framework的基础类。如果我们用管道将变量$objOU传递给Get-Member cmdlet我们将不会得到太多东西。这是因为$objOU代表了实际OU的一个“适应视图”。PowerShell经常创建这些适应视图作为对用户表示多数实质信息的方法。如果你想要得到对象包含的所有信息你需要使用PSBase对象。
如同我们在脚本中所做的一样。
|
一旦我们有了在目标OU中所寻找到的所有对象集合时(名为$colItems的集合),我们就通过管道将信息传递至ForEach-Object cmdlet。正如名称所示,ForEach-Object提供给我们一个遍历集合中所有对象的方法,一个接一个。通过循环后我们判断objectCategory属性是否等于CN=Computer,CN=Schema,CN=Configuration,DC=fabrikam,DC=com(是的,我们能说的是objectCategory肯定是以这种方式表示。)如果符合的话,我们调用Get-WMIObject cmdlet,传递对象的Name属性值至-computername参数。如果objectCategory不等于CN=Computer等等。那么我们就返回至循环开始然后对集合中的下一个对象重复这个过程。
姑且承认,这有点麻烦,但是能正常工作。如果你知道更好的方法来从指定中提取计算机账户集合(并且只是计算机账户),那么,发邮件到 scripter@microsoft.com 。我们会很高兴收到你的来信的。
换一个视角来说,另一种处理这个问题的方法是对我们向您展示的搜索脚本(针对域内所有计算机的那个脚本)做两个小修改。做了这些修改之后,你的脚本最终将看起来像这样:
$strFilter = "computer"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = "LDAP://OU= Workstations,DC=fabrikam,DC=com"
$objSearcher.SearchScope = "Base"
$objSearcher.PageSize = 1000
$objSearcher.Filter = "(objectCategory=$strFilter)"
$colResults = $objSearcher.FindAll()
foreach ($i in $colResults)
{
$objComputer = $i.GetDirectoryEntry()
Get-WMIObject Win32_BIOS -computername $objComputer.Name
}
|
那么我们在这里做了什么修改?像我们所说的,我们做了两件事。首先,我们将SearchScope设置为Base。这告诉脚本值搜索目标OU并忽略任何子OU。(你想要搜索子OU?那么保留SearchScope的值为Subtree。)
其次,我们设置SearchRoot的属性值为目标OU的ADsPath:
$objSearch.SearchRoot = "LDAP://OU= Workstations,DC=fabrikam,DC=com"
|
为什么?啊,你走在我们前面了:是的,我们这么做是告诉脚本我们想要从Workstations OU开始搜索。
这个方法需要更多一点的代码,但是脚本将会完成的更快,尤其是如果在你的目标OU中有一整个分支机构的计算机。
|
注意 更快一点?一整个分支机构的计算机?这些意味着什么?好的,这很难说,特别是当涉及到你的网络速度,网络带宽及所有其它要素。然后,一次活动目录搜索总会在几秒内完成,然后你需要循环在目标OU中找到的对象子集。这个枚举脚本,作为对比,需要你费力地访问OU中的所有对象,从其它任何对象中分离出所有计算机对象。
|
对Excel表格中列出的所有计算机运行脚本
上周我们承诺我们将向你展示如何对Excel表格中列出的所有计算机运行脚本;当然,在实现我们的承诺之前我们花了很多时间来解释活动目录的搜索。因此我们决定调整下。我们将向你展示一个脚本,该脚本从一个表格中(C:\Scripts\Test.xls)读取所有计算机名,然后从这些计算机中的每一台上提取BIOS信息。然而我们不想做的是,确切解释该脚本是如何运行的。这是我们将来要做的。现在,我们给出这个脚本:
$a = New-Object -comobject Excel.Application
$a.Visible = $True
$b = $a.Workbooks.Open("C:\Scripts\Test.xls")
$c = $b.Worksheets.Item(1)
$i = 1
do
{
$d = $c.Cells.Item($i,1).Value()
if ($d -ne $null)
{Get-WMIObject Win32_BIOS -computername $d}
$i++
}
while ($d -ne $null)
|
只要确保你的计算机名在列A中被列出,比且在名字之间没有空行。这应当能帮助克服你所提到的问题直到我们有机会向你解释如何使用Windows PowerShell来同Excel一起协同工作。
这也意味着该是说再见的时候了。我们下周见。
英文原文
http://www.microsoft.com/technet/scriptcenter/resources/pstips/oct07/pstip1019.mspx
<欢迎投稿>
<论坛讨论>