Windows PowerShell
Дополнительную гибкость командной строке придает Windows PowerShell – полнофункциональная командная оболочка, в которую помимо стандартных утилит командной строки включены встроенные cmdlet-команды и возможности программирования. По умолчанию PowerShell не устанавливается.
Чтобы установить ее, выполните следующие действия:
- Щелкните кнопку Диспетчер сервера (Server Manager) на панели быстрого запуска или щелкните кнопку Пуск (Start) и выберите команды Администрирование (Administrative Tools) и Диспетчер сервера (Server Manager).
- В диспетчере сервера выберите узел Компоненты (Features)
- Пролистайте список компонентов и выберите Windows PowerShell. Щелкните Далее (Next) и Установить (Install).
Оболочка PowerShell из комплекта поставки может быть не самой новой. Проверьте центр загрузки на сайте Майкрософт па предмет наличия более новой версии. Установив PowerShell, вы обнаружите команду для ее запуска в меню Пуск (Start).
Если вы хотите вызвать PowerShell из командной строки, помните, что соответствующий исполняемый файл (powershell.exe) находится в папке %SystemRoot%\System32\WindowsPowerShell\Версия, где Версия – номер установленной версии PowerShell, например, v.1.0 или v.1.1.
Запустив PowerShell, введите в командной строке имя cmdlet-команды, и она запустится, как любая команда. Команды cmdlet можно также запускать из сценариев. Названия cmdlet-команд состоят из пар «глагол-существительное». Глагол говорит о действии cmdlet-команды, а существительное указывает на объект этого действия. Например, команда get-variable возвращает имена и значения переменных среды Windows PowerShell.
Обычно в именах cmdlet-команд используются следующие глаголы:
- Get Запрашивает специфический объект или подмножество типа объекта, например, выбранный почтовый ящик или всех пользователей почтовых ящиков.
- Set Модифицирует параметры объекта.
- Enable Включает параметр.
- Disable Выключает параметр.
- New Создает новый экземпляр объекта, например, почтового ящика.
- Remove Удаляет экземпляр объекта.
Примеры команд
Чтобы просмотреть полный список cmdlet-команд, введите в командной строке PowerShell команду:
Чтобы получить справку по конкретной команде, введите help и имя команды, например,
У cmdlet-команд есть настраиваемые псевдонимы, которые можно использовать для запуска cmdlet-команд. Чтобы просмотреть список псевдонимов, введите в командной строке PowerShell
С помощью следующей команды вы создадите псевдоним для любой команды:
new-item -path alias: Псевдоним -value: Полный Путь Команды
new-item -path alias: Псевдоним -value: Полный Путь Команды |
Здесь Псевдоним – имя создаваемого псевдонима, а ПолныйПутьКоманды – полный путь к команде. Например:
new-item -path alias:sm -value:c:\windows\system32\compmgmtlauncher.exe
new-item -path alias:sm -value:c:\windows\system32\compmgmtlauncher.exe |
В этом примере создается псевдоним sm для запуска диспетчера сервера. Чтобы использовать этот псевдоним при работе с Windows PowerShell, просто введите в командной строке sm и нажмите Enter.
Получение списка и запуск служб Powershell Service и управление
Для управления службами в Powershell есть восемь команд с помощью которых мы можем контролировать весь процесс работы сервисов и создавать новые. Что бы увидеть весь список команд созданных в PS для работы с сервисами нужно выполнить:
Get-Command -Noun Service
Мы увидим следующие командлеты:
- Get-Service — получение списка служб в Powershell.
- New-Service — создание нового сервиса.
- Restart-Service — перезапуск службы.
- Resume-Service — восстанавливает работу остановленного сервиса.
- Start-Service — запуск службы в Powershell.
- Stop-Service — остановка.
- Suspend-Service — приостановка работы (пауза).
Учитывайте, что в виде команд делается не полное управление сервисами. Например удаление делается через WMI, которое тоже будут рассмотрены.
Узнать статус всех служб можно так:
Get-Service
Каждый результат выдаваемый командами в PS представляет собою объект. Этот объект содержит разные свойства и методы. Свойства — это например имя или статус. По умолчанию мы видим не все свойства. Что бы узнать какие еще свойства есть у команды нужно выполнить следующее:
Get-Service | Get-Member -MemberType Properties
Часть свойств реализована в виде ключей. Попробуем вывести сразу все свойства всех сервисов, но в таком случая вывод будет сложно читаемый:
Get-Service | SELECT *
Возможно вывести только имена, статус и тип запуска:
Get-Service | Ft Name, Status, StartType
Или выведем информацию по одному сервису:
Get-Service "WinRM" | Ft Name, Status, StartType
У сервисов есть короткое имя и отображаемое. Так мы выведем оба:
В именах мы можем использовать маски, а это значит что мы можем не знать полное название и использовать знак * в том месте, где не уверены в названии или написании:
Get-Service -DisplayName "Win*" | Ft Name, DisplayName, Status, StartType
Не желательно указывать отображаемое имя так как язык операционных систем может быть разным и если вы выполняете командлеты удаленно, вероятно будут ошибки:
- Cannot find any service with service name
- Cannot find any service with display name
- Не удается найти службу с отображаемым именем
Кроме этого есть два ключа, которые тоже поддерживают маски:
- Include — включают поиск по какой-то маске или точному соответствию.
- Exclude — исключает упоминание.
Можно сравнить разницу с прошлым примером:
Get-Service -Name "Win*" -Include "*RM" -Exclude "*mgmt","*Http*","*Defend" | Ft Name, Status, StartType
У сервисов несколько статусов:
- Running — запущен.
- Stopped — остановлен.
- Paused — приостановлен (пауза).
По каждому из них мы можем отфильтровать результат:
Get-Service | where -Property Status -eq Stopped
Кроме этого есть 3 типа запуска:
- Automatic — автоматический запуск вместе с системой.
- AutomaticDelayedStart — запуск после загрузки системы.
- Manual — ручной запуск.
- Disabled — отключенный.
Допустим, что у нас есть какая-то проблема в операционной системе и мы хотим проверить все сервисы, который автоматически запускаются при включении ОС. Для этого нам нужно использовать несколько условий, где статус не равен Running и тип запуска равен Automatic:
Get-Service | where -FilterScript {$_.Status -ne 'Running' -and $_.StartType -eq 'Automatic'} | ft 'Name','StartType','Status'
Службы могут быть зависимы от других и для проверки этих свойств есть два параметра:
- DependentServices — кто зависит от этого сервиса.
- RequiredServices — от кого зависит этот сервис.
Get-Service -DisplayName 'Windows Remote Management (WS-Management)' -RequiredServices -DependentServices
Аналогично параметрам команды выше есть свойства DependentServices и ServicesDependedOn (RequiredServices). Они выводят одно и то же.
Есть несколько свойств, которые не выведены в параметры — это:
- CanPauseAndContinue — можно ли приостановить работу сервиса и возобновить.
- CanShutdown — может ли быть выключен.
- CanStop — может ли быть полностью остановлен.
Эти свойства так же можно увидеть в GUI. В командлете ниже я использую алиас gsv (короткое имя Get-Service):
gsv -ServiceName 'Winrm' | SELECT CanPauseAndContinue,CanShutdown,CanStop | fl
Каждая команда PS, где присутствует параметр ComuterName, может быть выполнена удаленно. Для удаленного управления в Powershell нужны дополнительные настройки, которые уже описывались в предыдущей статье.
Имена всех компьютеров, с которых мы хотим получить имена сервисов, можно указать через запятую:
gsv -ComputerName 'AD1','localhost' | where Status -eq 'Stopped' | Select MachineName,Name,Status,StartType
Чтобы в Powershell запустить службу и остановить достаточно указать только имя. Причем можно указывать как короткое и отображаемое имя:
Stop-Service -Name 'WinRM'
Start-Service -DisplayName 'Служба удаленного управления Windows (WS-Management)'
Можно выполнить получение, остановку и запуск в одну команду:
gsv 'WinR*' | Stop-Service | Start-Service
Во большинстве командлетах PS, которые делают изменения, можно увидеть два параметра. Первый — это WhatIf, который покажет результат выполнения командлета, но не попытается ее выполнить. Такие параметры удобно использовать при выполнении сложных задач типа кластеров. Второй параметр Confirm, который потребует подтверждение перед выполнением. Вместе эти параметры не работают:
Stop-Service -DisplayName 'Windows Remote Management (WS-Management)' -Confirm
В запуске и остановке так же можно использовать параметры фильтрации, которые вы видели при получении статусов выше. Будьте осторожны перед выполнением так как вы можете остановить все службы:
Stop-Service 'WinR*' -Include '*M'
Start-Service 'WinR*' -Include '*M'
Если с сервисом работает другой сервис, то мы не завершим его работу и нужно указать дополнительный ключ Force:
Stop-Service 'WinRM' -Force
Если он не стоит появится ошибка:
- Cannot stop service ‘Remote Procedure Call (RPC) (RpcSs)’ because it has dependent services. It can only be stopped if the Force flag is set.
- Не удается получить доступ к зависимым службам «Удаленный вызов процедур (RPC) (RpcSs)».
Как уже было описано выше сервисы могут быть зависимы от других и в таком случае запуск может не получится. На примере ниже я получил все родительские сервисы (от которых зависит Spooler) и запустил их, а затем запустил требуемый:
$sub_services = Get-Service 'Spooler'
$sub_services.RequiredServices | Start-Service
$sub_services.Name | Start-Service
Перезапуск сервисов работает так же со всеми параметрами:
Restart-Service 'WinRM'
Удаленный запуск и остановка командлетами не предусмотрена, но это исправляется стандартными методами:
Invoke-Command -ComputerName 'AD1','localhost' -ScriptBlock {Start-Service 'WinRM'}
Не каждую службу можно приостановить (Pause) и восстанавливть. Что бы увидеть все службы, у которых есть такая возможность, выполните:
Get-Service | where CanPauseAndContinue | select -Property Name,CanPauseAndContinue,DisplayName
Командой ниже мы получи, нажмем паузу и восстановим работу сервиса Winmgmt:
Get-Service -Name 'Winmgmt' | Suspend-Service | Resume-Service
Ошибки, если мы попытаемся остановить службу у которых нет такой возможности:
- Service ‘Windows Remote Management (WS-Management)’ cannot be suspended because the service does not support beingsuspended or resumed.
- Не удается приостановить службу «Spooler», так как этой службой не поддерживается приостановка или возобновление.
В этих командах так же можно использовать параметры Include и Exclude, с масками.
На скрипте ниже показана проверка возможности приостановки сервиса, если у него есть такая возможность. Если ее нет, то сервис остановит свою работу:
# Закомментировано от случайностей
#$srv = 'Winmgm*'
$srv_suspend_on = (Get-Service $srv).CanPauseAndContinue
if ($srv_suspend_on){Suspend-Service $srv}
else {Stop-Service $srv}
Командлетом ниже я изменил тип запуска сервиса с отключенного на автоматический:
Set-Service 'WinRM' -StartupType Automatic
В случае изменения сервисов нельзя использовать маски *.
В документации Microsoft говориться про возможность изменения на четыре режима со следующими названиями:
- Automatic
- AutomaticDelayedStart
- Disabled
- Manual
Во первых половина называется иначе и изменение не срабатывает. Возможно изменение не срабатывает из-за багов Windows Server 2019,может из-за зависимостей (в GUI все работает), но скорее всего дело в PS. При работе с WMI все срабатывает. Реальные варианты запуска такие:
- Automatic
- Boot
- Disabled
- Manual
- System
Через эту команду можно так же выполнить запуск и остановку:
Set-Service 'WinRM' -Status Stopped
Все возможные значения:
Можно изменить описание сервиса:
Set-Service 'WinRM' -Description 'что вы сделали с PS'
Команды изменения можно выполнять удаленно:
Set-Service WinRM -ComputerName 'AD1' -StartupType Automatic
Для создания сервиса необходимо указать два параметра — это имя и путь до бинарного файла:
New-Service -Name 'TestService' -BinaryPathName 'C:\test.exe'
В PS 5.1 не предусмотрена команда удаления, она появилась в версии 6.1, которая устанавливается отдельно и может работать иначе. Для удаления сервисов, а так же частичного изменения, используется WMI. Указывайте все возможные параметры, что бы в последующем не лезть в WMI.
Параметры, которые в последующем нельзя будет изменить через PS:
- DisplayName — полное отображаемое имя.
- Credential — учетная запись, под которой будет происходить запуск.
- DependsOn — от кого будет зависеть.
$credential = Get-Credential
New-Service -Name 'TestService1' -BinaryPathName 'C:\test.exe' -DisplayName 'Testing Services In Powershell' -Credential $credential -DependsOn WinRM
При объявлении переменной $credential у нас запросятся учетные данные (логин/пароль). По умолчанию сервис работает под текущим аккаунтом. Я создавал сервис на доменном компьютере и пока я не ввел FQDN вид [email protected] я получал ошибку:
- Service ‘Testing Services In Powershell (TestService1)’ cannot be created due to the following error: The account name is invalid or does not exist, or the password is invalid for the account name specified
Кроме этого есть параметры, которые возможно изменить через команды выше:
- Description — описание.
- StartupType — тип запуска.
New-Service 'InstallService' -BinaryPathName 'C:\test.exe' -Description 'Descr' -StartupType Automatic
За работу со службами в WMI отвечает класс win32_service. Преимущества такого подхода в том, что тут можно посмотреть и изменить все детали работы сервисов. Более подробно WMI в Powershell мы уже рассматривали.
Что бы получить список аналогичный Get-Service выполните:
Get-WmiObject -Class 'Win32_Service' | ft
В классе WMI можно увидеть больше свойств, которые можно использовать:
Get-WmiObject -Class 'Win32_Service' | Get-Member -MemberType Properties
Одно из преимуществ использования в WMI это то, что мы можем увидеть идентификатор процесса под которым работает сервис. Если сервис остановлен, то его идентификатор 0. Так мы увидим идентификатор процесса с именем WinRM и получим всю информацию по процессу:
$srv = Get-WmiObject -Class Win32_Service | Where -Property Name -eq 'WinRM'
$srv.ProcessId
Get-Process -Id $srv.ProcessId
О том как работать с процессами в Powershell мы писали в прошлой статье.
С помощью WMI мы можем вернуть имя аккаунта, под которым запущена служба и ее описание. Используя параметр ComputerName это можно сделать удаленно на множестве компьютеров:
Get-WmiObject -ComputerName 'AD1','localhost' -Class Win32_Service | ft Name,StartName,Description -AutoSize
Для управления в WMI существую методы. Все методы класса можно получить так:
Get-WmiObject -Class 'Win32_Service' | Get-Member -MemberType Method
Удалим сервис, который создавался через New-Service:
Get-WmiObject -Class 'Win32_Service' | Get-Member -MemberType Method
Для изменения паролей нужно использовать следующую конструкцию. После выполнения команды сервис нужно перезапустить:
# Логин пользователя
$user = 'ServiceUser'
# Пароль пользователя
$password = 'Pass0000'
# Имя сервиса
$srv_name = 'ServiceName'
$srv = Get-WmiObject -Class win32_service | where -Property Name -eq $srv_name
$srv.Change($null,$null,$null,$null,$null,$null,$null,$null,$null,$user,$password)
$null установлены у всех параметров, которые мы не хотим изменять. В нашем случае нужно изменить только логин и пароль и они указаны в переменных. Увидеть какие параметры не меняются с $null можно так:
$srv = get-wmiobject win32_service | where Name -eq Winrm
$srv.GetMethodParameters("change")
…Теги: #powershell
Топ 7 Powershell командлетов для новичков / Netwrix corporate blog / Habr
Наверное, все слышали о PowerShell, но наверняка не всем довелось с ним работать. Для тех, кто только начинает прокладывать свой путь в дебри PowerShell, мы приводим перевод поста, вышедшего на портале 4sysops.com. В нем рассказано о 7 командах, которые помогут тем, кто только начал работать с PowerShell. За подробностями – добро пожаловать под кат.GET-HELP
Самый первый и самый главный командлет PowerShell – вызов справки. С помощью командлета Get-Help можно проверить синтаксис, посмотреть примеры использования и детальное описание параметров любого PowerShell командлета. Этот командлет примечателен тем, что вы просто можете набрать Get-Help Services, чтобы получить список всех командлетов, которые подходят для работы со службами. Пример:
PS C:\> Get-Help Service
Вы можете выбрать любой командлет из списка, выведенного по запросу выше, чтобы получить справку о нем. Например,
PS C:\> Get-Help -Name Get-Service
Вы получаете всю информацию о командлете Get-Service (будет рассмотрен ниже).
GET-CONTENT
Чтение содержимого файлов – наиболее частое требование для новичков, которые пытаются выучить PowerShell. Процедура чтения файлов с PowerShell упрощается. Даже неспециалист может читать содержимое файла, просто передав его в командлет Get-Content.
Пример.
PS C:\> Get-Content C:\scripts\Computers.txt
mytestpc1
techibee.com
dummynotresolvinghost.com
PS C:\>
Необходимо больше информации о командлете? Воспользуйтесь Get-Help:
PS C:\> Get-Help Get-Content -Detailed
GET-SERVICE
Этот командлет перечисляет все службы, установленные на компьютере. Вы можете использовать его для получения информации о конкретной службе, совокупности служб или просто обо всех службах на компьютере.
Пример:
PS C:\> Get-Service wwansvc, spooler
Status Name DisplayName
------ ---- -----------
Running spooler Print Spooler
Stopped wwansvc WWAN AutoConfig
PS C:\>
Здесь мы запросили информацию о двух службах wwansvc и spooler
Выводится таблица со статусом службы, ее именем и отображаемым именем.
Мы можем видеть что служба spooler запущена, а wwansvc остановлена
STOP-SERVICE И START-SERVICE
Запуск и остановка служб – достаточно важный момент в работе администратора Windows. В PowerShell имеются встроенные командлеты, которые упрощают работу администратора, не требуя открытия консоли MMC. Используя эти командлеты Вы можете останавливать/запускать службы как на локальных, так и на удаленных компьютерах.
Примеры:
Запуск/остановка службы на локальном компьютере (на примере службы spooler):
PS C:\> Stop-Service -Name Spooler
PS C:\> Start-Service -Name Spooler
Запуск/остановка службы на удаленном компьютере (spooler):
PS C:\> $ServiceObj = Get-Service -ComputerName MyPC1 -Name spooler
PS C:\> Stop-Service -InputObj $ServiceObj
PS C:\> Start-Service -InputObj $ServiceObj
GET-PROCESS
Этот командлет позволяет быть в курсе, какие процессы запущены на локальных или удаленных компьютерах. Показываются имя и ID процесса, а также путь к исполняемому файлу, имя компании, версия исполняемого файла и память, используемая процессом.
Примеры:
Получение информации о процессах, запущенных на локальном компьютере:
PS C:\> Get-Process
Введите следующий командлет для получения подробной информации о запущенных процессах
PS C:\> Get-Process | Format-List * -Force
Получение информации о процессах, запущенных на удаленном компьютере:
PS C:\> Get-Process -ComputerName MYPC1 | Format-List * -Force
MYPC1 необходимо заменить на имя того компьютера, с которого вы хотите получить информацию о запущенных процессах.
STOP-PROCESS
Этот командлет остановливает процесс на локальном или удаленном компьютере. Он берет имя или ID процесса и завершает этот процесс. Это полезно в тех случаях, когда приложение не отвечает.
Пример:
Остановить процесс с ID 22608 на локальном компьютере:
PS C:\> Stop-Process -Id 22608
Остановить все процессы Excel на локальном компьютере:
PS C:\> Stop-Process -name excel
Совет: Хотя у командлета Stop-Process отсутствует параметр -ComputerName, Вы все равно можете использовать его для завершения удаленных процессов, используя предложенный ниже совет:
PS C:\> $Obj = Get-Process -Name excel -ComputerName MYPC1
PS C:\> Stop-Process -InputObject $Obj
Upd:
В посте приведен перевод статьи с портала 4sysops.com
Top 7 PowerShell commands for beginners
P.S. Смотрите также интересные посты на Хабре, посвященные работе с PowerShell
Аудит доступа к файлам
Аудит Active Directory (Часть 1 и 2)
Актуализируем учетные данные Active Directory
Аудит создания учетных записей в AD
Что такое PowerShell?
Все мы знаем, что такое командная строка, какие возможности она дает, но не все знают о похожем интерфейсе, который называется PowerShell. По сути это почти такая же командная строка, только с гораздо более широкими возможностями. В этой статье и пойдет речь о том, что такое PowerShell, и что этот инструмент может нам предложить.
Определение PowerShell
Начнем с того, что такое оболочка. Оболочка – это интерфейс, который позволяет воспользоваться какими-либо функциями операционной системы. Таким образом, PowerShell это оболочка, которую разработала компания Microsoft и предназначена для более автоматизированного выполнения задач. Данный инструмент создан на основе .NET и оболочки командной строки, а также языка сценариев.
Также, существует такое понятие, как PowerShell ISE, которое выступает в качестве интегрированной среды сценариев, другими словами, это графический интерфейс, с помощью которого мы можем создавать какие-то сценарии, и не нужно вводить для этого все команды в командную строку.
Первая версия инструмента PowerShell появилась еще в 2006 году для Windows XP, Server 2003 и Vista. На данный момент последняя версия инструмента 4.0. Выпущена в 2013 году вместе с Windows 8.1.
Какие функции выполняет PowerShell?
Как я уже сказал выше, Microsoft создала эту утилиту для того, чтобы какие-либо задачи с операционной системой можно было выполнять намного быстрее. Допустим вы хотите увидеть USB устройства, которые подключены к компьютеру, так вот, это можно сделать с помощью PowerShell. Еще можно создать, которая будет выполняться в фоновом режиме, пока вы будете заниматься своими делами. Если замечены лишние фоновые процессы, которые нагружают системы, их можно отключить с помощью PowerShell. Также, можно создать документ, в котором будет храниться информация о компьютерных сетях или другое.
Таким образом, с помощью данной утилиты можно легко и быстро выполнять трудоемкие задачи, а также создавать любые сценарии или объединять несколько команд.
В том случае, если вы являетесь администратором сети, то PowerShell сможет вам помочь в работе, например, с Active Directory. Также, стоит заметить, что утилита содержит более 100 команд. Это говорит о том, что она поможет решить вам большинство задач.
Запуск PowerShell на Windows 7
Для того, чтобы запустить PowerShell на данной операционной системе необходимо в поиске, где Пуск, ввести «powershell».
По-другому инструмент можно открыть, если зайти в меню Пуск, все программы, стандартные и папка Windows PowerShell.
Запуск PowerShell на Windows 8.1
Для открытия PowerShell в Windows 8.1 необходимо в поиске ввести ключевое слово «powershell», чтобы открыть поиск, нажмите Win+Q.
Другой способ открытия – через окно «Выполнить». Нажимаем Win+R, открывается окно, в которое вы вводите powershell.
Запуск PowerShell на Windows 10
Тут тоже самое, что и в двух предыдущих версиях Windows. Поиск находится на панели задач, нажимаете на него и вводите powershell. И способ с интерфейсом «Выполнить», как в Windows 8.1.
Использование в повседневности
Данным инструментом пользуются многие IT-специалисты и администраторы и не зря, так как он дает большие возможности и снижает время, на выполнение каких-либо задач.
Если пользователь имеет в своем пользовании большую сеть, которая состоит из нескольких сотен серверов, то нужно будет внедрить систему безопасности, которая будет работать при использовании определенной службы. Проблема в том, что нужно проверить, установлена ли данная служба на всех серверах. Конечно, можно подключится вручную к каждому серверу, но руководству это не очень понравится, так как на выполнение такой задачи уйдет очень много времени.
Чтобы сократить время работы до нескольких минут, можно воспользоваться PowerShell, с помощью которого, мы при помощи одного сценария соберем всю необходимую информацию и сохраним ее в отдельный документ.
Также, о данном инструменте написано не мало книг, все-таки данный инструмент дает широкие возможности, которые вряд ли опишутся в данной статье полноценно. Таким образом, если вас заинтересовала данная тема, вы можете ее начать ее изучать более подробно.
Как в скрипте PowerShell определить его местоположение
Как в скрипте PowerShell определить его местоположение
Предположим, что в скрипте требуется определить текущие параметры, такие как имя файла или директория, из которой скрипт был запущен. Сделать это можно разными способами, но проще всего воспользоваться автоматическими переменными, в которых хранятся сведения о состоянии PowerShell.
Так в переменной $PSScriptRoot хранится текущая директория, из которой был запущен скрипт, а в переменной $PSCommandPath — полный путь и имя файла скрипта. Для примера создадим скрипт такого содержания и выполним его:
″PSScriptRoot is $PSScriptRoot″
″PSCommandPath is $PSCommandPath″
Также для определения местоположения можно воспользоваться переменной $MyInvocation. Эта переменная имеет свойство MyCommand, в котором содержатся сведения о выполняемой команде. Заменим содержимое скрипта и выполним его:
$MyInvocation.MyCommand | fl *
Примечание. Если попробовать получить значение переменных $PSScriptRoot, $PSCommandPath и $MyInvocation из консоли, то вы ничего не получите. Эти переменные заполняются только для скриптов и функций.
Таким образом, $MyInvocation.MyCommand.Path содержит полный путь и имя скрипта, $MyInvocation.MyCommand.Name — имя файла. Ну а текущую директорию можно получить командой:
$MyInvocation.MyCommand.Path | Split-Path -Parent
Для примера добавим в скрипт следующие команды и выполним его:
Full name is $($MyInvocation.MyCommand.Path)
Name is $($MyInvocation.MyCommand.Name)
Current directory is $($MyInvocation.MyCommand.Path | Split-Path -Parent)
Переменная $MyInvocation имеет одну особенность, о которой надо помнить. Для наглядности создадим два скрипта, первый:
$MyInvocation | fl *
.\script2.ps1
и второй:
$MyInvocation | fl *
Т.е. в первом скрипте мы выводим содержимое переменной $MyInvocation, затем из него же вызываем второй скрипт, в котором также выводим $MyInvocation. Запускаем первый скрипт и смотрим результат. Как видите, содержимое переменной для первого и второго скрипта отличается. Дело в том, что в $MyInvocation хранятся данные о вызывающем объекте, поэтому для второго скрипта оказались заполненными свойства ScriptName, PSScriptRoot и PSCommandPath, которые как раз указывают на первый скрипт, из которого была запущена текущая команда.
Полный список автоматических переменных и их подробное описание можно получить командой about_Automatic_Variables.
Создание функций Powershell и команды с вызовом и передачей параметров
Во время написания программы или скрипта любой начинающий программист столкнется с проблемой где ему нужно повторить код дважды и в этот могут помочь функции в Powershell. Функции так же называют методами и процедурами. Кроме многократного использования они так же полезны для выделения одной части скрипта от другой, хоть она и выполняется один раз. Методы описанные ниже существуют во множестве языках и работают по похожему сценарию.
Представим, что каждое утро вы проверяете 50 последних логов за 14 часов журнала Application с помощью этой команды:
Get-EventLog -LogName Application -Newest 50 | where TimeWritten -ge (Get-Date).AddHours(-14)
Команда не очень сложная, но в скором времени ее надоест писать. Для сокращения этой работы ее можно выделить в отдельную функцию:
function Get-DayLog {
Get-EventLog -LogName Application -Newest 50 | where TimeWritten -ge (Get-Date).AddHours(-14)
}
Любая функция обязательно должна состоять из трех вещей:
- function — объявляет и говорит что после нее будет название;
- имя функции — название, по которому мы будем ее вызывать. В нашем случае имя Get-DayLog;
- скобки — обозначают начало и конец выражения.
После написания функции она вызывается по имени:
Get-DayLog
Учитывая, что нам может потребоваться получить данные не за последние 14 часов и более чем за 50 дней нам может потребуется изменить ее передав параметры.
Не обязательно использовать имя такого же плана, как принято в Powershell, то есть вместо «Get-DayLog» можно писать «daylog». Такой подход рекомендуем и является распространенной практикой, который поможет отличить запуск сторонней программы от функции.
Функции в Powershell всегда именуются по следующему признаку. Первое слово это глаголы типа:
- Get — получить;
- Set — изменить;
- Install — установить;
- New — создать.
Второе имя — это существительное, как в случае выше DayLog(дневной лог). У Microsoft есть утвержденный список глаголов, который доступен по ссылке на английском языке. Если вы не будете придерживаться этих правил и захотите добавить свою функцию (командлет или модуль) в один из репозиториев, то он может не пройти модерацию.
Чаще всего функции принимают какой-то объект и возвращают. Для примера может потребоваться изменить время, когда эти логи созданы и их количество. В существующей функции такой возможности нет. Вы можете править код каждый раз, но это не подход программиста. Что бы такая возможность появилась в функции нужно добавить параметры, которая она будет принимать:
function Get-DayLog ($param1,$param2) {
Get-EventLog -LogName Application -Newest $param1 | where TimeWritten -ge (Get-Date).AddHours($param2)
}
Параметры функции обозначаются в круглые скобки и пишутся после названия функции и до выражения.
Теперь, для вызова функции, требуется передавать два параметра:
Get-DayLog -param1 50 -param2 -14
При вызове функции мы передаем два обязательных параметра со значениями. Эти значения, внутри функции, будут доступны под названиями $param1 и $param2. Эти переменные мы передаем в команду получения и фильтрации логов.
Установка значений по умолчанию
В нашей задаче, чаще всего, мы получаем только 50 последних логов и нам не хочется указывать их каждый раз. Если мы не будем указывать этот параметр в существующей функции, то получим ошибку. Что бы этого избежать нужно указать значение по умолчанию. На примере ниже я присвоил $param1 значение 50. Оно будет использоваться только в том случае, если мы не используем этот параметр при вызове:
function Get-DayLog ($param1=50,$param2) {
Get-EventLog -LogName Application -Newest $param1 | where TimeWritten -ge (Get-Date).AddHours($param2)
}
Get-DayLog -param2 -7
Мы должны всегда указывать ключ param2, что добавляет немного работы. Что бы это исправить достаточно поменять их местами:
function Get-DayLog ($param2,$param1=50) {
Get-EventLog -LogName Application -Newest $param1 | where TimeWritten -ge (Get-Date).AddHours($param2)
}
Get-DayLog -7 1
Get-DayLog -7
Как видно на примере, если мы не указываем ключи param1 и param2 важна последовательность, так как именно в такой последовательности они будут присвоены переменным внутри функций.
Возвращение значений
В отличие от других языков, если мы присвоим переменной $result результат функции Get-DayLog, то она будет содержать значения:
$result = Get-DayLog -7 1
Это будет работать до тех пор, пока мы не решим изменить функцию присвоив переменные:
function Get-DayLog ($param2,$param1=50) {
$events = Get-EventLog -LogName Application -Newest $param1 | where TimeWritten -ge (Get-Date).AddHours($param2)
}
$result = Get-DayLog -7
$result
$events
Мы не можем получить результат используя переменную $result, так как функция не выводит информацию, а хранит ее в переменной $events. Вызывая $events мы тоже не получаем информацию, так как тут работает понятие «область видимости переменных».
Так как функции это отдельная часть программы вся логика и ее переменные не должны касаться другой ее части. Область видимости переменных подразумевает это же. Все переменные, созданные внутри функции остаются в ней же. Эту ситуацию можно исправить с помощью return:
function Get-DayLog ($param2,$param1=50) {
$events = Get-EventLog -LogName Application -Newest $param1 | where TimeWritten -ge (Get-Date).AddHours($param2)
return $events
}
$result = Get-DayLog -7
$result
Я бы рекомендовал всегда возвращать значения через return, а не использовать вывод используя команды типа Write-Output внутри функции. Использование return останавливает работу функции и возвращает значение, а это значит что его не стоит ставить по середине логики если так не планировалось.
Возвращаемых значений может быть несколько. Для примера создадим функцию, которая будет считать зарплату и налог:
function Get-Salary ($Zarplata) {
$nalog = $Zarplata * 0.13
$zarplata_bez_nds = $Zarplata - $nalog
return $nalog,$zarplata_bez_nds
}
Get-Salary -Zarplata 100000
Я вернул оба значения разделив их запятой. По умолчанию всегда возвращается массив. Массивы в Powershell это набор не именованных параметров. Более подробно о них мы уже писали.
В случае с массивами, что бы добавит надпись о зарплате, и налоге нужно использовать индексы:
$result = Get-Salary -Zarplata 100000
# Определяем тип данных
$result.GetType()
Write-Host "это зарплата" $result[1]
Write-Host "это налог" $result[0]
Возвращаться может любой тип данных. Например мы можем использовать другой тип данных хэш таблицы, которые в отличие от массивов именованные:
function Get-Salary ($Zarplata) {
$nalog = $Zarplata * 0.13
$zarplata_bez_nds = $Zarplata - $nalog
return @{"Налог"=$nalog;"Зарплата"=$zarplata_bez_nds;}
}
Get-Salary -Zarplata 100000
Подробно о хэш таблицах в Powershell вы можете почитать в предыдущих статьях. Далее так же будет несколько примеров с ними.
Вы можете возвращать любой тип данных и в любом формате и последовательности. Каждый из них имеет своё преимущество.
Область видимости переменных
Все переменные объявленные до момента вызова функции могут быть ей использованы:
$Zarplata = 100000
function Get-Salary {
$nalog = $Zarplata * $nalog
$zarplata_bez_nds = $Zarplata - $nalog
return @{"Налог"=$nalog;"Зарплата"=$zarplata_bez_nds;}
}
$nalog = 0.20
Get-Salary
Такой подход не запрещает переопределить переменную внутри функции дав ей другое значение:
$Zarplata = 100000
function Get-Salary {
$Zarplata = 200000
$nalog = $Zarplata * $nalog
$zarplata_bez_nds = $Zarplata - $nalog
return @{"Налог"=$nalog;"Зарплата"=$zarplata_bez_nds;}
}
$nalog = 0.20
Get-Salary
$Zarplata
Как уже писалось выше, значения внутри функции не доступны вне нее и у нас есть все возможности что бы этого не потребовалось. Тем не менее есть способ объявить внутри функции переменную, которая будет доступна вне нее.
Такие переменные называются глобальными. Объявляются приставкой $global:
$Zarplata = 100000
function Get-Salary {
#Глобальная переменная
$global:Zarplata = 200000
$nalog = $Zarplata * $nalog
$zarplata_bez_nds = $Zarplata - $nalog
return @{"Налог"=$nalog;"Зарплата"=$zarplata_bez_nds;}
}
$nalog = 0.20
Get-Salary
$Zarplata
Как вы видите, в отличие от предыдущего примера, переменная $zarplata изменила значение. Использование глобальных переменных является нежелательным так как может привести к ошибкам. Ваш скрипт может быть импортируемым модулем и об этой переменной может никто не знать, тем не менее она будет в области видимости.
Строгие типы данных
Powershell автоматически преобразует типы данных. В отличие от других языков результат этого выражения будет число 3, а не «111»:
3 * "1"
Такой подход может привести к ошибке. Мы можем исправить это объявляя типы:
function Get-Size ([int]$Num){
$size = 18 * $Num
return $size
}
Get-Size 5
Get-Size "str"
То есть объявляя типы данных мы либо получим ошибку избежав неверного преобразования. Если бы мы передавали такую строку «1», то у нас корректно выполнилось преобразование в число.
Таких типов данных в Powershell всего 13:
- [string] — строка;
- [char] — 16-битовая строка Unicode;
- [byte] — 8 битовый символ;
- [int] — целое 32 битовое число;
- [long] — целое 64 битовое число;
- [bool] — булево значение True/False;
- [decimal] — 128 битовое число с плавающей точкой;
- [single] — 32 битовое число с плавающей точкой;
- [double] — 64 битовое число с плавающей точкой;
- [DateTime] — тип данных даты и времени;
- [xml] — объект xml;
- [array] — массив;
- [hashtable] — хэш таблица.
Примеры работы с некоторыми типами данных вы увидите далее.
$args
В языках программирования есть понятие позиционного параметра. Это такие параметры, которые могут передаваться без имен:
function Get-Args {
Write-Host "Пример с arg: " + $args[0] -BackgroundColor Red -ForegroundColor Black
Write-Host "Пример с arg: " + $args[1] -BackgroundColor Black -ForegroundColor Red
}
Get-Args "Первый" "Второй"
Обратите внимание, что $args является массивом и значение получаются по индексу. Я не ставлю запятую при вызове функции так как в этом случае у меня был бы массив двойной вложенности.
Обязательные параметры Mandatory
Попробуем выполнить следующий пример, который должен вернуть дату изменения файла:
function Get-ItemCreationTime ($item){
Get-Item -Path $item | select LastWriteTime
}
Get-ItemCreationTime "C:\Windows\explorer.exe"
Get-ItemCreationTime
Первый вызов функции прошел успешно, так как мы передали параметры. Во втором случае мы не передаем значения, а значит переменная $item равна $null (неопределенному/неизвестному значению). Во многих языках, в таких случаях, у нас появилась бы ошибка еще в момент вызова функции Get-ItemCreationTime, а не во время выполнения Get-Item.
Представьте что до получения даты изменения файла будут еще какие-то действия, например удаление и создание файлов, которые могут привести к поломке компьютера или программы. Что бы этого избежать можно объявить этот параметр обязательным:
function Get-ItemCreationTime ([parameter(Mandatory=$true)]$item){
Get-Item -Path $item | select LastWriteTime
}
Get-ItemCreationTime "C:\Windows\explorer.exe"
Get-ItemCreationTime
Атрибут Mandatory обязывает указывать значение. Если оно будет проигнорировано, то мы получим ошибку еще до момента выполнения функции.
Param()
Вы могли видеть функции, которые имеют значение Param(). Это значение так же объявляет параметры. На предыдущем примере это значение использовалось бы так:
function Get-ItemCreationTime {
param (
[parameter(Mandatory=$true)]$item
)
Get-Item -Path $item | select LastWriteTime
}
Get-ItemCreationTime "C:\Windows\explorer.exe"
Get-ItemCreationTime
Microsoft Рекомендует использовать именно такой синтаксис написания функции, но не обязывает его использовать. Такой синтаксис говорит, что это не просто функция, а командлет.
Создадим скрипт, в котором будет происходить умножение, где добавим несколько обязательных параметров используя синтаксис с Param:
function Get-PlusPlus {
param (
[parameter(Mandatory=$true, Position=0)]
[int]
$item1,
[parameter(Position=1)]
[int]
$item2,
[parameter(Position=2)]
[string]
$item3
)
$summ = $item1 + $item2
Write-Output $item3 $summ
}
Get-PlusPlus 2 5 "Summ"
Position говорит под каким номером передается значение.
Одно из преимуществ работы с param() в том, что у нас становятся доступны ключи типа -Confirm и -Verbose.
CmdletBinding()
Использование этого атрибута позволяет расширять возможность по созданию командлетов. Microsoft пишет, что использование CmdletBinding или Parameter расширяет возможность функций в Powershell, но по моему опыту не всегда все срабатывает и нужно ставить оба атрибута.
На примере ниже я установил ограничение на длину строк с 1 по 13 символов с помощью ValidateLength(1,13). Position=1 говорит об индексе элемента в массиве:
function Get-LenStr {
[CmdletBinding()]
param (
[parameter(Mandatory=$true,
Position=1
)]
[ValidateLength(1,13)]
[string]
$len1,
[parameter(Mandatory=$true,
Position=0
)]
[string]
$len2
)
Write-Host $len2 $len1
}
Get-LenStr "Это строка 1" "Это строка 2"
Таких дополнительных аргументов для команд достаточно много. Для примера еще несколько атрибутов, которые можно добавить в блок parameter:
- HelpMessage = «Текст» — подсказка по использованию переменной. Это пояснение можно увидеть при запросе справки через Get-Help;
- ParameterSetName=»Computer» — указывает к какой переменной относятся параметры;
Отдельные блоки типа [ValidateLength]:
- [Alias(‘t’)] — устанавливает алиас для этого параметра в виде буквы t;
- [PSDefaultValue(Test=’Test’)] — устанавливает значение по умолчанию переменной Test;
- [string[]] — такое использование говорит, что значение принимает массив строк
- [AllowNull()] — позволяет обязательным параметрам быть $null
- [AllowEmptyString()] — позволяет обязательным параметрам быть пустой строкой
- [AllowEmptyCollection()] — обязательный параметр с пустым массивом
- [ValidateCount(1,5)] — минимальное и максимальное количество значений.
- [ValidatePattern(«[0-9]»)] — проверка на шаблон используя регулярного выражения
Больше примеров и аргументов на сайте Microsoft.
Передача массивов в виде параметров
В предыдущих статьях было множество примеров по работе с массивами и хэш таблицами. Их же, как и любой другой тип данных, мы можем передавать в функцию. Для примера все команды Powershell, которые имеют ключ ComputerName, могут выполняться удаленно. Большинство таких команд могут принимать в виде значений массивы, то есть нам не обязательно передавать поочередно имена компьютеров.
Функция будет принимать массив с именами компьютеров и возвращать все остановленные сервисы. Я так же объявлю этот тип строгим, для наглядности, хотя и без этого в любом случае сработает:
function Get-ServiceStopped ([array]$Computers){
$services = Get-Service -ComputerName $Computers | where Status -eq Stopped
return $services
}
Get-ServiceStopped '127.0.0.1','localhost'
Массивы так же работают по индексам, что позволяет передавать больше параметров. Такой способ не релевантный, но может когда-то пригодиться.
function Get-ServiceStopped ([array]$Computers){
$services = Get-Service -ComputerName $Computers[0,-2] | where Status -eq $Computers[-1]
return $services
}
Get-ServiceStopped '127.0.0.1','localhost','Stopped'
Хэш таблицы
Параметры хэш таблиц и могут передаваться не просто в функцию, а как параметры командлетов. Добавим в нашу функцию запуск сервиса, если он остановлен:
function Get-ServiceStopped ([hashtable]$Params){
$services = Get-Service @Params | where Status -eq Stopped
$services = Start-Service $services
return $services
}
Get-ServiceStopped @{Name='WinRM';ComputerName=@('127.0.0.1','localhost')}
Знак @ в команде объявляет, что данные хэш таблицы будут использоваться как параметры команды. Важно, чтобы их имена соответствовали настоящим параметрам.
Нет никаких ограничений на использования условий. Это бывает достаточно удобно, когда функция должна вернуть разные значения.
IF
Ниже приведен пример, где в зависимости от скорости загрузки основной части сайта будет возвращен разный ответ. Если скорость ответа меньше 76 миллисекунды нормальная, в случае если более долгого ответа вернется другой результат:
function Get-SiteResponse {
# Начало отсчета
$start_time = Get-Date
# Выполнение запроса
$request = Invoke-WebRequest -Uri "https://fixmypc.ru"
# Фиксирование окончания выполнения
$end_time = Get-Date
# Высчитываем разницу во времени
$result = $end_time - $start_time
# Проверка и возвращение результата
if ($result.Milliseconds -lt 76) {
return "Скорость ответа нормальная " + $result.Milliseconds}
else{
return "Сайт отвечает долго " + $result.Milliseconds }
}
Get-SiteResponse
Switch
Мы уже говорили про Powershell Switch в предыдущих статьях. Если коротко, то это более удобные условия. Используя предыдущий пример, но со Switch, это будет выглядеть так:
function Get-SiteResponse {
# Начало отсчета
$start_time = Get-Date
# Выполнение запроса
$request = Invoke-WebRequest -Uri "https://fixmypc.ru"
# Фиксирование окончания выполнения
$end_time = Get-Date
# Высчитываем разницу во времени
$result = $end_time - $start_time
# Проверка и возвращение результата
switch($result.Milliseconds) {
{$PSItem -le 76} {
return "Скорость ответа нормальная " + $result.Milliseconds}
default {
return "Сайт отвечает долго " + $result.Milliseconds }
}
}
Get-SiteResponse
Другой пример Switch это вызов функции в зависимости от переданных параметров. На примере ниже я вызываю функцию, в которой находится Switch. В эту функцию я передаю имя компьютера, которое проверяется на упоминание указанных фраз и вызывает соответствующую функцию. Каждая функция, которая устанавливает обновления, возвращает значение в Switch, а затем происходит return внутри нее:
function Install-SQLUpdates {
# делаем установку
return "Установка обновлений на SQL сервер прошла успешно"
}
function Install-ADUpdates {
# делаем установку
return "Установка обновлений на сервер AD прошла успешно"
}
function Install-FileServerUpdates {
# делаем установку
return "Установка обновлений на файловый сервер прошла успешно"
}
function Make-Switch ($computer) {
# Проверка имени компьютера
$result = switch($computer){
{$computer -like "SQL*"} {Install-SqlUpdates}
{$computer -like "AD*"} {Install-ADUpdates}
{$computer -like "FileServer*"} {Install-FileServerUpdates}
default {"Такого типа компьютеров нет"}
}
return $result
}
Make-Switch "AD1"
Со switch так же удобно передавать булевы значения. В примере ниже если указан ключ -On сервис включится, а если его нет выключится:
function Switch-ServiceTest ([switch]$on) {
if ($on) {Write-Output "Сервис включен"}
else {"Сервис выключен"}
}
Switch-ServiceTest -On
Switch-ServiceTest
Вы наверняка работали через команды Powershell, которые позволяли использовать конвейер следующим образом:
Get-Process -Name *TestProc* | Stop-Process
Если мы захотим использовать подход описанный выше, создав новые команды в виде функций, то конвейер не будет работать:
function Get-SomeNum {
# Генерация числа
$num = Get-Random -Minimum 5 -Maximum 10
return $num
}
function Plus-SomeNum ($num) {
Write-Host "Прибавление числа " $num
$num += $num
return $num
}
Get-SomeNum
Plus-SomeNum 5
Get-SomeNum | Plus-SomeNum
Выполнив следующую команду мы сможем увидеть, что значения, которые могут приниматься через конвейер помечаются специальным атрибутом:
Get-Help Stop-Process -Parameter Name
Таких атрибутов всего два:
- ValueFromPipelineByPropertyName — получение значения из конвейера по имени;
- ValueFromPipeline — получение через конвейер только значения .
Кроме этого, внутри нашей функции, мы должны добавить специальный блок Process. Наш скрипт в итоге будет выглядеть так:
function Get-SomeNum {
$num = Get-Random -Minimum 5 -Maximum 10
return $num
}
function Plus-SomeNum {
[cmdletbinding()]
Param (
[parameter(ValueFromPipeline=$True)]
[int]
$num
)
process {
Write-Host "Прибавление числа " $num
$num += $num
return $num
}
}
1..5 | Plus-SomeNum
Get-SomeNum | Plus-SomeNum
[cmdletbinding()] — атрибут расширения функции, который добавляет некоторые возможности в функции позволяя им работать как команду.
Если бы мы не указали блок Process функция бы вернула только последней результат из массива 1..5:
Если наши команды будут иметь критический характер, такой как удаление, или через конвейер может передаваться несколько значений, то стоит использовать атрибут ValueFromPipelineByPropertyName. Таким образом мы исключим попадания через конвейер случайного значения. На примере ниже я изменил
function Get-SomeNum {
$num = Get-Random -Minimum 5 -Maximum 10
$object = [pscustomobject]@{num=$num}
return $object
}
function Plus-SomeNum {
[cmdletbinding()]
Param (
[parameter(ValueFromPipelineByPropertyName=$True)]
[int]
$num
)
process {
Write-Host "Прибавление числа " $num
$num += $num
return $num
}
}
Get-SomeNum | Plus-SomeNum
[pscustomobject]@{num=5} | Plus-SomeNum
[pscustomobject]@{bad=5} | Plus-SomeNum
Как уже писалось ValueFromPipelineByPropertyName принимает только именованные параметры и в случае с именем «bad» мы получаем ошибку:
- Не удается привязать объект ввода к любым параметрам команды, так как команда не принимает входные данные конвейера
- The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
Причем передавать именованные параметры через хэш таблицы мы не можем, только через pscustomobject.
Вы можете указывать сразу два атрибута таким образом:
[parameter(ValueFromPipelineByPropertyName,ValueFromPipeline)]
Это позволит использовать и значение с именем, если оно указано либо без него. Это не спасет вас от ситуации, если вы передаете параметр с другим именем:
Передача через конвейер нескольких значений
Для примера рассмотрим ситуацию, где нам нужно передать через конвейер два значения. Если Get-SomeNum будет возвращать массив, то через конвейер у нас будет проходить каждое число по отдельности. Это еще один повод использовать именованные параметры:
function Get-SomeNum {
$number1 = Get-Random -Minimum 5 -Maximum 10
$number2 = Get-Random -Minimum 1 -Maximum 5
$object = [pscustomobject]@{num1=$number1;num2=$number2}
return $object
}
function Plus-SomeNum {
[cmdletbinding()]
Param (
[parameter(ValueFromPipelineByPropertyName=$true,
ValueFromPipeline=$true,
Mandatory=$true)]
[int]
$num1,
[parameter(ValueFromPipelineByPropertyName=$true,
ValueFromPipeline=$true,
Mandatory=$true)]
[int]
$num2
)
begin {$num1 += $num1
$num2 = $num2 * $num2}
process {
return @{"Результат сложения"=$num1; "Результат умножения"=$num2}
}
}
Get-SomeNum | Plus-SomeNum
При вызове справки на любой командлет мы получим такую информацию:
Описание функции, так же как и ее именование относится к рекомендованным действиям. Что бы это сделать нужно после объявления функции заполнить соответствующий блок. Я заполнил этот блок для одного из примеров:
function Get-SomeNum {
<#
.SYNOPSIS
(короткое описание) Получение случайного числа
.DESCRIPTION
(полное описание) Получение случайного числа от 1 до 3
.EXAMPLE
(пример) Get-Random
.EXAMPLE
(второй)
.PARAMETER num
(описание параметра) Параметр ни на что не влияет
.PARAMETER num2
(описание второго)
#>
[CmdletBinding()]
param (
[int]
$num
)
$num = Get-Random -min 1 -Max 3
return $num
}
Get-SomeNum
Некоторые виды описаний, например Examples, можно использовать несколько раз.
Скорее всего нашу функцию или готовый командлет мы захотим использовать и далее. В зависимости от ситуации мы должны сохранять и загружать его разными способами.
Импорт на множество компьютеров
Если это командлет, который будет использоваться на множестве компьютеров или вы его планируете использовать короткое время, то скрипт можно сохранить в файл с расширением «.ps1». Загрузка такой функции будет выполняться так:
Import-Module C:\funct.ps1 -Force
После выполнения этой команды мы сможем использовать нашу функцию.
Минус такого способа в том, что нужно будет делать каждый раз после закрытия консоли (сессии).
Такой подход хорошо подходит в удаленных сценариях, когда на компьютерах пользователей нужно сделать какую-то работу.
Загрузка как модуля
Если вы планируете использовать функцию на своем компьютере, то вы можете загрузить эту команду как модуль. Вы можете использовать и на других компьютерах, но я считаю это плохим вариантом.
Первое что нужно сделать это получить пути окружения Powershell:
$env:PSModulePath
Выберете один из путей, где лежат модули или перейдите по следующему:
C:\Users\%username%\Documents\WindowsPowerShell\Modules
В указанной папке Modules вам нужно создать папку и файл с одинаковыми именами. Файлу добавляете расширение «.psm1» и помещаете в него свой скрипт.
В моём случае путь выглядит так:
C:\Users\%username%\Documents\WindowsPowerShell\Modules\Test\Test.psm1
После этого закройте все окна Powershell и откройте заново. Модуль будет импортироваться автоматически. Проверти что он загружен можно с помощью команды:
Get-Module -ListAvailable -Name "*Часть имени папки*"
…
Теги: #powershell
Удаленное управление с помощью PowerShell
Существует довольно много методов для работы с удаленными компьютерами. Есть Windows Management Instrumentation (WMI), широко используемый в VBScript. Есть различные утилиты, которые позволяют осуществлять удаленное управление, типа PSExec от Sysinternals. Даже многие командлеты PowerShell имеют параметр ComputerName для выполнения на удаленных компьютерах.
В общем методов полно, но у каждого из них есть свои минусы. Во первых — разный синтаксис, в котором легко запутаться. Во вторых — некоторые команды ведут себя по разному в зависимости от того, локально или удаленно они выполняются. Ну и наконец, для связи может потребоваться открытие дополнительных портов на брандмауэре, что не есть хорошо с точки зрения безопасности.
PowerShell Remoting решает большинство описанных проблем. Он основан на Microsoft реализации протокола Web Services for Management (WS-Management), а для связи использует службу Windows Remote Management (WinRM). Связь между компьютерами осуществляется по HTTP (по умолчанию) или HTTPS. Весь трафик между двумя компьютерами шифруется на уровне протокола (за исключением случаев, когда используется SSL). Поддерживаются несколько методов аутентификации, включая NTLM и Kerberos.
В отличие от утилит, использующих различные программные интерфейсы, PS Remoting работает следующим образом: команды, вводимые на локальном компьютере, передаются на удаленный компьютер и там выполняются, затем результат передается обратно. Поскольку все команды выполняются локально, нет необходимости заботится о совместимости. Кроме того, для работы PS Remoting нужен всего один открытый порт на брандмауэре.
Есть несколько способов управления с помощью PowerShell Remoting.
Управление «один к одному»
Самый простой способ удаленного управления — интерактивно открыть удаленную сессию и в ней выполнить нужные действия. Например, откроем сессию на компьютер SRV4 и рестартуем на нем сервис печати:
Enter-PSSession -ComputerName SRV4
Restart-Service -Name spooler
Посмотрим состояние сервиса и закроем удаленную сессию:
Get-Service -Name spooler
Exit-PSSession
Интерактивная работа подходит для решения несложных задач удаленного администрирования. Если же надо автоматизировать процесс, то лучше воспользоваться командлетом Invoke-Command. Вот так с его помощью можно сделать то же самое действие:
Invoke-Command -ScriptBlock {Restart-Service spooler} -ComputerName SRV4
Эта команда откроет удаленную сессию на SRV4, выполнит блок команд, указанный в параметре -ScriptBlock, и закроет сессию. А чтобы задание выполнялось в фоновом режиме, дополнительно можно указать параметр -AsJob.
Cледует помнить о том, что при работе в фоновом режиме PowerShell не возвращает результат. Для его получения придется воспользоваться командлетом Receive-Job.
Для того, чтобы выполнить не пару-тройку команд, а какой либо скрипт, у Invoke-Command есть параметр –FilePath, который можно использовать вместо –ScriptBlock для определения файла сценария. Для примера я создал скрипт, который выводит список остановленных служб и запустил его на удаленной машине SRV4:
Invoke-Command -FilePath .\script.ps1 -ComputerName SRV4
Управление «один ко многим»
Довольно часть возникает необходимость параллельно выполнить одну задачу на нескольких компьютерах. Это довольно легко можно сделать с помощью того же Invoke-Command. Например, имена компьютеров можно просто перечислить через запятую:
Invoke-Command -ScriptBlock {Restart-Service spooler} -ComputerName SRV4,SRV5
Поместить в переменную:
$servers = @(″SRV1″,″SRV2″,″SRV3″)
Invoke-Command -ScriptBlock {Restart-Service spooler} -ComputerName $servers
Или взять из файла:
Invoke-Command -ScriptBlock {Restart-Service spooler} -ComputerName`
(Get-Content .\servers.txt)
Примечание: у Invoke-Command есть параметр ThrottleLimit, ограничивающий максимальное количество компьютеров, которыми можно управлять одновременно. По умолчанию этот параметр равен 32. При необходимости его можно изменить, но учтите, что повышение этого параметра увеличит нагрузку на процессор и память вашего компьютера, поэтому эту операцию нужно выполнять с большой осторожностью.
Сессии
Каждый раз при выполнении Invoke-Command создается новая сессия, на создание которой тратится время и ресурсы. Чтобы этого избежать мы можем открыть одну сессию, в которой и выполнять все команды. Например, откроем сессию с именем SRV4 на компьютер SRV4 и поместим ее в переменную $session, а затем этой сессии выполним нашу задачу (остановим многострадальный spooler):
$session = New-PSSession -ComputerName SRV4 -Name SRV4
Invoke-Command -ScriptBlock {Get-Service spooler | Stop-Service}`
-Session $session
Сессия будет активна до того момента, пока мы не выйдем из консоли PowerShell. Также сессию можно закрыть — Disconnect-PSSession или удалить — Remove-PSSession.
А теперь несколько интересных возможностей, появившихся в PowerShell 3.0. Если раньше при выходе из сессии или закрытии консоли сессия удалялась, то в PS 3.0 при закрытии сессия переходит в состояние disconnected. Мы можем открыть новый сеанс на этом же (или любом другом) компьютере и выполнить команду прямо в этой отключенной сессии. В качестве примера стартуем на компьютере SRV4 сервис печати, остановленный в прошлый раз:
Invoke-Command -ScriptBlock {Start-Service spooler}`
-ComputerName SRV4 -Disconnected
Еще один вариант использования отключенных сессий — запуск длительных по времени задач. Для примера откроем сессию c именем LongJob на SRV4 и запустим в ней фоновое задание, которое будет выводить список сервисов с интервалом в 1 минуту:
$session = New-PSSession -ComputerName SRV4 -Name LongJob
Invoke-Command -Session $session -ScriptBlock`
{Get-Service | foreach {$_;sleep 60} } -AsJob
Посмотрим, как выполняется задача и закроем сессию:
Receive-Job -Name Job2
Disconnect-PSSession $session
Идем на другой компьютер и открываем консоль, Подключаемся к сессии LongJob и с помощью командлета Receive-PSSession получаем результат выполнения задания:
Connect-PSSession -Name LongJob -ComputerName SRV4
Receive-PSSession -Name LongJob
Или еще вариант, без явного подключения к сессии с помощью Connect-PSSession:
$session = Get-PSSession -Name LongJob -ComputerName SRV4
$job = Receive-PSSession $session -OutTarget Job
Receive-Job $job
Примечание: для того, чтобы результат остался в системе, Receive-Job надо использовать с параметром -Keep.
Неявное удаленное управление
Еще один, довольно нестандартный способ удаленного управления — неявное удаленное управление (Implicit remoting). Используя его можно, не создавая удаленной сессии, локально выполнять командлеты, находящиеся на удаленном компьютере.
Для примера берем обычную рабочую станцию, без установленных средств удаленного администрирования. Создаем удаленную сессию с контроллером домена SRV4 и импортируем в эту сессию модуль Active Directory:
$session = New-PSSession -ComputerName SRV4
Invoke-Command {Import-Module ActiveDirectory} -Session $session
Затем экспортируем из удаленной сессии командлеты Active Directory и помещаем их в локальный модуль RemoteAD:
Export-PSSession -Session $session -CommandName *-AD* -OutputModule RemoteAD`
-AllowClobber
Эта команда создаст в папке WindowsPowerShell\Modules\RemoteAD новый модуль PowerShell. Загружены будут только командлеты с именами, соответствующими шаблону *-AD*. При этом сами командлеты не копируются на локальный компьютер. Локальный модуль служит своего рода ярлыком, а сами команды будут выполняться на удаленном контроллере домена.
После создания модуля удаленную сессию можно закрыть, она больше не понадобится.
Импортируем новый модуль в текущий сеанс (в PS 3.0 можно этот шаг пропустить):
Import-Module RemoteAD
А теперь внимание — мы не открываем удаленную сессию с контроллером домена, где расположены командлеты. Не нужно явно запускать этот сеанс — это можно сделать неявно, попытавшись выполнить удаленные командлеты:
New-ADUser -Name BillGates -Company Microsoft
Get-ADUser BillGates
При этом будет восстановлено удаленное подключение к контроллеру домена, после чего команда будет передана на контроллер домена и там выполнена. Результат выполнения будет сериализован в XML и передан по сети на локальный компьютер, где будет выполнена десериализация в объекты, с которыми может работать PowerShell.
Удаленный сеанс будет активным до тех пор, пока вы не закроете консоль или не удалите модуль RemoteAD.
Неявное удаленное управление позволяет работать с командлетами на удаленном компьютере практически так же, как если бы они были установлены на локальной машине. При этом все необходимые командлеты всегда под рукой, что довольно удобно.
В заключение скажу, что на данный момент PowerShell Remoting является основным инструментом для удаленного управления операционными системами Windows. Поэтому знать о его возможностях и уметь ими пользоваться просто необходимо любому Windows-администратору.