Как импортировать пользовательский модуль PowerShell в удаленный сеанс?
Я разрабатываю пользовательский модуль PowerShell, который я хотел бы использовать в контексте удаленного сеанса на другом компьютере. Следующий код (который, очевидно, не работает) объясняет, чего я пытаюсь достичь:
import-module .MyCustomModule.psm1
$session = new-pssession -computerName server01
invoke-command -session $session -scriptblock {
<# use function defined in MyCustomModule here #>
}
Первый вопрос заключается в том, возможно ли вообще достичь этого сценария?Я имею в виду, что я хотел бы, чтобы мой пользовательский модуль физически присутствовал на моей машине, а не на удаленном сервере.
Я нашел этот поток , но мне это не удалось работать - это не позволяет создавать сеанс с удаленной машины обратно на локальную. Вероятно, я столкнулся с ограничениями конфигурации, упомянутыми где-то в комментариях к этой теме... Кроме того, автор упомянул о последствиях для производительности, которые имеют решающее значение для моего решения...
Если это возможно, то как?
Версия PowerShell в настоящее время не является ограничением - если решение доступно только в PS 3.0 - я могу жить с этим.
4 ответа:
Было несколько замечательных комментариев к этому вопросу, и я потратил некоторое время, исследуя различные способы подхода к проблеме.
Начнем с того, что то, о чем я изначально просил, невозможно. Я имею в виду, что если вы идете по пути модуля, то модуль должен физически присутствовать на целевой машине, чтобы иметь возможностьImport-Module
входить в удаленный сеанс.Чтобы абстрагироваться от моего вопроса дальше, я пытаюсь создать многоразовую платформу на основе PowerShell для развертывания продукта. Это произойдет это означает, что мы поощряем людей запускать некоторые сценарии на локальной машине для развертывания на каком-либо удаленном сервере. Насколько я изучил эту область, есть два возможных пути, которые дружественны здравому смыслу.
Модульный подход
Следующий процесс:
- поместите каждый логически отличающийся элемент функциональности в модуль PowerShell (
*.psm1
)- распределите модуль на удаленную машину и расширьте переменную
PSModulePath
чтобы включить новые модули location- на клиентской машине создайте новый сеанс на удаленном сервере и используйте
Invoke-Command -Session $s -ScriptBlock {...}
- в блоке скрипта start from
Import-Module CustomModule
- он будет искатьCustomModule
на удаленной машине и, очевидно, найдет егоПреимущества
Вот причины, по которым стоит любить этот подход:
- следствие традиционной роли модуля-облегчение создания многоразовых библиотек
- согласно отличная книгаWindows PowerShell in Action , "модули можно использовать для создания доменных приложений". Насколько я понимаю, это может быть достигнуто путем объединения вложенности модулей и смешивания скриптовых / бинарных модулей, чтобы предоставить интуитивно понятный интерфейс, специфичный для определенной области. В принципе, это то, что я ценю больше всего для цели PowerShell-based deployment framework
Недостатки
Важно учитывать следующее: рассмотрение:
- Вы должны найти способ доставки пользовательских модулей на удаленную машину. Я играл с NuGet , и я не уверен, что он хорошо подходит для этой задачи, но есть и другие варианты, например, MSI installer или plain
xcopy
из общей папки. Кроме того, механизм доставки должен поддерживать обновление / понижение и (предпочтительно) установку нескольких экземпляров, но это больше связано с моей задачей, чем с проблемой в общиеСкриптовый подход
Следующий процесс:
- поместите каждый логически отличающийся фрагмент функциональности в отдельный скрипт PowerShell ( * . ps1)
- на клиентской машине создайте новый сеанс для удаленного сервера и используйте
Invoke-Command -Session $s -FilePath .\myscript.ps1
для загрузки функций, определенных в скрипте, в удаленный сеанс- используйте другой
Invoke-Command -Session $s -ScriptBlock {...}
и обратитесь к вашим пользовательским функциям - они будут там в сеансеПреимущества
В следующие хорошие моменты этого подхода:
- это просто - вам не нужно знать об особенностях модуля. Просто напишите простые сценарии PowerShell, и все
- Вам не нужно ничего доставлять на удаленную машину - это делает решение еще более простым и менее подверженным ошибкам в обслуживании
Недостатки
Конечно, это не идеально:
Наконец, я должен сказать, что удаленная машина все еще должна быть подготовлена к удалению. Вот что я хочу сказать. имею в виду:
- там меньше контроля над решением: например, если вы "импортируете" набор функции к сессии, все они " импортированы "и видны пользователю, поэтому никакой" инкапсуляции " и т. д. я уверен, что многие решения могут жить с этим, поэтому не принимайте решения, основываясь только на этом пункте
- функциональность в каждом файле должна быть самодостаточной - любой точечный источник или импорт модуля оттуда будет искать удаленную машину, а не локальную
- политика выполнения должна быть изменена на что-то, потому что она ограничена по умолчанию:
Set-ExecutionPolicy Unrestricted
- удаленное взаимодействие PowerShell должно быть включено:
Enable-PSRemoting
- учетная запись, которую запускает скрипт, должна быть добавлена к локальным администраторам удаленного сервера
- если вы планируете получить доступ к общим файлам в удаленном сеансе, убедитесь, что вы знаете о многоходовой аутентификации и выполните соответствующие действия
- убедитесь, что ваш антивирус-ваш друг. и не отправляет вас в PowerShell hell
Вот еще один подход: воссоздайте модуль в удаленном сеансе, без копирования каких-либо файлов.
Я не делал попыток справиться с зависимостями между модулями, но это, кажется, работает нормально для простых автономных модулей. Он полагается на модуль, доступный в локальной сессии, так как это упрощает определение экспорта, но с небольшим количеством дополнительной работы он также будет работать с файлом модуля.
function Import-ModuleRemotely([string] $moduleName,[System.Management.Automation.Runspaces.PSSession] $session) { $localModule = get-module $moduleName; if (! $localModule) { write-warning "No local module by that name exists"; return; } function Exports([string] $paramName, $dictionary) { if ($dictionary.Keys.Count -gt 0) { $keys = $dictionary.Keys -join ","; return " -$paramName $keys" } } $fns = Exports "Function" $localModule.ExportedFunctions; $aliases = Exports "Alias" $localModule.ExportedAliases; $cmdlets = Exports "Cmdlet" $localModule.ExportedCmdlets; $vars = Exports "Variable" $localModule.ExportedVariables; $exports = "Export-ModuleMember $fns $aliases $cmdlets $vars;"; $moduleString= @" if (get-module $moduleName) { remove-module $moduleName; } New-Module -name $moduleName { $($localModule.Definition) $exports; } | import-module "@ $script = [ScriptBlock]::Create($moduleString); invoke-command -session $session -scriptblock $script; }
Я не верю, что это поддерживается прямо из коробки без каких-либо "хаков". Разумным шагом, вероятно, было бы поместить модуль в общедоступное место, например на файловый сервер, и импортировать его на сервер, когда он вам понадобится. Пример:
$session = new-pssession -computerName server01 invoke-command -session $session -scriptblock { #Set executionpolicy to bypass warnings IN THIS SESSION ONLY Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process #Import module from public location Import-Module \\fileserver\folders\modulelocation... <# use function defined in MyCustomModule here #> }