Подтвердить что ты не робот

Как импортировать пользовательский модуль 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 - я могу жить с этим.

4b9b3361

Ответ 1

Было несколько замечательных комментариев к этому вопросу, и я потратил некоторое время на изучение различных способов решения проблемы.

Начать с того, что я изначально просил, невозможно. Я имею в виду, если вы идете по пути модуля, то модуль должен физически присутствовать на целевой машине, чтобы можно было Import-Module в удаленный сеанс.

Чтобы абстрагироваться от моего вопроса, я пытаюсь создать многократно используемую среду на основе PowerShell для развертываний продукта. Это будет принудительное развертывание, что означает, что мы рекомендуем людям запускать некоторые сценарии на локальном компьютере для развертывания на каком-либо удаленном сервере. Насколько я исследовал область, есть два возможных пути, которые являются дружественными для здравого смысла.

Модули подход

Процесс, которому нужно следовать:

  • поместите каждый логически различный элемент функциональности в модуль PowerShell (*.psm1)
  • распространить модуль на удаленный компьютер и расширить переменную PSModulePath в нее местоположение новых модулей
  • на клиентском компьютере создайте новый сеанс на удаленном сервере и используйте Invoke-Command -Session $s -ScriptBlock {...}
  • в блоке скрипта запускайте из Import-Module CustomModule - он будет искать CustomModule на удаленной машине и, очевидно, найдет его

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

Ниже приведены причины любить этот подход для:

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

Недостатки

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

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

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

Процесс, которому нужно следовать:

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

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

Ниже приведены хорошие моменты этого подхода:

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

Недостатки

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

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

Наконец, я должен сказать, что удаленный компьютер все еще должен быть подготовлен к удаленному взаимодействию. Это то, что я имею в виду:

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

Ответ 2

Здесь другой подход: воссоздайте модуль на удаленном сеансе, не копируя никаких файлов.

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

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

Ответ 3

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

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

Ответ 4

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

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

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

Ответ 5

Спасибо за эту тему, это было полезно....

Но я на самом деле переписал функцию.

Имейте в виду, что исходная функция в этом посте или эта переписанная функция не содержит данных манифеста модуля. Таким образом, вы не можете полагаться на проверку версий модуля.

function Import-ModuleRemotely {
    Param (
        [string] $moduleName,
        [System.Management.Automation.Runspaces.PSSession] $session
    )

    Import-Module $moduleName

    $Script = @"
    if (get-module $moduleName)
    {
        remove-module $moduleName;
    }

    New-Module -Name $moduleName { $($(Get-Module $moduleName).Definition) } | Import-Module
"@

    Invoke-Command -Session $Session -ScriptBlock {
        Param($Script)
        . ([ScriptBlock]::Create($Script))
        Get-Module 
    } -ArgumentList $Script
}