Перейти к основному содержимому

Автоматизация задач в Windows с помощью PowerShell

Разработчику Архитектору Инженеру
Маршрут Windows

Терминал и CMDсправочник команд → простые цепочки в BAT → этот материал про .ps1.

Сначала о модели автоматизации — 124 PowerShell.

Для Linux-серверов параллельный путь — скрипты Unix.

Play ITЗагрузка интерактивного демо…


Скрипты в среде Windows на PowerShell

Если BAT-файл из статьи про сценарии CMD перестаёт хватать — нужны объекты, JSON, модули Azure AD или нормальная обработка ошибок — следующий шаг PowerShell. Скрипт .ps1 запускается так же из консоли или планировщика, но внутри вы работаете с типизированными объектами .NET, а не только со строками CMD.

Скрипты в Windows — это программы, написанные на языке сценариев и предназначенные для управления операционной системой, её компонентами и приложениями без прямого участия человека. Автоматизация в Windows изначально развивалась через пакетные файлы (.bat, .cmd) и visual-basic Script (.vbs). Эти технологии выполняли базовые задачи — запуск программ, копирование файлов, управление переменными окружения. Однако их возможности были ограничены — слабая интеграция с системными API, отсутствие строгой типизации, минимальные средства обработки ошибок, трудности в работе с объектами.

PowerShell появился как ответ на вызовы современной ИТ-инфраструктуры — рост числа управляемых узлов, усложнение конфигураций, необходимость централизованного администрирования. Это не просто оболочка, как Command Prompt. PowerShell — это полноценная платформа автоматизации, основанная на .NET Framework (в Windows PowerShell) и .NET Core/.NET 5+ (в PowerShell Core / PowerShell 7+). Она встроена в каждую версию Windows начиная с Windows 7 и Windows Server 2008 R2, и поддерживается на всех современных серверных и клиентских редакциях.

Главное отличие PowerShell от предшественников — его объектная природа. В традиционных текстовых оболочках (например, в Bash) команды обмениваются текстом. Команда ls -l выдаёт строку текста, которую grep парсит по шаблону, cut разрезает на колонки, awk агрегирует числа. Каждый инструмент работает с потоком символов, и программист отвечает за корректность структуры этого потока. В PowerShell команды (cmdlet’ы) передают друг другу объекты — экземпляры классов .NET с именованными свойствами и методами. Когда вы вызываете Get-Process, вы получаете не строки, а массив объектов типа System.Diagnostics.Process. У каждого объекта есть свойства — Id, Name, CPU, WorkingSet; и методы — Kill(), WaitForExit(), Refresh(). Последующие команды (Where-Object, Sort-Object, Select-Object) работают непосредственно со свойствами и методами, не требуя синтаксического разбора текста. Это исключает ошибки в парсинге, повышает надёжность и делает скрипты самодокументируемыми.


Что такое PowerShell

PowerShell — это комбинация из трёх компонентов:

  1. Оболочка командной строки (shell) — интерфейс, в который пользователь вводит команды. Это программа powershell.exe (для Windows PowerShell) или pwsh.exe (для PowerShell 7+). Она загружает среду выполнения, обрабатывает ввод, выводит результаты, управляет сессией.

  2. Язык сценариев — декларативный и императивный язык, поддерживающий переменные, условные конструкции, циклы, функции, классы, модули, обработку исключений, потоки и асинхронные операции. Синтаксис заимствован из языков C-семейства, но адаптирован под административные задачи — глагол-существительное в именах команд (Get-ChildItem, Set-Location, Test-Path), конвейерная передача (|), автоматическое связывание параметров.

  3. Платформа управления — набор командлетов (cmdlet), провайдеров (provider), модулей и API, позволяющих управлять практически всем в системе — файловой системой, реестром, сетевыми интерфейсами, службами, задачами планировщика, сертификатами, WMI/CIM, Active Directory, Azure, Office 365 и сотнями других компонентов. Большинство командлетов реализованы как .NET-классы, унаследованные от Cmdlet или PSCmdlet. Это позволяет сторонним разработчикам создавать собственные расширения, интегрируя PowerShell в любые системы.

PowerShell поддерживает кроссплатформенную работу: начиная с версии 6.0, он работает не только в Windows, но и в Linux, macOS, даже в контейнерах Docker и на устройствах ARM. Это делает его универсальным инструментом для гибридных и облачных сред.


Файлы скриптов

Скрипт PowerShell — это текстовый файл с расширением .ps1. Простейший скрипт может содержать одну команду:

Write-Host "Привет, Вселенная IT!"

Более сложный скрипт включает параметры, проверки, циклы, обработку ошибок:

param(
[string]$Path = ".",
[switch]$Recurse
)

if (-not (Test-Path $Path)) {
Write-Error "Путь '$Path' не существует."
exit 1
}

$files = Get-ChildItem -Path $Path -File -Recurse:$Recurse
Write-Host "Найдено $($files.Count) файлов."
$files | ForEach-Object {
Write-Host " $($_.Name)$($_.Length) байт"
}

Такой файл сохраняется как List-Files.ps1. Его содержимое — это программа, которую PowerShell интерпретирует построчно (или компилирует в памяти для повышения производительности). Скрипт может быть запущен многократно, с разными параметрами, в разных контекстах.

Кроме .ps1, в экосистеме PowerShell используются:

  • .psm1 — файлы модулей (загрузка: Import-Module <имя>).

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

  • .ps1xml — файлы форматирования и типов. Они определяют, как объекты отображаются в консоли (столбцы, ширина, цвета), какие методы и свойства доступны по умолчанию.

Эти расширения не требуют дополнительной настройки — PowerShell распознаёт их по имени и применяет соответствующие правила обработки.


Политика выполнения

PowerShell включает встроенную систему безопасности, называемую политикой выполнения (Execution Policy). Эта политика не является средством защиты от вредоносного ПО, а скорее инструментом предотвращения случайного запуска непроверенного кода. По умолчанию на клиентских системах (Windows 10/11) установлена политика Restricted. Она блокирует выполнение всех скриптов, включая локальные, и разрешает только интерактивный ввод команд в консоли.

Другие распространённые политики:

  • AllSigned — разрешает выполнение только тех скриптов, которые подписаны цифровой подписью доверенного издателя. Это используется в строго контролируемых корпоративных средах.

  • RemoteSigned — разрешает запуск локальных скриптов без подписи, но требует подписи для скриптов, скачанных из интернета (например, через браузер или почту). Эта политика считается оптимальной для большинства пользователей.

  • Unrestricted — разрешает запуск всех скриптов, но перед выполнением скачанного скрипта выводит предупреждение. Подходит для изолированных сред разработки и тестирования.

  • Bypass — отключает все проверки. Используется в автоматизированных системах, где скрипты запускаются без участия человека.

Политику можно проверить командой:

Get-ExecutionPolicy

Изменение политики выполнения:

Set-ExecutionPolicy RemoteSigned -Scope CurrentUser

Важно: политика выполнения хранится в реестре и применяется на этапе запуска сессии PowerShell. Она не влияет на выполнение команд, введённых вручную, и не блокирует вызов .NET-методов напрямую.

Типичная ситуация на домашнем ПК: при двойном щелчке по .ps1 появляется сообщение, что выполнение скриптов запрещено. Для учебных скриптов своего авторства достаточно (один раз, от своего пользователя):

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

Скрипты, созданные локально, запустятся; файлы, скачанные из браузера, Windows помечает зоной "из интернета" — может понадобиться Unblock-File .\script.ps1. В корпоративной сети политику задаёт GPO: администратор может запретить Set-ExecutionPolicy — тогда запускайте подписанные скрипты или используйте powershell -ExecutionPolicy Bypass -File script.ps1 только там, где это разрешено политикой безопасности.


Запуск скриптов

Скрипт можно запустить несколькими способами, каждый со своими особенностями.


1. Запуск из консоли PowerShell

Откройте PowerShell (через "Пуск" → "Windows PowerShell" или поиск). Перейдите в каталог со скриптом:

cd C:\Scripts

Выполните скрипт с помощью оператора вызова (&) или точечной нотации (dot-sourcing):

  • .\MyScript.ps1 — запускает скрипт в дочерней области видимости. Переменные, созданные внутри скрипта, не попадают в текущую сессию после завершения. Это стандартный и безопасный способ.

  • . .\MyScript.ps1dot-source — загружает скрипт в текущую область видимости. Функции и переменные остаются доступны после завершения. Используется для загрузки профилей, библиотек функций, настройки окружения.


2. Запуск из командной строки (cmd.exe) или ярлыка

Можно вызвать PowerShell из любого места системы, передав путь к скрипту:

powershell.exe -File "C:\Scripts\MyScript.ps1"

Ключ -File гарантирует, что аргумент интерпретируется как путь к файлу, даже если имя содержит пробелы или специальные символы. Альтернатива — -Command, но она требует экранирования и менее надёжна для сложных скриптов.

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


3. Запуск через контекстное меню

Если щёлкнуть правой кнопкой по файлу .ps1 в Проводнике, в меню появится пункт "Выполнить с помощью PowerShell". Эта команда запускает скрипт в новом окне консоли с политикой, установленной по умолчанию. Окно остаётся открытым после завершения — это позволяет увидеть результаты и ошибки. Для автоматических задач такой способ не подходит, но он удобен при тестировании.


4. Запуск через PowerShell ISE или VS Code

Интегрированные среды предоставляют кнопки "Запустить", "Запустить выделенное", "Отладка". Запуск происходит в рамках текущей сессии, что позволяет пошагово выполнять код, просматривать значения переменных, ставить точки останова.


Сравнение PowerShell и Bash

PowerShell и Bash — это не просто две разные командные оболочки. Это выражение двух различных подходов к управлению вычислительной средой: один — ориентированный на объекты и строгую типизацию, другой — на текст и композицию утилит.


Исторический контекст

Bash (Bourne-Again Shell) развивалась в среде Unix и Linux, где доминировала идея "всё есть файл". Даже устройства, процессы и сетевые сокеты представлены как файлы в виртуальной файловой системе. Это позволило создать утилиты, которые читают из stdin, пишут в stdout, и комбинируются через конвейер (|). Каждая утилита решает одну задачу, делает это хорошо, и передаёт результат — текст — следующей. Так родились grep, sed, awk, cut, sort, uniq. Эта модель чрезвычайно гибкая, но требует от пользователя знания синтаксиса регулярных выражений, экранирования и формата вывода каждой утилиты.

PowerShell появился позже, в 2006 году, в среде Windows, где API системных компонентов строились на COM и .NET — объектно-ориентированных технологиях. Разработчики PowerShell осознанно отказались от текстового конвейера в пользу объектного. Цель — сделать автоматизацию доступной для администраторов без глубоких знаний программирования, сохраняя при этом мощь и выразительность.


Конвейер

В Bash команда:

ps aux | grep nginx | awk '{print $2, $11}'

— сначала порождает текстовый вывод ps, затем grep фильтрует строки, содержащие "nginx", затем awk разбирает каждую строку по пробелам и выбирает второе и одиннадцатое поле. Если формат вывода ps изменится (например, добавится колонка с GPU-нагрузкой), скрипт сломается. Если имя процесса содержит пробелы, awk сдвинет индексы. Это хрупкая конструкция.

В PowerShell аналогичная задача:

Get-Process -Name nginx | Select-Object Id, Path

— получает массив объектов Process, фильтрует по свойству Name, затем выбирает два свойства: Id (идентификатор процесса) и Path (полный путь к исполняемому файлу). Формат объекта не зависит от способа отображения. Даже если Microsoft изменит текстовый вывод Get-Process, объекты останутся теми же. Свойства Id и Path — часть контракта API, и их изменение потребует критического обновления системы, что маловероятно.

Объектный конвейер исключает необходимость в grep/awk для базовой фильтрации и выборки. Вместо них используются универсальные командлеты:

  • Where-Object — фильтрация по условию ({ $_.CPU -gt 50 })
  • Sort-Object — сортировка по свойствам (-Property CPU -Descending)
  • Group-Object — группировка (-Property Company)
  • Measure-Object — агрегация (-Property WS -Sum -Average)
  • ForEach-Object — применение кода к каждому элементу ({ $_.Kill() })

Эти командлеты работают с любыми объектами, а не только с процессами. Это обеспечивает единообразие — один и тот же синтаксис применим к файлам, службам, сетевым адаптерам, сертификатам.


Синтаксис и читаемость

Bash использует компактный, насыщенный символами синтаксис:

  • $? — код возврата последней команды
  • $$ — PID текущего процесса
  • $! — PID последнего фонового процесса
  • &> — перенаправление stdout и stderr
  • [[ ]] — расширенное условие
  • $( ) — подстановка команды

Этот стиль экономит символы, но повышает порог входа. PowerShell выбирает многословность и самодокументируемость:

  • $LASTEXITCODE — понятное имя переменной
  • $PID — идентификатор процесса
  • Start-Process -NoNewWindow — параметры в именованной форме
  • if ($result -eq $true) — читаемое условие
  • & { Get-Date } — явный вызов скрипт-блока

Имена командлетов следуют схеме Глагол-Существительное (Get-Service, Stop-Process, New-Item, Test-Connection). Это позволяет угадывать команду по её назначению и легко находить её через Get-Command -Verb Stop или Get-Help *service*.


Ошибки и отладка

Bash по умолчанию не прерывает выполнение при ошибке. Скрипт продолжает работать, даже если команда завершилась с кодом 1. Это требует явной проверки $? после каждой критической операции. PowerShell по умолчанию использует останавливающие ошибки (terminating errors) для большинства командлетов. Если Get-ChildItem не находит путь, он генерирует исключение. Это принуждает к осознанной обработке сбоев.

PowerShell поддерживает полноценную обработку исключений:

try {
$content = Get-Content -Path "C:\secret.txt" -ErrorAction Stop
Write-Host "Файл прочитан."
}
catch [System.UnauthorizedAccessException] {
Write-Warning "Нет прав на чтение файла."
}
catch {
Write-Error "Неизвестная ошибка: $($_.Exception.Message)"
}
finally {
Write-Host "Операция завершена."
}

Конструкция try/catch/finally работает как в C# или Java. Это делает код предсказуемым и устойчивым к внештатным ситуациям.


Провайдеры

Bash работает преимущественно с файловой системой. Доступ к реестру, сертификатам или переменным окружения требует специальных утилит (reg, certutil, printenv) или встроенных команд (declare, export).

PowerShell вводит концепцию провайдеров (providers). Провайдер — это адаптер, который отображает произвольное хранилище данных в иерархию, похожую на файловую систему. Встроенные провайдеры:

  • FileSystem — диски, папки, файлы
  • Registry — разделы и параметры реестра Windows (HKLM:\SOFTWARE)
  • Environment — переменные окружения (Env:\PATH)
  • Certificate — сертификаты (Cert:\CurrentUser\My)
  • Alias — псевдонимы команд (Alias:\cd)
  • Variable — переменные сессии (Variable:\PWD)
  • Function — определённые функции (Function:\Write-Host)

Команды Get-ChildItem, Get-Item, Set-Item, New-Item, Remove-Item работают со всеми провайдерами одинаково:

# Просмотр переменных
Get-ChildItem Env:

# Чтение параметра реестра
(Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager").BootExecute

# Добавление переменной окружения
New-Item -Path Env:\MY_APP_CONFIG -Value "Production"

# Удаление сертификата
Remove-Item -Path "Cert:\LocalMachine\My\$thumbprint"

Это создаёт единый язык управления разнородными данными.


Интегрированная среда сценариев (ISE)

Подробнее на Learn: Introducing the Windows PowerShell ISE.

Windows PowerShell ISE — это официальная графическая среда разработки скриптов, поставляемая вместе с Windows. Она включает три основные области:

  1. Панель меню и панель инструментов — кнопки "Создать", "Открыть", "Сохранить", "Запустить", "Остановить", "Выполнить выделенное". Каждая операция имеет горячие клавиши (например, F5 — запуск всего скрипта, F8 — выполнение выделенного фрагмента).

  2. Область сценариев — редактор кода с подсветкой синтаксиса, автоформатированием, сворачиванием блоков (#region/#endregion), навигацией по ошибкам. Поддерживается поиск и замена по регулярным выражениям, переход к строке по номеру (Ctrl+G), отмена и повтор действий (Ctrl+Z, Ctrl+Y).

  3. Область консоли — интерактивная сессия PowerShell. В отличие от отдельного окна консоли, здесь можно комбинировать выполнение скриптов и ручной ввод. Например, запустить скрипт, затем вызвать функцию из него, проверить значение переменной, изменить её и повторно вызвать функцию — всё в одном окне.


Отладка

ISE предоставляет полноценный отладчик:

  • Точки останова ставятся кликом слева от номера строки или через меню. Можно задать условие ($i -gt 10) или действие при остановке (вывести значение переменной).
  • Шаг с заходом (F11) — заходит внутрь функции.
  • Шаг с обходом (F10) — выполняет функцию целиком, не заходя внутрь.
  • Выполнение до курсора (Ctrl+F10) — запускает код до указанной строки.
  • Панель локальных переменных — отображает все переменные в текущей области видимости, их тип и значение. Можно изменить значение прямо в таблице.
  • Панель стека вызовов — показывает цепочку вызовов функций, позволяя "подняться" на уровень выше.

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


Работа с профилями и модулями

ISE автоматически загружает пользовательский профиль при запуске. Профиль — это скрипт Microsoft.PowerShellISE_profile.ps1, расположенный в папке Documents\WindowsPowerShell. В нём можно:

  • Задать псевдонимы (Set-Alias ll Get-ChildItem)
  • Определить вспомогательные функции (function prompt { "PS> " })
  • Настроить цветовую схему ($psISE.Options.ConsoleTokenColors['String'] = 'DarkGreen')
  • Импортировать часто используемые модули (Import-Module ActiveDirectory)

ISE также поддерживает работу с модулями. Можно открыть .psm1-файл, внести изменения, нажать F5 — и модуль перезагрузится в текущей сессии. Это ускоряет цикл "написать — проверить — доработать".

Примечание: Начиная с Windows 10 версии 1903, Microsoft объявила ISE устаревшей и рекомендует использовать Visual Studio Code с расширением PowerShell. Однако ISE остаётся в системе по совместимости и удобна для быстрого запуска и отладки без установки дополнительных инструментов.


Структура скрипта

Простой одноразовый скрипт — это последовательность команд. Профессиональный скрипт — это программная единица, которая:

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

Параметры

В начале скрипта объявляется блок param(). Он определяет, какие аргументы может принимать скрипт, их тип, значение по умолчанию, обязательность:

param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$ComputerName,

[int]$Port = 80,

[ValidateSet("HTTP", "HTTPS")]
[string]$Protocol = "HTTP",

[switch]$Verbose
)
  • [Parameter(Mandatory = $true)] требует обязательного указания параметра.
  • [ValidateNotNullOrEmpty()] проверяет, что строка не пустая и не null.
  • [ValidateSet(...)] ограничивает значения списком.
  • [switch] — это булев флаг, который активируется, если указан (например, -Verbose).

Параметры можно передавать:

  • По имени: .\Test-Connection.ps1 -ComputerName srv01 -Port 443
  • Позиционно: .\Test-Connection.ps1 srv01 443 (если порядок соблюдён)
  • Через хэш-таблицу (splatting):
$params = @{ ComputerName = "srv01"; Port = 443; Verbose = $true }
.\Test-Connection.ps1 @params

Splatting особенно удобен при вызове командлетов с множеством параметров.


Документация

PowerShell поддерживает стандарт комментариев на основе XML — комментарии помощи (comment-based help). Они размещаются в начале скрипта или функции и описываются так:

<#
.SYNOPSIS
Проверяет доступность веб-сервера.

.DESCRIPTION
Отправляет HTTP-запрос на указанный хост и порт, возвращает код ответа.

.PARAMETER ComputerName
Имя или IP-адрес сервера.

.PARAMETER Port
Порт (по умолчанию — 80).

.EXAMPLE
Test-WebServer -ComputerName example.com -Port 443
#>

После этого команда Get-Help .\Test-WebServer.ps1 выводит отформатированное описание, как для встроенных командлетов. Это делает скрипты самодокументируемыми и интегрируемыми в обучающие материалы.


Обработка ошибок

В скриптах используются три уровня обработки:

  1. Предварительная проверкаTest-Path, Test-Connection, Test-ModuleManifest. Эти командлеты возвращают $true/$false без исключений.

  2. Управляемые исключенияtry/catch, как показано выше.

  3. Глобальные настройки — параметр -ErrorAction у каждой команды:

    • Stop — превращает неостанавливающую ошибку в исключение.
    • Continue — выводит ошибку, но продолжает выполнение (по умолчанию).
    • SilentlyContinue — подавляет вывод ошибки.
    • Ignore — подавляет ошибку и не записывает её в $Error.

Пример надёжного чтения файла:

$content = Get-Content -Path $Path -ErrorAction SilentlyContinue
if (-not $content) {
Write-Warning "Файл '$Path' пуст или недоступен."
return
}

Такой подход позволяет избежать аварийного завершения и дать пользователю осмысленную обратную связь.


Сквозной пример — скрипт под планировщик

Ниже один файл, который объединяет параметры, проверки, лог, обработку ошибок и готов к запуску из Планировщика заданий (аналог cron/systemd на Linux — скрипты Unix).

Сохраните как C:\Scripts\Sync-ProjectLogs.ps1:

<#
.SYNOPSIS
Копирует логи проекта в архивную папку с датой в имени.
.EXAMPLE
.\Sync-ProjectLogs.ps1 -SourcePath C:\App\logs -ArchiveRoot D:\LogArchive
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateScript({ Test-Path $_ -PathType Container })]
[string]$SourcePath,

[string]$ArchiveRoot = "D:\LogArchive",

[int]$KeepDays = 14
)

$ErrorActionPreference = 'Stop'
$LogFile = Join-Path $ArchiveRoot ("sync_{0:yyyyMMdd}.log" -f (Get-Date))

function Write-Log {
param([string]$Message)
$line = "[{0:yyyy-MM-dd HH:mm:ss}] {1}" -f (Get-Date), $Message
Add-Content -Path $LogFile -Value $line -Encoding UTF8
Write-Host $line
}

try {
if (-not (Test-Path $ArchiveRoot)) {
New-Item -Path $ArchiveRoot -ItemType Directory -Force | Out-Null
}

$stamp = Get-Date -Format "yyyy-MM-dd_HHmmss"
$dest = Join-Path $ArchiveRoot "logs_$stamp"
Write-Log "Старт: $SourcePath -> $dest"

Copy-Item -Path (Join-Path $SourcePath '*') -Destination $dest -Recurse -Force

$cutoff = (Get-Date).AddDays(-$KeepDays)
Get-ChildItem -Path $ArchiveRoot -Directory -Filter 'logs_*' |
Where-Object { $_.CreationTime -lt $cutoff } |
ForEach-Object {
Write-Log "Удаляю старый архив: $($_.FullName)"
Remove-Item -LiteralPath $_.FullName -Recurse -Force
}

Write-Log "Успешно завершено."
exit 0
}
catch {
Write-Log "ОШИБКА: $($_.Exception.Message)"
exit 1
}

Запуск вручную (после Set-ExecutionPolicy RemoteSigned -Scope CurrentUser, см. выше):

powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\Scripts\Sync-ProjectLogs.ps1" -SourcePath "C:\App\logs"

Планировщик заданий Windows — типичные настройки:

ВкладкаЗначение
ДействиеПрограмма: powershell.exe
Аргументы-NoProfile -ExecutionPolicy Bypass -File "C:\Scripts\Sync-ProjectLogs.ps1" -SourcePath "C:\App\logs"
Рабочая папкаC:\Scripts (необязательно, но удобно для относительных путей)
ТриггерЕжедневно, 03:00
УсловияПри питании от сети (для ноутбуков)

Проверка последнего кода выхода в планировщике: в истории запусков смотрите код 0 / 1; в скрипте exit 1 при ошибке — планировщик может настроить повтор или оповещение.

Для задач сложнее архивации логов тот же каркас переносится: paramtry/catchWrite-Logexit с кодом. В BAT аналог — %ERRORLEVEL% и перенаправление в .log.


Триггеры автоматизации — расписание и наблюдатели

Скрипт .ps1 — это действия в модели автоматизации. Запускает его триггер. Концепция целиком — в 124 PowerShell; углублённо — Триггеры — расписание и наблюдатели. Здесь — практика в Windows.

Тип триггераСутьТипичный инструмент в Windows
РасписаниеСкрипт стартует по календарю, условие не проверяетсяПланировщик заданий, Register-ScheduledTask (ниже)
НаблюдательЖдёт условие — новый файл, порог диска, строка в логеFileSystemWatcher, цикл с Start-Sleep
СобытиеВнешний сигнал — веб-хук, CI, ручной запускGitHub Actions, Jenkins, вызов из другого скрипта

Расписание подходит для очистки, синхронизации и отчётов "раз в ночь". Наблюдатель — когда реагировать нужно на появление файла в папке, а не на время суток. Интервал опроса выбирают под задачу: если данные приходят раз в час, проверка каждую минуту лишняя нагрузка.

Минимальный наблюдатель за папкой (скрипт-исполнитель вызывают отдельно — так проще тестировать):

$folder = 'C:\Inbound'
$filter = '*.csv'
$action = {
param($sender, $eventArgs)
$path = $eventArgs.FullPath
Write-Host "Новый файл: $path"
# Start-Process pwsh -ArgumentList '-File C:\Scripts\Process-Inbound.ps1', $path
}

$watcher = New-Object System.IO.FileSystemWatcher $folder, $filter
$watcher.IncludeSubdirectories = $false
$watcher.EnableRaisingEvents = $true
Register-ObjectEvent -InputObject $watcher -EventName Created -Action $action

Write-Host "Наблюдатель за $folder ($filter). Ctrl+C для остановки."
while ($true) { Start-Sleep -Seconds 5 }
Watcher в продакшене

Долгоживущий наблюдатель удобнее оформить как службу Windows или задачу планировщика с частым перезапуском; для простых сценариев достаточно cron-подобного "опроса" — Get-ChildItem + сравнение с прошлым состоянием. На Linux тот же паттерн — inotify и systemd — см. скрипты Unix.


Регистрация задачи из PowerShell — Register-ScheduledTask

GUI планировщика удобен для разовой настройки; тот же результат воспроизводим в коде — удобно для образов ПК, onboarding и DevOps.

$scriptPath = 'C:\Scripts\Sync-ProjectLogs.ps1'
$source = 'C:\App\logs'

$action = New-ScheduledTaskAction `
-Execute 'powershell.exe' `
-Argument "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`" -SourcePath `"$source`"" `
-WorkingDirectory 'C:\Scripts'

$trigger = New-ScheduledTaskTrigger -Daily -At '03:00'

$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable

$principal = New-ScheduledTaskPrincipal `
-UserId "$env:USERDOMAIN\$env:USERNAME" `
-LogonType S4U `
-RunLevel Limited

Register-ScheduledTask `
-TaskName 'ITUniverse-SyncProjectLogs' `
-Action $action `
-Trigger $trigger `
-Settings $settings `
-Principal $principal `
-Description 'Архив логов проекта (пример из энциклопедии)'

Get-ScheduledTask -TaskName 'ITUniverse-SyncProjectLogs' | Get-ScheduledTaskInfo
ПараметрСмысл
-RunLevel LimitedБез прав администратора (для задач в профиле пользователя)
-RunLevel HighestКак "с наивысшими правами" в GUI — только если скрипту это нужно
-StartWhenAvailableДогнать пропуск, если ПК был выключен в 03:00
Unregister-ScheduledTask -TaskName '...' -Confirm:$falseУдалить задачу

Проверка вручную без ожидания триггера: Start-ScheduledTask -TaskName 'ITUniverse-SyncProjectLogs', затем журнал в C:\Scripts или Event Viewer → Task Scheduler.

Экспорт для другого ПК: Export-ScheduledTask -TaskName 'ITUniverse-SyncProjectLogs' -Path .\task.xml (импорт — Register-ScheduledTask -Xml).


Удалённое выполнение на Windows — PowerShell Remoting

Автоматизация редко ограничивается одним компьютером. PowerShell умеет запускать команды на других Windows-машинах по WinRM (WS-Management), а в PowerShell 7+ есть режим по SSH.

Базовый сценарий через WinRM:

$cred = Get-Credential
Invoke-Command -ComputerName SRV-APP-01 -Credential $cred -ScriptBlock {
Get-Service -Name wuauserv | Select-Object Name, Status
}

Постоянная сессия (удобно для серии команд):

$s = New-PSSession -ComputerName SRV-APP-01 -Credential $cred
Invoke-Command -Session $s -ScriptBlock { hostname; Get-Date }
Copy-Item -Path "C:\Scripts\Sync-ProjectLogs.ps1" -ToSession $s -Destination "C:\Scripts\"
Remove-PSSession $s
КомандаНазначение
Enable-PSRemoting -ForceВключить WinRM и правила firewall на целевой машине
Invoke-CommandВыполнить блок кода удалённо
New-PSSessionОткрыть постоянную удалённую сессию
Enter-PSSessionИнтерактивно "войти" в удалённый PowerShell

Минимальные требования для remoting по WinRM:

  • сеть и firewall позволяют порты WinRM (обычно 5985 HTTP / 5986 HTTPS);
  • целевой хост доверяет вашей аутентификации (домен или настроенный TrustedHosts);
  • учётная запись имеет права на удалённой машине;
  • при работе вне домена желательно использовать HTTPS или remoting по SSH.

Для PowerShell 7 можно использовать SSH-транспорт:

Invoke-Command -HostName srv-app-01 -UserName admin -ScriptBlock { Get-Process | Select-Object -First 5 }

Этот путь удобен в гибридных средах с Linux и Windows, где SSH уже стандарт.


Распространённые задачи

Скрипты PowerShell находят применение в десятках сценариев. Ниже — разбор типичных задач с пояснением логики, структуры и масштабируемости решений. Каждый пример можно использовать как шаблон и адаптировать под свои нужды.


1. Резервное копирование файлов и папок

Резервное копирование — одна из самых частых задач. Простой скрипт копирует папку с отметкой времени в имени архива:

param(
[string]$SourcePath = "C:\Projects",
[string]$BackupRoot = "D:\Backups"
)

$timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
$backupName = "Backup_$timestamp.zip"
$backupPath = Join-Path -Path $BackupRoot -ChildPath $backupName

# Создаём папку для бэкапов, если её нет
if (-not (Test-Path $BackupRoot)) {
New-Item -Path $BackupRoot -ItemType Directory -Force
}

# Используем встроенный .NET-метод для архивации
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::CreateFromDirectory($SourcePath, $backupPath)

Write-Host "Резервная копия создана: $backupPath"

Этот скрипт:

  • принимает параметры SourcePath и BackupRoot, задаёт значения по умолчанию;
  • генерирует уникальное имя с точной датой и временем;
  • проверяет существование целевой папки и создаёт её при необходимости;
  • использует класс ZipFile из .NET — это надёжнее вызова внешних утилит вроде 7z.exe;
  • выводит подтверждение.

Его можно расширить:

  • добавить проверку свободного места на диске (Get-PSDrive D | Select-Object Free);
  • включить сжатие с уровнем (CompressionLevel::Optimal);
  • отправить уведомление по электронной почте (Send-MailMessage);
  • удалить старые копии старше 30 дней (Get-ChildItem -Path $BackupRoot -Filter *.zip | Where-Object { $_.CreationTime -lt (Get-Date).AddDays(-30) } | Remove-Item).

Такой скрипт легко интегрируется в Планировщик заданий Windows — ежедневное, еженедельное или по триггеру (например, после завершения сборки проекта).


2. Управление правами доступа (ACL)

В Windows права доступа к файлам и папкам задаются через списки контроля доступа (ACL). Их настройка вручную — трудоёмкий процесс. Скрипт позволяет массово изменить права:

param(
[string]$Path = "C:\Shared",
[string]$User = "DOMAIN\Developers",
[string[]]$Permissions = @("ReadAndExecute", "Write")
)

$acl = Get-Acl -Path $Path

# Создаём правило доступа
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
$User,
$Permissions -join ",",
"ContainerInherit,ObjectInherit",
"None",
"Allow"
)

# Добавляем правило в ACL
$acl.SetAccessRule($accessRule)

# Применяем обновлённый ACL
Set-Acl -Path $Path -AclObject $acl

Write-Host "Права для '$User' назначены на '$Path'."

Скрипт:

  • получает текущий ACL через Get-Acl;
  • создаёт объект FileSystemAccessRule с указанием пользователя, прав (Read, Write, FullControl и др.), наследования и типа (разрешение/запрет);
  • добавляет правило в ACL;
  • применяет изменения.

Важные детали:

  • "ContainerInherit,ObjectInherit" означает, что права распространяются на подпапки и файлы.
  • Можно заменить SetAccessRule на AddAccessRule, если нужно добавить правило, не затирая существующие.
  • Для удаления прав используется RemoveAccessRule.

Такой подход исключает ошибки при ручной настройке и гарантирует единообразие политик безопасности.


3. Мониторинг использования ресурсов

Скрипт может отслеживать нагрузку на компьютер и реагировать на отклонения:

param(
[int]$CpuThreshold = 90,
[int]$MemoryThreshold = 85,
[int]$CheckIntervalSeconds = 60
)

while ($true) {
$cpu = (Get-Counter '\Processor(_Total)\% Processor Time').CounterSamples.CookedValue
$mem = (Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory /
(Get-CimInstance Win32_OperatingSystem).TotalVisibleMemorySize * 100
$freeMemPercent = 100 - $mem

Write-Host "[$(Get-Date -Format 'HH:mm:ss')] CPU: $([int]$cpu)% | Память: $([int]$freeMemPercent)% свободно"

if ($cpu -gt $CpuThreshold) {
Write-Warning "Высокая загрузка CPU: $([int]$cpu)%"
Get-Process | Sort-Object CPU -Descending | Select-Object -First 5 | Format-Table Id, Name, CPU
}

if ($freeMemPercent -lt (100 - $MemoryThreshold)) {
Write-Warning "Низкий уровень свободной памяти: $([int]$freeMemPercent)%"
}

Start-Sleep -Seconds $CheckIntervalSeconds
}

Этот скрипт:

  • использует Get-Counter для получения точных данных счётчиков производительности (в отличие от приблизительных значений в диспетчере задач);
  • вычисляет процент свободной памяти на основе CIM-модели;
  • выводит текущую статистику с отметкой времени;
  • при превышении порогов — показывает топ-5 процессов по CPU;
  • работает в бесконечном цикле с паузой через Start-Sleep.

Его можно доработать:

  • записывать лог в файл (Out-File -Append);
  • отправлять оповещение в Telegram через Invoke-RestMethod;
  • автоматически завершать "прожорливые" процессы по правилам.

4. Массовая установка программ через Chocolatey

Chocolatey — менеджер пакетов для Windows. Скрипт позволяет развернуть стандартный набор ПО на новом компьютере:

# Установка Chocolatey (если отсутствует)
if (-not (Get-Command choco -ErrorAction SilentlyContinue)) {
Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
}

$packages = @(
"googlechrome",
"firefox",
"vscode",
"7zip",
"git",
"nodejs",
"python3",
"docker-desktop"
)

foreach ($pkg in $packages) {
Write-Host "Устанавливаю $pkg..."
choco install $pkg -y --no-progress
}

Скрипт:

  • проверяет наличие choco;
  • при необходимости устанавливает его с правильным протоколом безопасности;
  • перебирает список пакетов и устанавливает их без подтверждения (-y);
  • отключает прогресс-бар для автоматизации.

Это решение ускоряет подготовку рабочих станций в корпоративной среде, обеспечивает единообразие версий и исключает "ручной" поиск установщиков.


Модули и реиспользуемость кода

Один и тот же код не должен дублироваться в десятках скриптов. PowerShell предлагает механизм модулей — логических единиц, объединяющих связанные функции, переменные, классы и данные.


Структура модуля

Модуль — это папка с именем MyTools, содержащая как минимум один файл .psm1:

MyTools/
├── MyTools.psd1 # манифест (опционально, но рекомендуется)
└── MyTools.psm1 # основной код

Файл .psm1 содержит функции:

# MyTools.psm1
function Get-FreeSpace {
param([string]$Drive = "C")
$info = Get-PSDrive $Drive
[PSCustomObject]@{
Drive = $Drive
FreeGB = [math]::Round($info.Free / 1GB, 2)
UsedGB = [math]::Round($info.Used / 1GB, 2)
TotalGB = [math]::Round(($info.Free + $info.Used) / 1GB, 2)
}
}

function Test-Url {
param([string]$Url)
try {
$request = Invoke-WebRequest -Uri $Url -Method Head -TimeoutSec 10
$request.StatusCode -eq 200
} catch {
$false
}
}

Export-ModuleMember -Function Get-FreeSpace, Test-Url
  • Export-ModuleMember указывает, какие функции будут доступны после импорта модуля.
  • Внутри модуля можно определять приватные вспомогательные функции — они не будут экспортированы.

Использование модуля

  1. Поместите папку MyTools в одну из стандартных директорий модулей:

    • Для текущего пользователя: ~\Documents\PowerShell\Modules\
    • Для всех пользователей: $env:ProgramFiles\PowerShell\Modules\
  2. Импортируйте модуль:

Import-Module MyTools
  1. Используйте функции:
Get-FreeSpace -Drive D
Test-Url "https://spirzen.ru"

Преимущества модулей

  • Инкапсуляция — внутренняя логика скрыта, интерфейс чётко определён.
  • Версионность — в .psd1 можно указать ModuleVersion = '1.2.0', что позволяет управлять обновлениями.
  • Зависимости — модуль может требовать другие модули (RequiredModules = @('ActiveDirectory')).
  • Автозагрузка — начиная с PowerShell 3.0, модули загружаются автоматически при вызове экспортируемой функции.

Модули — основа для построения библиотек внутренних инструментов в крупных организациях. Например, модуль ADTools для работы с Active Directory, BackupSystem для управления архивами, ReportGenerator для формирования отчётов в Excel или PDF.


Лучшие практики написания скриптов

Качественный скрипт — это не только рабочий код. Это документ, который другие могут читать, поддерживать и расширять.


1. Именование

  • Файлы скриптов и модулей — в стиле PascalCase: Backup-Database.ps1, NetworkTools.psm1.
  • Функции — Глагол-Существительное, согласно утверждённому списку глаголов: Get-LogEntry, Set-Configuration, Test-Connection.
  • Переменные — camelCase$inputPath, $userList, $maxRetries.

2. Безопасность

  • Избегайте Set-ExecutionPolicy Unrestricted в продакшене. Используйте RemoteSigned или цифровую подпись.
  • Не храните пароли в открытом виде. Используйте Get-Credential, ConvertFrom-SecureString, или менеджеры секретов (Azure Key Vault, HashiCorp Vault).
  • Запускайте скрипты с минимально необходимыми правами. Для административных задач — отдельная учётная запись.
  • Не копируйте Invoke-Expression и curl | iex из чатов без разбора — опасные скрипты.

Цифровая подпись скриптов (Authenticode)

Подпись подтверждает, что файл .ps1 не меняли после выпуска и (для доверенного издателя) кто его подписал. Политика AllSigned требует подпись для каждого скрипта; RemoteSigned — для скачанных из интернета, локальные можно без подписи.

Просмотр статуса:

Get-AuthenticodeSignature -FilePath 'C:\Scripts\Sync-ProjectLogs.ps1'

Подписать (нужен сертификат Code Signing в хранилище Cert:\CurrentUser\My или LocalMachine\My):

$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert |
Sort-Object NotAfter -Descending |
Select-Object -First 1

if (-not $cert) {
throw 'Нет сертификата подписи кода. В учебной среде: New-SelfSignedCertificate -Type CodeSigningCert ...'
}

Set-AuthenticodeSignature `
-FilePath 'C:\Scripts\Sync-ProjectLogs.ps1' `
-Certificate $cert `
-TimestampServer 'http://timestamp.digicert.com'
Тип сертификатаГде применяют
Самоподписанный (лаборатория)Свой ПК, после Set-ExecutionPolicy и доверия к отпечатку
Корпоративный CAДомен Windows, GPO раздаёт доверие
Публичный Code SigningСофт для внешних клиентов

Самоподписанный сертификат не делает скрипт безопасным для всех — он лишь привязывает хеш файла к ключу. В организации подпись сочетают с контролем доступа к папке C:\Scripts и ревью изменений, как для кода приложения.

После подписи в планировщике можно убрать -ExecutionPolicy Bypass из аргументов — достаточно -File, если политика домена доверяет издателю.


3. Тестирование

PowerShell имеет встроенную систему тестирования — Pester. Пример теста:

# Backup-Database.Tests.ps1
Describe "Backup-Database" {
It "Создаёт ZIP-файл в указанной папке" {
$backupPath = "TestDrive:\backup.zip"
Backup-Database -Source "TestDrive:\Данные" -Destination $backupPath
$backupPath | Should -Exist
(Get-Item $backupPath).Length | Should -BeGreaterThan 0
}
}

Pester позволяет писать unit-тесты, интеграционные тесты, проверять исключения, параметры, выходные объекты. Это гарантирует, что изменения не ломают существующую функциональность.


4. Логирование

Всегда записывайте ключевые этапы выполнения:

$LogPath = "C:\Logs\Backup_$(Get-Date -Format 'yyyyMMdd').log"
Start-Transcript -Path $LogPath -Append
# ... основной код ...
Stop-Transcript

Или используйте Write-Information, Write-Verbose, Write-Debug с параметром -InformationAction Continue. Это позволяет включать/выключать уровни детализации без изменения кода.


PowerShell в Windows Terminal и VS Code

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


Windows Terminal

Это центральный терминал для Windows 10/11. Он поддерживает вкладки, профили, настраиваемые шрифты (включая Nerd Fonts), темы, прозрачность, анимации курсора. PowerShell автоматически становится одним из профилей:

  • Откройте Windows Terminal.
  • Нажмите стрелку вниз → "Настройки".
  • В settings.json можно добавить кастомную команду, переменные окружения, начальную директорию.

Пример профиля для PowerShell 7:

{
"guid": "{574e775e-4f2a-5b96-ac1e-a2962a402336}",
"name": "PowerShell 7",
"commandline": "pwsh.exe",
"hidden": false,
"colorScheme": "One Half Dark"
}

Visual Studio Code + расширение PowerShell

VS Code — рекомендуемая Microsoft среда для PowerShell-разработки. После установки расширения PowerShell появляются:

  • Интеллектуальное завершение кода (IntelliSense) с подсказками по параметрам.
  • Отладчик с точками останова, просмотром переменных, стеком вызовов.
  • Встроенный терминал с поддержкой нескольких сессий.
  • Анализ кода (PSScriptAnalyzer) — выявляет стилевые и потенциальные ошибки.
  • Поддержка Jupyter-ноутбуков (.ipynb) с ячейками PowerShell.

Преимущества VS Code:

  • Кроссплатформенность — один и тот же интерфейс на Windows, Linux, macOS.
  • Интеграция с Git — просмотр изменений, коммиты, ветвление прямо в редакторе.
  • Поддержка Remote-SSH — написание скриптов для Linux-серверов с той же средой.

Официальная документация Microsoft

СсылкаСодержание
Документация PowerShellХаб Learn: начало работы, установка, примеры
Что такое PowerShell?Оболочка, язык и платформа управления
Установка PowerShellpwsh на всех ОС
Примеры для администрированияГотовые сценарии под типовые задачи
PowerShell GalleryПоиск и установка модулей (Find-Module, Install-Module)

Углублённый раздел языка в энциклопедии: 5.26 PowerShell.


Смежные материалы

Раздел "Терминал"оглавление, знаки препинания и конвейеры, чек-лист.

WindowsBAT-сценарии (legacy и простые задачи), справочник команд.

Linuxскрипты Bash, удалённый SSH.

Инфраструктураосновы DevOps и CI/CD, системное администрирование.

Безопасностьопасные скрипты; политика выполнения не заменяет проверку содержимого файла.


Основа по протоколу

Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.

Содержание