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

Стандартные блоки и модули PowerShell

Разработчику Архитектору Инженеру
Загрузка PowerShell…

Зачем стандартные блоки

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

Поэтапный подход из 124: сначала рабочий скрипт для одной задачи, затем выделение повторяемых кусков в функции, затем модуль .psm1 для команды.


Анатомия блока-функции

Каждая функция для автоматизации получает три обязательных элемента:

ЭлементЗачем
[CmdletBinding()]Общие параметры -Verbose, -ErrorAction, -WhatIf
[OutputType(...)]Документирует тип результата для IDE и Get-Help
param() с типами и MandatoryРанний отказ при неверных аргументах

Пример — уникальное имя архива

Функция создаёт путь к новому .zip, проверяет папку и коллизию имён — типовой паттерн архивации логов:

function Set-ArchiveFilePath {
[CmdletBinding()]
[OutputType([string])]
param(
[Parameter(Mandatory)]
[string]$ZipPath,

[Parameter(Mandatory)]
[string]$ZipPrefix,

[Parameter(Mandatory)]
[datetime]$Date
)

if (-not (Test-Path -LiteralPath $ZipPath)) {
New-Item -Path $ZipPath -ItemType Directory -Force | Out-Null
Write-Verbose "Создана папка: $ZipPath"
}

$zipName = '{0}{1:yyyyMMdd}.zip' -f $ZipPrefix, $Date
$zipFile = Join-Path -Path $ZipPath -ChildPath $zipName

if (Test-Path -LiteralPath $zipFile) {
throw "Архив уже существует: $zipFile"
}

return $zipFile
}

Пример — файлы старше N дней

Параметр «возраст» задают положительным числом; внутри функции преобразуют в отрицательный для AddDays:

function Get-FilesOlderThan {
[CmdletBinding()]
[OutputType([System.IO.FileInfo[]])]
param(
[Parameter(Mandatory)]
[string]$Path,

[Parameter(Mandatory)]
[ValidateRange(1, [int]::MaxValue)]
[int]$Days
)

$cutoff = (Get-Date).AddDays(-$Days)
Get-ChildItem -LiteralPath $Path -File |
Where-Object { $_.LastWriteTime -lt $cutoff }
}

KISS
Пользователю проще передать «30 дней», чем «−30» в AddDays. Валидация ValidateRange(1, …) отсекает отрицательные значения до логики архивации.


Сборка скрипта из блоков

Скрипт верхнего уровня — оркестратор: параметры, вызов функций, журнал, код выхода.

param(
[Parameter(Mandatory)]
[string]$LogFolder,

[Parameter(Mandatory)]
[string]$ArchiveFolder,

[string]$ArchivePrefix = 'logs_',

[int]$OlderThanDays = 30
)

function Write-Log([string]$Message) {
$line = '{0:u} {1}' -f (Get-Date), $Message
Add-Content -Path "$PSScriptRoot\archive.log" -Value $line
Write-Host $line
}

try {
Write-Log "Старт: $LogFolder"
$files = Get-FilesOlderThan -Path $LogFolder -Days $OlderThanDays
if (-not $files) {
Write-Log 'Нет файлов для архивации.'
exit 0
}

$lastDate = ($files | Sort-Object LastWriteTime -Descending | Select-Object -First 1).LastWriteTime
$zipPath = Set-ArchiveFilePath -ZipPath $ArchiveFolder -ZipPrefix $ArchivePrefix -Date $lastDate

Compress-Archive -Path $files.FullName -DestinationPath $zipPath
$files | Remove-Item -Force
Write-Log "Архив: $zipPath"
exit 0
}
catch {
Write-Log "ОШИБКА: $($_.Exception.Message)"
exit 1
}

Проблема «функции внутри .ps1»: при dot-sourcing нескольких скриптов имена функций могут конфликтовать. Решение — вынести блоки в модуль.


Модуль .psm1

Модуль — каталог с файлом ИмяМодуля.psm1 (и опционально ИмяМодуля.psd1 — манифест).

Структура каталога:

ArchiveTools/
├── ArchiveTools.psd1 # манифест (версия, экспорт)
└── ArchiveTools.psm1 # функции

ArchiveTools.psm1:

function Set-ArchiveFilePath { ... } # код из примера выше

function Get-FilesOlderThan { ... }

Export-ModuleMember -Function Set-ArchiveFilePath, Get-FilesOlderThan

Минимальный ArchiveTools.psd1:

@{
ModuleVersion = '1.0.0'
RootModule = 'ArchiveTools.psm1'
FunctionsToExport = @('Set-ArchiveFilePath', 'Get-FilesOlderThan')
Description = 'Блоки архивации логов для внутренних скриптов'
}

Установка для разработки (путь добавьте в $env:PSModulePath или скопируйте в Documents\PowerShell\Modules):

Import-Module .\ArchiveTools\ArchiveTools.psd1 -Force
Get-Command -Module ArchiveTools
ДействиеКоманда
Список экспортированных функцийGet-Command -Module ArchiveTools
Справка по функции модуляGet-Help Set-ArchiveFilePath
Обновление после правкиImport-Module ... -Force

Советы по модулям для автоматизации

ПравилоПояснение
Один блок — одна задача«Имя архива» и «список файлов» — разные функции
Comment-based help в каждой функцииGet-Help без чтения исходника — см. 111
Install-Module вне рабочего скриптаУстановку модулей делают при подготовке образа ПК или в CI
Версии модулей фиксируютДва скрипта могут требовать разные версии одного модуля
Тестируйте блок отдельноВ консоли: Get-FilesOlderThan -Path C:\Logs -Days 7 -Verbose

Когда выносить в модуль

СитуацияРешение
Функция нужна в одном .ps1Оставить в том же файле или dot-source .\Blocks.ps1
Тот же блок в 2+ скриптахМодуль или общий .ps1 с dot-sourcing
Блок отдают другой командеМодуль + Git + 111 чек-лист
Нужна публикация в GalleryМанифест .psd1, semver, Publish-Module

Чек-лист блока

#Проверка
1Есть [CmdletBinding()] и [OutputType]
2Параметры типизированы; обязательные — Mandatory
3Ошибки — throw или -ErrorAction Stop, а не тихий $null
4Есть Write-Verbose для отладки в планировщике
5Функция возвращает объект/значение, а не только пишет в консоль

Дальше

ТемаМатериал
Запуск по расписанию и наблюдатели126
Планировщик Windows, пример .ps12.05/112
Параметры, SupportsShouldProcess118
Модули Gallery, Az, Graph121

См. также

Другие статьи этого же раздела в боковом меню (как на странице "О разделе").