我有两台服务器。服务器 A 需要联系服务器 B 并使用我创建的 powershell 函数库(只是一个 .ps1 文件)运行特定函数。被调用的函数在执行过程中还会调用其他函数,所有这些函数都包含在同一个 .ps1 文件中。最后,当服务器 A 调用驻留在服务器 B 上的函数时,他还需要向该函数传递 5 个参数。
使用 New-PSSession、Enter-PSSession 或仅 Invoke-Command 的几种组合,我能够成功调用服务器 B 函数;然而,我遇到了困难。
如果我事先在服务器 A 上执行 Import-Module 并导入我试图从中调用该函数的 .ps1 文件的副本,我才能成功让服务器 B 函数提示我输入它期望的参数。否则,无法找到或识别服务器 B 上的远程函数。
如果我不导入模块,当我在服务器上调用该函数时,BI 可以让它执行简单的操作,例如 echo 或 Write-Output,但无法识别函数。
最后,基于我可以导入模块并成功让函数提示我输入的情况,输入后,服务器 B 上的基本函数开始执行,但是当调用其中的子函数、更新日志等时,它会失败,表明它无法识别它们。同样,它们都包含在同一个 .ps1 文件中。
以下是一些代码示例,概述了我正在尝试做的事情:
示例 1
以下命令在服务器A上运行:
Invoke-Command -ScriptBlock { \\SERVERB\c$\orchestration\scripts\IvantiSyncEngine_RemotePatchRequest.ps1 }
以下是我们在上面的脚本中调用的服务器 B 上的目标 IvantiSyncEngine_RemotePatchRequest.ps1 文件:
# IvantiSyncEngine_RemotePatchRequest.ps1
Write-Output "Here we go!"
服务器A上的输出与预期一致:
PS C:\> Invoke-Command -ScriptBlock { \\SERVERB\c$\orchestration\scripts\IvantiSyncEngine_RemotePatchRequest.ps1 }
Here we go!
示例 2
接下来,我扩展了服务器 B 上的脚本,如下所示:
# IvantiSyncEngine_RemotePatchRequest.ps1
Write-Output "Here we go!"
Request-RemotePatch
被调用的函数也存在于服务器 B 上,位于单独的 .ps1 文件中。该函数如下所示:
function Request-RemotePatch {
Param (
# Define the parameters this function accepts
[Parameter(Mandatory=$true, Position=0)]
[string] $PatchHostname,
[Parameter(Mandatory=$true, Position=1)]
[string] $PatchDomain,
[Parameter(Mandatory=$true, Position=2)]
[string] $PatchRebootType,
[Parameter(Mandatory=$true, Position=3)]
[string] $RequesterHostname,
[Parameter(Mandatory=$true, Position=4)]
[string] $RequesterUsername
)
Begin{
}Process{
Write-Output "Testing"
# Empty line for spacing
Update-Log $global:RemotePatchLog "`n"
# Log to RemoteRequests log file
$MixedMessage = "Request received from $RequesterHostname\$RequesterUsername to remotely patch $PatchHostname, on domain $PatchDomain, with reboot type $PatchRebootType."
Update-Log $global:RemotePatchLog $MixedMessage
# Initialize the scan
Update-Log $global:RemotePatchLog "Scanning..."
$scan = Start-PatchScan -Name "RemoteAPI-Scan" -EndpointNames $PatchHostname -TemplateName "Security Patch Scan"
# Wait for the scan to complete and return details
$scan | Watch-PatchScan
Update-Log $global:RemotePatchLog "Scanning complete."
# Deploy against that scan
Update-Log $global:RemotePatchLog "Deploying Patches..."
$DeployTemplateName = "RemoteAPI-" + $PatchRebootType
$deploy = Start-PatchDeploy -ScanUid ($scan.Uid) -TemplateName $DeployTemplateName
# Wait for deploy to complete and return details
$deploy | Watch-PatchDeploy
Update-Log $global:RemotePatchLog "Deployment complete."
}
End{}
}
如您所见,它接受 5 个参数。它还会调用同一 .ps1 文件中的其他函数,主要用于日志记录等操作。
最后,当我尝试从服务器 A 进行相同的调用时,我得到了以下信息:
PS C:\> Invoke-Command -ScriptBlock { \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_RemotePatchRequest.ps1 }
Here we go!
cmdlet Request-RemotePatch at command pipeline position 1
Supply values for the following parameters:
PatchHostname:
所以到目前为止,一切都按预期进行。但是,我目前没有将参数传递给我想要修复的远程函数。但现在为了说明发生了什么,我只需在提供的提示中输入值即可。这是我按 Enter 键执行之前的最终设置:
PS C:\> Invoke-Command -ScriptBlock { \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_RemotePatchRequest.ps1 }
Here we go!
cmdlet Request-RemotePatch at command pipeline position 1
Supply values for the following parameters:
PatchHostname: kam1oqapp161
PatchDomain: connex
PatchRebootType: NoReboot
RequesterHostname: kam1oqweb163
RequesterUsername: Joey
点击执行后,我们得到的结果如下:
Out-File : Could not find a part of the path 'C:\Orchestration\Logs\RemotePatch.log'.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:326 char:9
+ Out-File -filePath $TheLogFile -InputObject $TheLogPayload -Append
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (:) [Out-File], DirectoryNotFoundException
+ FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand
Request received from kam1oqweb163\Joey to remotely patch kam1oqapp161, on domain connex, with reboot type NoReboot.
Out-File : Could not find a part of the path 'C:\Orchestration\Logs\RemotePatch.log'.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:326 char:9
+ Out-File -filePath $TheLogFile -InputObject $TheLogPayload -Append
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (:) [Out-File], DirectoryNotFoundException
+ FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand
Scanning...
Out-File : Could not find a part of the path 'C:\Orchestration\Logs\RemotePatch.log'.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:326 char:9
+ Out-File -filePath $TheLogFile -InputObject $TheLogPayload -Append
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (:) [Out-File], DirectoryNotFoundException
+ FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand
Start-PatchScan : The term 'Start-PatchScan' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:675 char:17
+ $scan = Start-PatchScan -Name "RemoteAPI-Scan" -EndpointNames $PatchHost ...
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Start-PatchScan:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Watch-PatchScan : The term 'Watch-PatchScan' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:678 char:17
+ $scan | Watch-PatchScan
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Watch-PatchScan:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Scanning complete.
Out-File : Could not find a part of the path 'C:\Orchestration\Logs\RemotePatch.log'.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:326 char:9
+ Out-File -filePath $TheLogFile -InputObject $TheLogPayload -Append
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (:) [Out-File], DirectoryNotFoundException
+ FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand
Deploying Patches...
Out-File : Could not find a part of the path 'C:\Orchestration\Logs\RemotePatch.log'.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:326 char:9
+ Out-File -filePath $TheLogFile -InputObject $TheLogPayload -Append
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (:) [Out-File], DirectoryNotFoundException
+ FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand
Start-PatchDeploy : The term 'Start-PatchDeploy' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the
path is correct and try again.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:684 char:19
+ $deploy = Start-PatchDeploy -ScanUid ($scan.Uid) -TemplateName $DeployTe ...
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Start-PatchDeploy:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Watch-PatchDeploy : The term 'Watch-PatchDeploy' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the
path is correct and try again.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:687 char:19
+ $deploy | Watch-PatchDeploy
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Watch-PatchDeploy:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Deployment complete.
Out-File : Could not find a part of the path 'C:\Orchestration\Logs\RemotePatch.log'.
At \\kam1osapp56\c$\orchestration\scripts\IvantiSyncEngine_Functions.ps1:326 char:9
+ Out-File -filePath $TheLogFile -InputObject $TheLogPayload -Append
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (:) [Out-File], DirectoryNotFoundException
+ FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand
我就到此为止。代码引用不会保留颜色,但这里有很多红色。错误主要围绕 2 个问题。(1) 无法找到文件夹引用和位置,以及 (2) 无法识别正在调用的其他函数和命令。
问题 1: 为什么引用远程文件夹位置的远程函数很难找到这些位置?我只从服务器 A 调用启动函数,它从服务器 B 启动所有其他函数。所有这些位置引用难道不应该相关吗,因为它们是从服务器 B 调用的?我假设如果我将所有远程引用从“C:...”更改为“\ServerB\C$...”,它就会解决问题,我只是不确定为什么有必要这样做?
问题2 对于无法识别的函数和命令,最佳实践解决方案是什么?需要导入模块等吗?
当我们充实这些部分时,我会继续添加更多内容,因为这些都是我所关注的内容。
更新日期:2018 年 5 月 18 日
好吧,我已经部分解决了上面的 (1) 和 (2)。以下是我所做的。
我将正在使用的函数 .ps1 文件转换为 .psm1 文件,创建了正确的清单,在引用 PS 模块时设置了正确的文件夹结构,并在从远程服务器调用时执行了正确的模块安装/导入。完成所有这些操作会将所有必要的函数加载到服务器 (A) 上的本地空间中,从而消除有关无法识别的引用或函数的任何错误。执行导入时,我使用的是 UNC 路径。新代码如下:
$s = New-PSSession -ComputerName 'SERVERB.DOMAINX.DOMAINY.com'
Invoke-Command -ScriptBlock {Import-Module STProtect -PassThru} -session $s
Invoke-Command -ScriptBlock {Import-Module \\SERVERB\c$\orchestration\scripts\PSModules\IvantiSyncEngine_Functions -DisableNameChecking -PassThru} -session $s
Invoke-Command -ScriptBlock ${function:Request-RemotePatch} -ArgumentList 'TARGETSERVERTOPATCH','TARGETDOMAIN','REBOOTTYPE','MYPCNAME','MYUSER' -session $s
Remove-PSSession -session $s
Exit-PSSession
如果您查看上面的代码,您还会注意到我使用 -ArgumentList 将变量传递给我们远程调用的函数取得了一些成功。现在,当我从服务器 A 调用服务器 B 上的远程函数时,我收到了所有正确的提示,但现在失败了,并显示以下错误消息:
所以...
问题 3
似乎有些东西没有在听,但我还没弄清楚到底是什么。有什么想法吗?