我正在尝试创建一个脚本作为计划任务运行,该脚本将针对多台服务器运行并检索一些信息。
首先,我使用 Get-ADComputer 查询 AD 中符合特定条件的所有服务器,从而填充服务器列表。
问题是,列表作为对象返回,我无法将其传递给 New-PSSession 列表。我尝试通过执行以下操作将其转换为逗号分隔的字符串:
foreach ($server in $serverlist) {$newlist += $server.Name + ","}
但这仍然不起作用。
另一种方法是遍历列表并一次对每个服务器运行各种命令,但我的偏好是避免这样做并使用一对多远程处理来运行它们。
更新:为了澄清我最终想要做的事情是使用-ComputerName $serverlist
,所以我希望 $serverlist 是一个字符串而不是一个对象。
更新2:感谢大家的建议。在它们和我原来的方法之间,我开始怀疑是否-ComputerName
可以接受字符串变量?我成功地将计算机列表转换为逗号分隔的字符串,但无论我怎么做,我总是得到无效的网络地址。
答案1
进一步解释一下,如果你已经执行,Get-ADComputer
那么你可能会得到一个对象数组或一个对象。
我假设您正在调用以下内容:
$serverList = Get-ADComputer -LDAPFilter '(cn=*DC*)';
DC
这将获得所有以他们名义登记的计算机。
因为您可能有一个数组(不止一个)或对象,所以使用 cmdlet ForEach-Object
(也别名为%
或foreach
)进行处理是一种很好的方法。这基本上是对管道中的每个记录执行一个脚本块。但是,正如您所经历的,您从查询中得到的是一个对象,而不是字符串。所以您只需选择一个要使用的属性。
例如如果你使用:
$serverList[0] | Get-Member
您将获得计算机对象的所有属性。gm
也是的别名Get-Member
。例如
PS> $serverList[0] | gm
TypeName: Microsoft.ActiveDirectory.Management.ADComputer
Name MemberType Definition
---- ---------- ----------
Contains Method bool Contains(string propertyName)
Equals Method bool Equals(System.Object obj)
GetEnumerator Method System.Collections.IDictionaryEnumerator GetEnumerator()
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Item ParameterizedProperty Microsoft.ActiveDirectory.Management.ADPropertyValueCollection Item(string p...
DistinguishedName Property System.String DistinguishedName {get;set;}
DNSHostName Property System.String DNSHostName {get;set;}
Enabled Property System.Boolean Enabled {get;set;}
Name Property System.String Name {get;}
ObjectClass Property System.String ObjectClass {get;set;}
ObjectGUID Property System.Nullable`1[[System.Guid, mscorlib, Version=2.0.0.0, Culture=neutral, ...
SamAccountName Property System.String SamAccountName {get;set;}
SID Property System.Security.Principal.SecurityIdentifier SID {get;set;}
UserPrincipalName Property System.String UserPrincipalName {get;set;}
快速查看一下,您就会发现有一个 DNSHostName 属性。
因此,要执行命令,您可以使用以下命令:
$serverList = Get-ADComputer -LDAPFilter '(cn=*DC*)';
$serverList | ForEach-Object { New-PSSession -ComputerName $_.DNSHostName; }
但是我要指出的是,New-PSSession
除了创建与计算机的远程会话(必须启用 PowerShell Remoting 才能支持此功能)之外,该 cmdlet 不会执行太多操作。您需要使用该Invoke-Command
cmdlet 从脚本执行某些操作。
示例 1:
$serverList = Get-ADComputer -LDAPFilter '(cn=*DC*)';
$serverList | ForEach-Object { New-PSSession -ComputerName $_.DNSHostName; } | ForEach-Object { Invoke-Command -Session $_ -ScriptBlock { #Execute commands here }; };
示例 2:
Get-ADComputer -LDAPFilter '(cn=*DC*)' | % { Invoke-Command -ComputerName $_.DNSHostName -ScriptBlock { #Execute commands here }; };
第二个示例优化了,New-PSSession
因为如果您只是要调用Invoke-Command
没有高级选项的,那么它实际上并不是必需的。
还要注意,没有必要将结果存储在变量中,只有在执行时间很长并且需要多次使用它时才Get-ADComputer
会这样做。Get-ADComputer
是$_
一个管道变量,它成为当前管道中的任何对象。
更新
具体来说,要Invoke-Command
对多台计算机执行相同的命令,您可以使用参数-ComputerName
来提供一个字符串数组。(这可以通过查看帮助来了解,即-ComputerName <String[]>
。并非所有命令都是这种情况)。
如上所述,输出是一个对象。因此将构造Get-ADComputer
与参数兼容的字符串数组。-ComputerName
$serverList = @();
Get-ADComputer -LDAPFilter '(cn=*DC*)' | % { $serverList += $_.DNSHostName; }
基本上,这会创建一个新数组并将每台计算机的 DNS 主机名添加到其中,但最终会得到string[]
。然后您可以使用:
Invoke-Command -ComputerName $serverList -ScriptBlock { #Execute commands here };
不过还要注意,虽然表面上看起来您只执行了一条命令,但实际上它几乎总是多个 PowerShell cmdlet。例如,命令管道后面附加了一个静默| Out-Host
。
您还可以将任何命令包装在函数中以执行单个命令。
希望这能回答你的问题。
答案2
powershell 上的每个结果都是一个 .Net 对象。
当您在 powershell 中得到像您这样的结果 (一个对象) 并且需要一个字符串进行搜索或在另一个命令中使用时,您可以将结果 (对象) 通过管道传输到 cmdlet Out-String
。或者,您可以使用Out-File
。
如果您想要更改输出中的某些内容,您可以将其与其他 cmdlet(如 Format-*、、等)结合Format-Table
使用Format-List
。
因此,当你获取一个对象并且需要一个字符串时,请使用如下方法:
Command-Let | Out-String
或者
Command-Let | Format-Custom (parameters) | Out-String
答案3
我无法访问 AD cmdlet,因此只能在需要对象作为字符串时提供我以前做过的事情。
我在 MSSQLTips.com 上写了一篇关于使用 SQLPS 备份数据库的技巧。所以我基本上查询了 SQLPS 以获取数据库的名称,然后将其传递给命令BACKUP
,该命令必须将名称视为字符串,而不是对象类型。你可以看到它这里如果你想。
如果您将$serverlist
传递给Get-Member
,请查看是否有ToString()
方法。我认为此方法适用于大多数 System object 类型。如果您确实看到了它,那么您可以这样尝试您的命令:
$serverlist | foreach {$_.ToString()} | foreach {New-PSSession -computer $_}