远程计算机上的 Restart-Service 结果为 NoServiceFoundForGivenName,但 Get-Service 有效

Restart-Service on remote computer results in NoServiceFoundForGivenName, but Get-Service works

我正在尝试在所有 SQL 服务器实例上远程重启 SQL 代理服务。我 运行 来自我的一个开发服务器的以下脚本同时登录到一个帐户,该帐户对 CSV 文件中列出的每个服务器都具有本地管理员权限。该文件包含我所有 SQL 实例的 HostName 和 InstanceName,包括此开发服务器和远程服务器。所有都是 Windows Server 2008R2 或 2012,运行ning SQL Server 2008R2 或 2012。PowerShell 正在 运行 作为管理员。

$Servers = Import-CSV C:\Temp\InstanceList.csv
ForEach ($Line In $Servers)
{
  $sqlHostName = $Line.hostName
  $sqlInstanceName = if ($Line.instanceName -eq "") {$Line.instanceName} else {("$" + $Line.instanceName)}
  $serviceName = if ($sqlInstanceName -eq "") {"SQLSERVERAGENT"} else {"SQLAgent" + $sqlInstanceName}
  Get-Service -computer $sqlHostName ($serviceName)
  Get-Service -computer $sqlHostName ($serviceName) | Restart-Service
}

它在本地机器的实例上成功,但在远程实例上失败。这是我看到的远程默认实例的错误。第一行显示我有正确的服务名称,下一行声称它不存在:

Running  SQLSERVERAGENT     SQL Server Agent (MSSQLSERVER)
Restart-Service : Cannot find any service with service name 'SQLSERVERAGENT'.
At line:11 char:70
+   Get-Service -computer $sqlHostName ($serviceName) | Restart-Service <<<<
    + CategoryInfo          : ObjectNotFound: (SQLSERVERAGENT:String) [Restart-Service], ServiceCommandException
    + FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.RestartServiceCommand

启用远程处理之前已在每台服务器上 运行 本地。

为什么Get-Service可以显示服务名称,但是Restart-Service却声称该服务不存在。感觉应该是权限问题,但是不知道怎么解决。有什么想法吗?

那是因为 Get-Service 还在本地 运行ning;它不知道管道中先前命令的 -Computer 参数,并且它不像 Get-Service 那样支持 -Computer 。要将 Restart-Service 用于远程服务,您有两种选择:

选项 1:使用 Enter-PSSession

您可以使用 Enter-PSSession 在相关计算机上进入远程 PowerShell 会话。当你这样做时,它实际上会在远程计算机上打开一个 shell,你给它的任何命令都会 运行。 Enter-PSSession 还支持 -Credential 参数,因此如果您在本地 运行ning 的帐户不是在远程计算机上具有权限的管理帐户,您可以先创建一个凭据对象,然后再使用它。代码可能看起来像这样:

$Credential = Get-Credential
Enter-PSSession -ComputerName $ComputerName -Credential $Credential

当您 运行 这样做时,您的提示会改变:它会在方括号中添加远程计算机的名称(让您知道您正在对不同的主机执行命令)。它也可能默认为远程主机上的 users\documents 文件夹,如下所示:

[ComputerName]: PS C:\Users\Username\Documents>

现在,您 运行 的任何命令都会 运行 在目标计算机上。您将不再需要 Get-Service 上的 -Computer。所以你可以 运行:

Get-Service ($serviceName) | Restart-Service

只需确保完成后,使用 Exit-PSSession return 到本地提示。

选项 2:使用 Invoke-Command

另一种选择(可能更适合您正在执行的自动化)是使用 Invoke-Command。 Invoke-Command 支持 -Credential 和 -ComputerName 参数,以及 -ScriptBlock 参数。 ScriptBlock 很简洁,因为它允许您轻松传递和修改具有自己参数的代码。所以在你上面的例子中,你可以这样做:

$ScriptBlock = {Get-Service | Where-Object {$_.Name -like "SQL*"} | Restart-Service}
Invoke-Command -ComputerName $ComputerName -ScriptBlock $ScriptBlock

根据您的尝试,我会像这样重写您的脚本:

$Servers = Import-CSV C:\Temp\InstanceList.csv
$ScriptBlock = {param([string] $RemoteServiceName)  Get-Service | Where-Object {$_.Name -eq $RemoteServiceName} | Restart-Service}

ForEach ($Line In $Servers)
{
  $sqlHostName = $Line.hostName
  $sqlInstanceName = if ($Line.instanceName -eq "") {$Line.instanceName} else {("$" + $Line.instanceName)}
  $serviceName = if ($sqlInstanceName -eq "") {"SQLSERVERAGENT"} else {"SQLAgent" + $sqlInstanceName}
  Invoke-Command -ComputerName $sqlHostName -ScriptBlock $ScriptBlock -ArgumentList $serviceName
}

我正在使用您的循环逻辑,但我正在使用您从 .CSV 文件中获取的变量来指定我希望 Invoke-Command 到 运行 的 -ComputerName,并且我正在使用 ScriptBlock使用 ArgumentList 和 Parameters 提供最终获得 运行.

的值

如果您想了解更多关于几个概念的信息,这里有一些链接:

  1. 脚本块信息:https://technet.microsoft.com/en-us/library/hh847893.aspx
  2. 调用命令:https://technet.microsoft.com/en-us/library/hh849719.aspx

选项 3:在对象上使用方法

当然,不需要那么难。您可以使用 Get-Service 获取对变量的服务,然后对该对象使用 .Restart() 方法。虽然您无法将对象传递给 Restart-Service,但您 可以 只需调用该方法,它就会重新启动该服务,甚至是远程启动:

$Servers = Import-CSV C:\Temp\InstanceList.csv
ForEach ($Line In $Servers)
{
    $sqlHostName = $Line.hostName
    $sqlInstanceName = if ($Line.instanceName -eq "") {$Line.instanceName} else {("$" + $Line.instanceName)}
    $serviceName = if ($sqlInstanceName -eq "") {"SQLSERVERAGENT"} else {"SQLAgent" + $sqlInstanceName}
    $Service = Get-Service -computer $sqlHostName ($serviceName)
    $Service.Restart()
}

这是 PowerShell 的一大优点。 None 这些答案是 "wrong",因为您可以使用其中任何一个;它们都是潜在的解决方案,我相信还有更多的解决方案。