Powershell使用Invoke-Command捕获返回值

要解决的问题:

我正在使用"调用命令"在远程计算机上执行脚本。

invoke-command -computername <server_name> -scriptblock {command to execute the script}

出现任何错误时,我的脚本将返回" -1"。 因此,我想通过检查返回代码来确保脚本已成功执行。

我尝试如下:

$result = invoke-command  -computername <server_name> -scriptblock { hostname }

但是它什么也没返回。

那么Invoke-command是否不捕获脚本块的返回码?

还有其他解决方法吗?

可以尝试的办法:

如果您在另一台服务器上以这种方式运行命令,则无法在该处获得脚本的返回代码。这是因为Invoke-Command可能仅在单个临时会话中在远程计算机上运行一个命令,而您无法再次连接到该会话。

但是,您可以做的是在远程计算机上创建一个会话并维护它,然后在该会话中调用脚本。之后,您可以再次检查该会话中的返回值。因此,遵循以下原则:

$mysession = New-PSSession -ComputerName <server_name>
Invoke-Command -Session $mysession -ScriptBlock { ... }
Invoke-Command -Session $mysession -ScriptBlock { $? }

例如:

$credential = Get-Credential
$mysession = New-PSSession -ComputerName "xiamingliangpc" -Credential $credential
Invoke-Command -Session $mysession -ScriptBlock { hostname }
Invoke-Command -Session $mysession -ScriptBlock { $? }

工作组环境的winRM使用

被远程主机开启winRM(工作组)

我们可以通过多种方式开启被远程主机的winRM,但这不是本文要讲述的重点,有兴趣的可以翻看我之前的一些文章;但本文尝试会详细讲解通过直接在被远程主机上执行开启winRM指令的方式开启并通过winRM远程管理被管理主机。

Tips:主机要开启winRM,方法是:

Enable-PSRemoting
Set-NetFirewallRule –Name "WINRM-HTTP-In-TCP-PUBLIC" –RemoteAddress Any
# 设置防火墙允许WINRM的访问

注意:"Run as administrator"

若您在开启过程中遇到下面的问题:

这种情况一般发生在计算机防火墙处于开启状态的情况

image-20220916134045524.png
WinRM-firewall-exception-will-not-work_thumb.png

您可能知道Windows有三种网络连接类型:私有、公共和域。当您第一次连接到网络时,Windows将询问连接类型。您可以在网络和共享中心中设置不同的网络发现规则、文件和打印机共享规则。

上面的错误消息表明,为了启用PowerShell Remoting,我们已经将网络设置为Public。有几种方法可以更改连接类型。出于只有微软知道的某种原因,您不能在网络和共享中心中这样做。对于我启用PowerShell Remoting的目标,Metro接口中的其他选项都不起作用。

只需要添加-SkipNetworkProfileCheck参数:

Enable-PSRemoting -SkipNetworkProfileCheck -Force</pre>
image-20220916134223240.png

通过以下指令查看防火强rule开启情况:

PS C:\WINDOWS\system32> Get-NetFirewallRule -Name "WINRM*"

Name                          : WINRM-HTTP-In-TCP-NoScope
DisplayName                   : Windows 远程管理(HTTP-In)
Description                   : 通过 WS-Management 实现的 Windows 远程管理的入站规则。[TCP 5985]
DisplayGroup                  : Windows 远程管理
Group                         : @FirewallAPI.dll,-30267
Enabled                       : True
Profile                       : Domain, Private
Platform                      : {}
Direction                     : Inbound
Action                        : Allow
EdgeTraversalPolicy           : Block
LooseSourceMapping            : False
LocalOnlyMapping              : False
Owner                         :
PrimaryStatus                 : OK
Status                        : 已从存储区成功分析规则。 (65536)
EnforcementStatus             : NotApplicable
PolicyStoreSource             : PersistentStore
PolicyStoreSourceType         : Local
RemoteDynamicKeywordAddresses :

Name                          : WINRM-HTTP-In-TCP
DisplayName                   : Windows 远程管理(HTTP-In)
Description                   : 通过 WS-Management 实现的 Windows 远程管理的入站规则。[TCP 5985]
DisplayGroup                  : Windows 远程管理
Group                         : @FirewallAPI.dll,-30267
Enabled                       : True
Profile                       : Public
Platform                      : {}
Direction                     : Inbound
Action                        : Allow
EdgeTraversalPolicy           : Block
LooseSourceMapping            : False
LocalOnlyMapping              : False
Owner                         :
PrimaryStatus                 : OK
Status                        : 已从存储区成功分析规则。 (65536)
EnforcementStatus             : NotApplicable
PolicyStoreSource             : PersistentStore
PolicyStoreSourceType         : Local
RemoteDynamicKeywordAddresses :

Name                          : WINRM-HTTP-Compat-In-TCP-NoScope
DisplayName                   : Windows 远程管理 - 兼容模式(HTTP-In)
Description                   : 通过 WS-Management 实现的 Windows 远程管理的兼容模式入站规则。[TCP 80]
DisplayGroup                  : Windows 远程管理(兼容性)
Group                         : @FirewallAPI.dll,-30252
Enabled                       : False
Profile                       : Domain
Platform                      : {}
Direction                     : Inbound
Action                        : Allow
EdgeTraversalPolicy           : Block
LooseSourceMapping            : False
LocalOnlyMapping              : False
Owner                         :
PrimaryStatus                 : OK
Status                        : 已从存储区成功分析规则。 (65536)
EnforcementStatus             : NotApplicable
PolicyStoreSource             : PersistentStore
PolicyStoreSourceType         : Local
RemoteDynamicKeywordAddresses :

Name                          : WINRM-HTTP-Compat-In-TCP
DisplayName                   : Windows 远程管理 - 兼容模式(HTTP-In)
Description                   : 通过 WS-Management 实现的 Windows 远程管理的兼容模式入站规则。[TCP 80]
DisplayGroup                  : Windows 远程管理(兼容性)
Group                         : @FirewallAPI.dll,-30252
Enabled                       : False
Profile                       : Private, Public
Platform                      : {}
Direction                     : Inbound
Action                        : Allow
EdgeTraversalPolicy           : Block
LooseSourceMapping            : False
LocalOnlyMapping              : False
Owner                         :
PrimaryStatus                 : OK
Status                        : 已从存储区成功分析规则。 (65536)
EnforcementStatus             : NotApplicable
PolicyStoreSource             : PersistentStore
PolicyStoreSourceType         : Local
RemoteDynamicKeywordAddresses :



PS C:\WINDOWS\system32>

确认Action是Allow状态。

在远程主机上测试被远程主机的winRM开启状态

PS C:\Users\Administrator> Test-WSMan "xiamingliangpc"


wsmid           : http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd
ProtocolVersion : http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
ProductVendor   : Microsoft Corporation
ProductVersion  : OS: 0.0.0 SP: 0.0 Stack: 3.0



PS C:\Users\Administrator>

以上显示代表对方已开启winRM功能。

如果远程的计算机没有加入域

在工作组计算机上,事情稍微复杂一些,在此过程中您可能会遇到一些额外的设置。

原因是启用PowerShell Remoting是一个安全风险,因为坏人肯定喜欢在您的计算机上远程自动化他们的黑客活动。因此,您必须设置一些额外的设定。

在Active Directory环境中,您可以只使用计算机名连接到远程计算机。如果远程连接到一台独立机器,则通常必须使用IP地址。如果您试图使用远程计算机的IP地址使用Enter-PSSession或者New-PSSession cmdlet连接到远程计算机,PowerShell将抛出以下错误:

注意:即使只要被远程主机开启的了winRM并设置好防火墙允许策略,Test-WSMan指令即可正常使用且不会报错;但实际能不能使用还得使用New-PSSession指令验证。

PS C:\Users\Administrator> $mysession = New-PSSession -ComputerName "xiamingliangpc" -Credential $credential
New-PSSession : [xiamingliangpc] 连接到远程服务器 xiamingliangpc 失败,并显示以下错误消息: WinRM 客户端无法处理该请求。如果身份
验证方案与 Kerberos 不同,或者客户端计算机未加入到域中, 则必须使用 HTTPS 传输或者必须将目标计算机添加到 TrustedHosts
配置设置。 使用 winrm.cmd 配置 TrustedHosts。请注意,TrustedHosts 列表中的计算机可能未经过身份验证。 通过运行以下命令可
获得有关此内容的更多信息: winrm help config。 有关详细信息,请参阅 about_Remote_Troubleshooting 帮助主题。
所在位置 行:1 字符: 14
+ ... mysession = New-PSSession -ComputerName "xiamingliangpc" -Credential $cre ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + CategoryInfo          : OpenError: (System.Manageme....RemoteRunspace:RemoteRunspace) [New-PSSession], PSRemotin
 gTransportException
 + FullyQualifiedErrorId : ServerNotTrusted,PSSessionOpenFailed
PS C:\Users\Administrator>

这种情况下,你需要修改远程主机的winrm设定;显示地允许管理被远程主机。(这里一定要注意是修改远程主机而不是被远程主机~~~)

我们首先检查下远程主机默认Enable-PSRemoting后;winrm的默认设定是什么。

PS C:\WINDOWS\system32> hostname
PCA001
PS C:\WINDOWS\system32> winrm get winrm/config/client
Client
 NetworkDelayms = 5000
 URLPrefix = wsman
 AllowUnencrypted = false
 Auth
 Basic = true
 Digest = true
 Kerberos = true
 Negotiate = true
 Certificate = true
 CredSSP = false
 DefaultPorts
 HTTP = 5985
 HTTPS = 5986
 TrustedHosts = 

PS C:\WINDOWS\system32>

显示地设定TrustedHosts

然后我们显示地设定TrustedHosts;设置的方式有两种:

1.老式的命令

winrm set winrm/config/client @{TrustedHosts="192.168.11.199"}
# "192.168.11.149"是我用来远程管理xiamingliangpc机器的主机IP

2.powershell样式的命令

Set-Item WSMan:\localhost\Client\TrustedHosts -Value "192.168.11.149" -Force
# "192.168.11.149"是我用来远程管理xiamingliangpc机器的主机IP

以上两种方法任选其一即可;我个人偏向使用powershell样式的命令。

PS C:\WINDOWS\system32> Set-Item WSMan:\localhost\Client\TrustedHosts -Value "192.168.11.149" -Force
PS C:\WINDOWS\system32> winrm get winrm/config/client
Client
 NetworkDelayms = 5000
 URLPrefix = wsman
 AllowUnencrypted = false
 Auth
 Basic = true
 Digest = true
 Kerberos = true
 Negotiate = true
 Certificate = true
 CredSSP = false
 DefaultPorts
 HTTP = 5985
 HTTPS = 5986
 TrustedHosts = 192.168.11.199

PS C:\WINDOWS\system32> hostname
PCA001
PS C:\WINDOWS\system32>

# 或者也可以通过以下命令查看生效情况
PS C:\WINDOWS\system32> Get-Item WSMan:\localhost\Client\TrustedHosts


 WSManConfig:Microsoft.WSMan.Management\WSMan::localhost\Client

Type            Name                           SourceOfValue   Value
----            ----                           -------------   -----
System.String   TrustedHosts                                   192.168.11.199


PS C:\WINDOWS\system32>

若您需要将winRM的TrustedHosts设置为*时,您可以使用下面的指令:

Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" -Force

只能使用被远程主机的IP地址进行访问

完成以上设定,我们在远程主机上只能通过IP地址访问被远程主机:

PS C:\Users\Administrator> $mysession = New-PSSession -ComputerName "192.168.11.199" -Credential $credential
PS C:\Users\Administrator> Get-PSSession

 Id Name            ComputerName    ComputerType    State         ConfigurationName     Availability
 -- ----            ------------    ------------    -----         -----------------     ------------
 17 WinRM17         192.168.11.199  RemoteMachine   Opened        Microsoft.PowerShell     Available


PS C:\Users\Administrator> Get-PSSession | Remove-PSSession
PS C:\Users\Administrator> Get-PSSession
PS C:\Users\Administrator>
PS C:\Users\Administrator>
PS C:\Users\Administrator>
PS C:\Users\Administrator> $mysession = New-PSSession -ComputerName "xiamingliangpc" -Credential $credential
New-PSSession : [xiamingliangpc] 连接到远程服务器 xiamingliangpc 失败,并显示以下错误消息: WinRM 客户端无法处理该请求。如果身份
验证方案与 Kerberos 不同,或者客户端计算机未加入到域中, 则必须使用 HTTPS 传输或者必须将目标计算机添加到 TrustedHosts
配置设置。 使用 winrm.cmd 配置 TrustedHosts。请注意,TrustedHosts 列表中的计算机可能未经过身份验证。 通过运行以下命令可
获得有关此内容的更多信息: winrm help config。 有关详细信息,请参阅 about_Remote_Troubleshooting 帮助主题。
所在位置 行:1 字符: 14
+ ... mysession = New-PSSession -ComputerName "xiamingliangpc" -Credential $cre ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + CategoryInfo          : OpenError: (System.Manageme....RemoteRunspace:RemoteRunspace) [New-PSSession], PSRemotin
 gTransportException
 + FullyQualifiedErrorId : ServerNotTrusted,PSSessionOpenFailed
PS C:\Users\Administrator>

通过主机名或者IP均可访问的设定

提供一个逗号分隔的单个计算机名字符串

Set-Item WSMan:\localhost\Client\TrustedHosts -Value 'machineA,machineB'

或(危险)通配符

Set-Item WSMan:\localhost\Client\TrustedHosts -Value '*'

追加到列表中,-Concatenate可以使用参数

Set-Item WSMan:\localhost\Client\TrustedHosts -Value 'machineC' -Concatenate

我们这里追加主机名到TrustedHosts

PS C:\WINDOWS\system32> Set-Item WSMan:\localhost\Client\TrustedHosts -Value 'xiamingliangpc' -Concatenate

WinRM 安全配置。
此命令修改 WinRM 客户端的 TrustedHosts 列表。TrustedHosts
列表中的计算机可能不会经过身份验证。该客户端可能会向这些计算机发送凭据信息。是否确实要修改此列表?
[Y] 是(Y)  [N] 否(N)  [S] 暂停(S)  [?] 帮助 (默认值为“Y”): y
PS C:\WINDOWS\system32>
PS C:\WINDOWS\system32>

查看设定结果:

PS C:\WINDOWS\system32> winrm get winrm/config/client
Client
 NetworkDelayms = 5000
 URLPrefix = wsman
 AllowUnencrypted = false
 Auth
 Basic = true
 Digest = true
 Kerberos = true
 Negotiate = true
 Certificate = true
 CredSSP = false
 DefaultPorts
 HTTP = 5985
 HTTPS = 5986
 TrustedHosts = 192.168.11.199,xiamingliangpc

PS C:\WINDOWS\system32> Get-Item WSMan:\localhost\Client\TrustedHosts


 WSManConfig:Microsoft.WSMan.Management\WSMan::localhost\Client

Type            Name                           SourceOfValue   Value
----            ----                           -------------   -----
System.String   TrustedHosts                                   192.168.11.199,xiamingliangpc


PS C:\WINDOWS\system32>

尝试通过主机名远程访问被管理主机

PS C:\Users\Administrator> $mysession = New-PSSession -ComputerName "xiamingliangpc" -Credential $credential
PS C:\Users\Administrator> Get-PSSession

 Id Name            ComputerName    ComputerType    State         ConfigurationName     Availability
 -- ----            ------------    ------------    -----         -----------------     ------------
 19 WinRM19         xiamingliangpc      RemoteMachine   Opened        Microsoft.PowerShell     Available


PS C:\Users\Administrator> Get-PSSession | Remove-PSSession
PS C:\Users\Administrator>

尝试远程执行指令并捕获返回值

# 1.建立session
PS C:\Users\Administrator> $mysession = New-PSSession -ComputerName "xiamingliangpc" -Credential $credential
# 2.在创建的session中远程执行指令
PS C:\Users\Administrator> Invoke-Command -Session $mysession -ScriptBlock { hostname }
xiamingliangpc
# 3.判定指定的执行成功与否
PS C:\Users\Administrator> Invoke-Command -Session $mysession -ScriptBlock { $? }
True
# 4.一个实际的例子:获取远程主机的最新10条system日志
PS C:\Users\Administrator> Invoke-Command -Session $mysession -ScriptBlock { Get-EventLog -LogName System -Newest 10 }

 Index Time          EntryType   Source                 InstanceID Message                   PSComputerName
 ----- ----          ---------   ------                 ---------- -------                   --------------
 74990 9月 16 14:11  Warning     DCOM                        10016 无法找到源“DCOM”中事... xiamingliangpc
 74989 9月 16 14:09  Warning     DCOM                        10016 无法找到源“DCOM”中事... xiamingliangpc
 74988 9月 16 13:43  Information Microsoft-Windows...           15 无法找到源“Microsoft-... xiamingliangpc
 74987 9月 16 13:43  Information Microsoft-Windows...           16 无法找到源“Microsoft-... xiamingliangpc
 74986 9月 16 13:43  Information Microsoft-Windows...           16 无法找到源“Microsoft-... xiamingliangpc
 74985 9月 16 13:43  Information Microsoft-Windows...           16 无法找到源“Microsoft-... xiamingliangpc
 74984 9月 16 13:43  Information Microsoft-Windows...           16 无法找到源“Microsoft-... xiamingliangpc
 74983 9月 16 13:43  Information Microsoft-Windows...           16 无法找到源“Microsoft-... xiamingliangpc
 74982 9月 16 13:43  Information Microsoft-Windows...           15 无法找到源“Microsoft-... xiamingliangpc
 74981 9月 16 13:42  Information Microsoft-Windows...           11 无法找到源“Microsoft-... xiamingliangpc

# 5.一个实际的例子:获取远程主机的最新10条system日志并将结果存放在本机变量中
PS C:\Users\Administrator> $result = Invoke-Command -Session $mysession -ScriptBlock { Get-EventLog -LogName System -Newest 10 }
PS C:\Users\Administrator> $result[0]

 Index Time          EntryType   Source                 InstanceID Message                   PSComputerName
 ----- ----          ---------   ------                 ---------- -------                   --------------
 74990 9月 16 14:11  Warning     DCOM                        10016 无法找到源“DCOM”中事... xiamingliangpc


PS C:\Users\Administrator>

# 6.用完的session注意要关闭
PS C:\Users\Administrator> $mysession | Remove-PSSession
PS C:\Users\Administrator>

本文到这里就结束了,做个总结。

总结

想要在远程执行powershell的情况下获得远程指令的返回值,我们需要:

1.管理主机和被管理主机都要开启(工作组环境)winRM

2.在管理主机上添加被管理主机的IP或者计算机名到winRM的TrustedHosts中

3.建立PSSession

4.在建立的PSSession中Invoke-Command

5.记得在PSSession使用完成后关闭PSSession

希望大家本文对大家有所帮助。

KeyWord:Powershell,WSman,PSSession,PSremoting,夏明亮

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容