我有一些用于设置 IIS Web 应用程序、消息队列等的 Powershell 脚本。
它们使用我们创建的一组共享函数库 - 因此每个脚本都以该行开头
. .\common.ps1
引用共享库。共享库包含一组函数Create-IisApplicationPool
,如Create-MessageQueue
、 等,然后从实际脚本中调用它们。这些脚本的问题在于您需要通过远程桌面登录并在本地运行它们,因此我正在编写一些新脚本,用于将代码部署到 Amazon EC2 实例,并使用 Powershell 远程处理在本地调用它们。
我无法弄清楚如何使这个共享库在远程 Powershell 会话中可用。
这是一个简单的例子:
函数.ps1
function Add-Title([string] $name) {
"Mr. $name"
}
function Say-HelloWorld([string] $name = "World") {
$computerName = $env:COMPUTERNAME
$greeting = Add-Title($name)
Write-Host "Hello, $greeting ($computerName)"
}
例子.ps1
. ./functions.ps1
$remoteUsername = "username"
$remotePassword = "password"
$remoteHostname = "172.16.0.100"
$securePassword = ConvertTo-SecureString -AsPlainText -Force $remotePassword
$cred = New-Object System.Management.Automation.PSCredential $remoteUsername, $securePassword
Say-HelloWorld("Spolsky")
在本地运行时,效果很好 - 并且按预期显示“您好,Spolsky 先生(DYLAN_PC)”。
现在,如果我Say-HelloWorld
用这个远程脚本调用替换该调用:
Invoke-Command -computerName $remoteHostname -Credential $cred -ScriptBlock {
Say-HelloWorld("Spolsky")
}
我收到 Powershell 错误:
The term 'Say-HelloWorld' 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 co
rrect and try again.
+ CategoryInfo : ObjectNotFound: (Say-HelloWorld:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
显然,远程会话无法看到本地已导入的函数。
对于简单的函数,此语法有效:
Invoke-Command -computerName $remoteHostname -Credential $cred -ScriptBlock ${function:Add-Title } -argumentlist "Spolsky"
但对于任何依赖于其他函数的函数来说,这都会失败。
我尝试了各种方法,使用 PS-ExportSession 并尝试将-Session
参数传递给 Invoke-Command,但找不到任何方法在可以导入远程会话的模块中捕获本地函数及其依赖项。任何帮助都非常感谢!
答案1
这是一个有点老的话题,但所有的答案都比这更迂回。
Import-Module .\Common.ps1 -Force
# Now you can call common functions locally
$commonFunctions = (Get-Command .\Common.ps1).ScriptContents
Invoke-Command -Session $session -ArgumentList $commonFunctions -ScriptBlock {
param($commonFunctions)
Invoke-Expression $commonFunctions
# Now you can call common functions on the remote computer
}
答案2
您说每个脚本都以 开头. .\common.ps1
,但我在您的示例脚本中没有看到它。无论如何,如果您需要使用 common.ps1 中的函数,那么您必须将其放在远程计算机可以访问的地方。
您可以像在 RDP 会话中一样在 Powershell 中使用重定向驱动器:
New-PSSession SERVER1 -Cred $creds | Enter-PSSession
. \\tsclient\c\common.ps1
或者你可以把 common.ps1 放在网络共享上:
. \\Server1\share\common.ps1
或者您可以将 common.ps1 上的内容粘贴到每个脚本中。
或者您可以将 common.ps1 放在远程机器的本地文件系统上。
当远程计算机上的 Powershell 解析器看到“。\common.ps1”时,它会尝试从当前工作目录加载 common.ps1,该目录可能是建立远程 PS 会话的用户的主目录。
答案3
我本以为你需要复制函数.ps1在对其进行点源化之前。实际上,我可能会将两个脚本复制到同一目录(例如,通过 C$ admin 共享到\\server\C$\foo
),然后C:\foo\example.ps1
使用进行远程调用Invoke-Command
。
答案4
我遇到了完全相同的问题。我认为这与在远程调用方上下文中运行的远程调用有关。我设法通过提供目标库的完整本地路径作为变量来解决该问题,然后通过该变量对库进行点源化:
function Get-ScriptDirectory
{
return Split-Path $script:MyInvocation.MyCommand.Path
}
function GetLocalFileName
{
param([string]$filename)
return [system.io.path]::Combine((Get-ScriptDirectory),$filename)
}
# dot-source the library using full local name to get round problem with remote invocation
$MYLIBRARY = GetLocalFileName "mylibrary.ps1"
. $MYLIBRARY