У меня есть сценарии Powershell для настройки веб-приложений IIS, очередей сообщений и т. Д.
Они используют созданный нами набор общих библиотек функций, поэтому каждый скрипт начинается со строки
. .\common.ps1
для ссылки на общую библиотеку. Общая библиотека содержит набор функций, например Create-IisApplicationPool
, Create-MessageQueue
и т.п., которые затем вызываются из фактического сценария. Проблема с этими сценариями заключается в том, что вам необходимо войти в систему через удаленный рабочий стол и запустить их локально, поэтому я пишу несколько новых сценариев для развертывания кода в экземпляре Amazon EC2 и использую удаленное взаимодействие Powershell для их локального вызова.
Я не могу понять, как сделать эту общую библиотеку доступной в удаленном сеансе Powershell.
Вот простой пример:
functions.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)"
}
example.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")
Работает локально, отлично работает - и говорит «Здравствуйте, мистер Спольски (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, но не может найти способа захвата локальных функций и их зависимостей в модуле, который можно импортировать в удаленный сеанс. Любая помощь с благодарностью получена!
Это немного старая тема, но все ответы более круговые, чем этот.
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
}
Я ожидал, что вам нужно скопировать functions.ps1 перед поиском через точку. Фактически, я бы, вероятно, скопировал оба сценария в один и тот же каталог (например, через общий ресурс администратора C $ в \\server\C$\foo
), а затем удаленно вызвать C:\foo\example.ps1
с помощью Invoke-Command
.
Вы говорите, что каждый сценарий начинается с . .\common.ps1
, но я не вижу этого в вашем примере сценария. В любом случае, если вам нужно использовать функции из common.ps1, вы должны поместить его куда-нибудь, чтобы удаленная машина могла получить доступ.
Вы можете использовать перенаправленные диски в Powershell, как в сеансе RDP:
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.
У меня была точно такая же проблема. Я предполагаю, что это связано с удаленными вызовами, выполняемыми в контексте удаленного вызывающего. Мне удалось обойти проблему, предоставив полный локальный путь к целевой библиотеке в качестве переменной, а затем используя точку-источник библиотеки через переменную:
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