Назад | Перейти на главную страницу

Как я могу использовать общие функции в удаленном сеансе Powershell?

У меня есть сценарии 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