Как импортировать пользовательский модуль 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 переменная для включения нового местоположения модулей
  • на клиентском компьютере создайте новый сеанс на удаленном сервере и используйте Invoke-Command -Session $s -ScriptBlock {...}
  • в блоке скрипта начать с Import-Module CustomModule - он будет искать CustomModule на удаленной машине и, очевидно, найдете его

преимущества

следующие причины любить этот подход для:

  • следствием традиционная роль модуля-облегчение создания многоразовых библиотек
  • согласно великой книге Windows PowerShell в действии, "модули можно использовать для создания домен-специфических применений". Насколько я понимаю, это может быть достигнуто путем объединения вложенности модулей и смешивания скриптовых / двоичных модулей для отображения интуитивного интерфейса, специфичного для определенного домена. В принципе, это тот, который я ценю больше всего для цели развертывания на основе PowerShell рамки

недостатки

важно принять во внимание следующее:

  • вы должны найти способ доставки пользовательских модулей на удаленную машину. Я играл с NuGet для, и я не уверен, что он хорошо подходит для этой задачи, но есть и другие варианты, например, MSI installer или plain xcopy из общей папки. Кроме того, механизм доставки должен поддержать подъем / downgrade и (предпочтительно) установки с несколькими экземплярами, но это больше связано с моей задачей, чем с проблемой в целом

Скрипты подход

процесс:

  • поместите каждую логически различную функциональность в отдельный скрипт PowerShell (*.ps1)
  • на клиентском компьютере создайте новый сеанс на удаленном сервере и используйте Invoke-Command -Session $s -FilePath .\myscript.ps1 для загрузки функций, определенных в скрипте на пульт сеанс
  • использовать другой Invoke-Command -Session $s -ScriptBlock {...} и обратитесь к своим пользовательским функциям-они будут там в сеансе

преимущества

следующие хорошие моменты этого подхода:

  • это просто - вам не нужно знать об особенностях модуля. Просто напишите простые сценарии PowerShell, и все
  • вам не нужно ничего доставлять на удаленную машину - это делает решение еще проще и меньше подвержен ошибкам в обслуживании

недостатки

конечно, это не идеал:

  • меньше контроля над решением: например, если вы " импортируете "набор функций в сеанс, все они" импортируются "и видны пользователю, поэтому нет" инкапсуляции " и т. д. я уверен, что многие решения могут жить с этим, поэтому не решайте, основываясь только на этом пункте
  • функциональность в каждом файле должно быть автономный-любой точечный источник или импорт модуля оттуда будет искать удаленную машину, а не локальную

наконец, я должен сказать, что удаленная машина все еще должна быть готова к удалению. Вот что я имею в виду:--12-->

  • политика выполнения должна быть изменена на что - то, потому что она ограничена по умолчанию: Set-ExecutionPolicy Unrestricted
  • PowerShell remoting должен быть включен:Enable-PSRemoting
  • учетная запись скрипт работает как надо быть добавлены к локальным администраторам удаленного сервера
  • если вы планируете получить доступ к файловым ресурсам в удаленном сеансе, убедитесь, что вы знаете о Multi-hop аутентификации и принять надлежащие меры
  • убедитесь, что ваш антивирус является вашим другом и не отправляет вас в PowerShell с АД

вот еще один подход: воссоздайте модуль в удаленном сеансе без копирования каких-либо файлов.

Я не пытался справиться с зависимостями между модулями, но это, похоже, работает нормально для простых автономных модулей. Он полагается на модуль, доступный в локальном сеансе, так как это упрощает определение экспорта, но с небольшой дополнительной работой он также будет работать с файлом модуля.

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;
}

Я не считаю, что это поддерживается справа от коробки без каких-либо"хаков". Разумным шагом, вероятно, было бы поместить модуль в общедоступное место, такое как файловый сервер, и импортировать его на сервер, когда вам это нужно. Ex:

$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 #> 
}

Как насчет создания scriptblock из вашей пользовательской функции и отправки его на серверы terget с помощью Invoke-command

Import-module YourModule
$s = [scriptblock]::Create($(get-item Function:\Your-ModuleFunction).Definition)

Invoke-Command -ScriptBlock $s -Computername s1,s2,sn