IT-специалисты редко работают только на нашем локальном компьютере. Используя командлет Invoke-Command в PowerShell, нам не придется это делать! Этот командлет позволяет нам без проблем писать код, как если бы мы работали на нашем локальном компьютере.
Используя функцию PowerShell Remoting, командлет Invoke-Command является часто используемым командлетом PowerShell, который позволяет пользователю выполнять код внутри PSSession. Этот PSSession может быть создан заранее с помощью командлета New-PSSession, или он может быстро создавать и уничтожать временную сессию.
Связано: PowerShell Remoting: Полное руководство
Подумайте о Invoke-Command как о PowerShell psexec. Хотя они реализованы по-разному, концепция одинакова. Возьмите небольшой код или команду и запустите его “локально” на удаленном компьютере.
Однако для работы Invoke-Command необходимо иметь включенное и доступное на удаленном компьютере PowerShell Remoting. По умолчанию все машины Windows Server 2012 R2 или более поздних версий имеют это включено, а также соответствующие исключения в брандмауэре. Если у вас все еще есть машины с Server 2008, есть несколько способов настройки Remoting, но простой способ – это запустить winrm quickconfig или Enable-PSRemoting на удаленной машине.
Чтобы продемонстрировать, как работает Invoke-Command с “ад-хок командой”, то есть такой, которая не требует создания нового PSSession, предположим, что у вас есть удаленный компьютер, присоединенный к домену Windows Server 2012 R2 или более поздней версии. Вещи становятся немного запутанными, когда работаем с компьютерами в рабочей группе. Я открою консоль PowerShell, наберу Invoke-Command
и нажму Enter.
I’m immediately asked to provide a scriptblock. The scriptblock is the code that we’re going to run on the remote computer.
Чтобы доказать, что код внутри скриптблока выполняется на удаленном компьютере, просто выполним команду hostname
. Эта команда вернет имя хоста компьютера, на котором она выполняется. Запуск hostname
на моем локальном компьютере возвращает его имя.
Теперь давайте передадим скриптблок с тем же кодом внутри скриптблока в Invoke-Command
. Прежде чем мы сделаем это, мы забыли обязательный параметр: ComputerName
. Мы должны указать Invoke-Command
, на каком удаленном компьютере выполнять эту команду.
Обратите внимание, что вывод hostname
теперь является именем удаленного компьютера WEBSRV1
. Вы выполнили некоторый код на WEBSRV1. Выполнение простого кода внутри скриптблока и передача его на один удаленный компьютер является самым простым применением Invoke-Command
, но он может делать гораздо больше.
Передача локальных переменных в удаленные скриптблоки.
Вы не будете иметь единственной ссылки Invoke-Command внутри сценария. Ваш сценарий, вероятно, будет длинным на десятки строк, с определением переменных, определением функций в модулях и так далее. Несмотря на то, что обрамление некоторого кода в пару фигурных скобок может выглядеть невинно, на самом деле вы изменяете всю область видимости, в которой выполняется этот код. В конце концов, вы отправляете этот код на удаленный компьютер. У этого удаленного компьютера нет представления о всем локальном коде на вашем компьютере, кроме того, что находится в блоке сценария.
Например, предположим, у вас есть функция с параметром имени компьютера и пути к файлу. Цель этой функции – запустить установщик программного обеспечения на удаленном компьютере. Вы можете передать имя компьютера и “локальный” путь к установщику, который уже находится на удаленном компьютере.
Следующая функция кажется разумной, верно? Давайте запустим ее.
Она завершается неясным сообщением об ошибке из-за моего использования оператора амперсанд. Код был правильным, но он не сработал из-за того, что $InstallerFilePath
был пустым, несмотря на то, что вы передали значение с параметром функции. Мы можем проверить это, заменив амперсанд на Write-Host
.
Новая строка функции: Invoke-Command -ComputerName $ComputerName -ScriptBlock { Write-Host "Путь установщика: $InstallerFilePath" }
Обратите внимание, что значение $InstallerFilePath
равно нулю. Переменная не была развернута, потому что она не была передана на удаленную машину. Чтобы передать локально определенные переменные в удаленный блок сценария, у нас есть два варианта; мы можем предварить имя переменной с $using:
внутри блока сценария, или мы можем использовать параметр ArgumentList
команды Invoke-Command
. Давайте рассмотрим оба варианта.
Параметр ArgumentList
Один из способов передать локальные переменные в удаленный блок сценария – использовать параметр ArgumentList
команды Invoke-Command
. Этот параметр позволяет передавать локальные переменные в параметр и заменять ссылки на локальные переменные в блоке сценария плейсхолдерами.
Передача локальных переменных в параметр ArgumentList
легка.
То, что сбивает с толку некоторых людей, – это структурирование переменных внутри блока сценария. Вместо использования { & $InstallerPath }
, мы должны изменить его на { & $args[0] }
или {param($foo) & $foo }
. Оба варианта работают одинаково, но какой из них следует использовать?
Параметр ArgumentList
представляет собой коллекцию объектов. Коллекции объектов позволяют передавать один или несколько объектов за один раз. В данном случае я передаю только один.
При выполнении командлета Invoke-Command этот набор берется и внедряется в скриптблок, фактически превращая его в массив с именем $args
. Помните, что $args -eq ArgumentList
. На этом этапе вы ссылаетесь на каждый индекс набора так же, как на элемент массива. В нашем случае у нас был только один элемент в наборе ($InstallerFilePath
), который “переводится” как $args[0]
, что означает первый индекс в этом наборе. Однако, если бы у вас было больше элементов, вы бы ссылались на них $args[1]
, $args[2]
и так далее.
Кроме того, если вы предпочитаете назначить более понятные имена переменным скриптблока, вы также можете добавить параметры к скриптблоку, как к функции. В конце концов, скриптблок является всего лишь анонимной функцией. Чтобы создать параметры скриптблока, создайте блок param с именем параметра. После создания ссылайтесь на этот параметр в скриптблоке, как показано ниже.
В данном случае элементы в коллекции ArgumentList
“сопоставляются” с определенными параметрами по порядку. Имена параметров не имеют значения; важен порядок. Invoke-Command
возьмет первый элемент в коллекции ArgumentList
, найдет первый параметр и сопоставит эти значения, сделает то же самое для второго, третьего и так далее.
Конструкция $Using
Конструкция $using
– еще один популярный способ передачи локальных переменных в удаленный блок сценария. Эта конструкция позволяет повторно использовать существующие локальные переменные, просто предварив имя переменной символом $using:
. Нет необходимости беспокоиться о коллекции $args
или добавлении блока параметров.
Конструкция $using
в PowerShell намного проще, но если вы когда-нибудь начнете изучать Pester, вы увидите, что ArgumentList
будет вашим другом.
Invoke-Command и New-PSSession
Технически, в этом посте речь идет только о Invoke-Command, но чтобы продемонстрировать его полезность, нам нужно кратко упомянуть команду New-PSSession
. Напомню, что ранее я упомянул, что Invoke-Command
может использовать “ад-хок” команды или использовать существующие сеансы.
На протяжении этого поста мы только что выполняли “ад-хок” команды на удаленных компьютерах. Мы создавали новую сессию, выполняли код и разрушали ее. Это подходит для единичных случаев, но не так хорошо для ситуации, когда вам нужно выполнить десятки команд на одном и том же компьютере. В этом случае лучше повторно использовать существующую PSSession, создав ее заранее с помощью New-PSSession
.
Перед выполнением любых команд вам нужно создать PSSession с помощью New-PSSession
. Это можно сделать, просто запустив $session = New-PSSession -ComputerName WEBSRV1
. Это создает удаленную сессию на сервере, а также ссылку на эту сессию на моем локальном компьютере. На этом этапе я могу заменить ссылки ComputerName
на Session
и указать Session
на сохраненную переменную $session
.
При запуске вы заметите, что производительность выше, потому что сеанс уже создан. Однако после завершения важно удалить открытую сессию с помощью Remove-PSSession
.
Сводка
Cmdlet Invoke-Command является одним из наиболее распространенных и мощных. Я лично использую его чаще всего из всех. Простота использования и возможность выполнения любого кода на удаленных компьютерах делают его чрезвычайно мощным инструментом, и я настоятельно рекомендую освоить эту команду от и до!