ADG Оставить заявку
Блог Автоматизация 4 мин чтения

PowerShell 5.1 в Windows Server 2016: переписываем bat-скрипты администрирования

PowerShell 5.1 входит в состав Windows Server 2016 с поддержкой классов. Рассказываем, как переносим на него накопившиеся bat-скрипты и что даёт новая архитектура кода.

Контекст момента

PowerShell 5.1 стал финальной версией в составе Windows Server 2016 с поддержкой классов, улучшенным DSC и механизмом Just Enough Administration

Microsoft объявили, что PowerShell 5.1 - финальная версия в составе Windows Server 2016. Не превью, не RC, а то что войдёт в релиз. Для нас это стало поводом разобрать пыльный шкаф с bat-скриптами и наконец переписать несколько наиболее замороченных.

Откуда взялся шкаф

За несколько лет сопровождения Windows-инфраструктуры у нас накопилось с десяток bat-скриптов, которые делают всякие рутинные вещи: чистка логов, ротация бэкапов, проверка состояния служб, отчёт по дискам. Часть из них появилась в период, когда PowerShell был опциональным и на целевых серверах его могло не оказаться. Часть написана людьми, которые просто знали cmd лучше чем PowerShell.

Проблема не в том, что bat плохой. Проблема в том, что когда логика усложнилась - условия, циклы, обработка ошибок - скрипты начали напоминать слоёный пирог из goto, if errorlevel и for /f "tokens=...". Читать их без кофе тяжело. Менять страшно.

Один конкретный пример: скрипт ротации бэкапов. Логика такая - проходим по папкам, смотрим на дату создания, удаляем старые кроме N последних, пишем в лог что удалили. В bat это 60 строк с несколькими уровнями вложенности через метки и переходы. Когда потребовалось добавить исключения для определённых папок, никто не хотел в это лезть.

Что даёт PowerShell 5.1 конкретно

Главное что нас интересует в 5.1 - это классы. Не как абстрактный концепт ООП, а как практический инструмент структуризации кода.

До 5.0 в PowerShell можно было писать функции, создавать объекты через New-Object или PSCustomObject, но описать собственный тип с методами и состоянием было неудобно. Приходилось городить конструкции или тащить C#-код через Add-Type. Теперь это выглядит нормально:

class BackupRotator {
    [string] $BackupRoot
    [int]    $KeepCount
    [string[]] $ExcludeFolders

    BackupRotator([string]$root, [int]$keep) {
        $this.BackupRoot      = $root
        $this.KeepCount       = $keep
        $this.ExcludeFolders  = @()
    }

    [void] Rotate() {
        $dirs = Get-ChildItem $this.BackupRoot -Directory |
                Where-Object { $this.ExcludeFolders -notcontains $_.Name } |
                Sort-Object CreationTime -Descending

        $toDelete = $dirs | Select-Object -Skip $this.KeepCount
        foreach ($d in $toDelete) {
            Remove-Item $d.FullName -Recurse -Force
            Write-Host "Удалено: $($d.FullName)"
        }
    }
}

Теперь добавление исключений - это одна строка вызова: $rotator.ExcludeFolders = @('archive', 'important'). Никаких новых условных ветвей в теле скрипта.

Как мы переносим

Процесс не быстрый, делаем итерационно - не ставим цель переписать всё за выходные. Логика такая:

  • Первый шаг - инвентаризация. Собрали список скриптов, отметили те где больше 30 строк или есть вложенные условия. Таких оказалось восемь.
  • Второй шаг - порт без рефакторинга. Берём bat, переводим в эквивалентный PowerShell без изменения структуры. Запускаем рядом, сверяем результат. Это занимает час-два на скрипт.
  • Третий шаг - рефакторинг с классами. Уже в PowerShell смотрим где есть повторяющийся блок кода или несколько связанных переменных - выносим в класс.

Не всё имеет смысл переводить в классы. Линейный скрипт из 15 строк который просто перезапускает службу - оставляем функцией. Классы полезны там где есть состояние (несколько переменных которые ходят вместе) и несколько операций над ним.

DSC тоже стал лучше

Параллельно с классами в 5.1 улучшили Desired State Configuration. Нас больше всего порадовало что ресурсы теперь можно писать с помощью тех же классов - [DscResource()] атрибут навешивается прямо на класс. Раньше DSC-ресурс требовал отдельного модуля с тремя функциями Get/Set/Test в конкретной структуре папок, и это было муторно. Классовая форма компактнее.

Для инфраструктур под управляемым сопровождением это важно: DSC позволяет описать желаемое состояние сервера декларативно и проверять drift. Не «запусти скрипт», а «сервер должен быть в таком состоянии». Разница в подходе ощутимая.

Что раздражает прямо сейчас

PowerShell 5.1 вместе с Windows Server 2016 - это хорошо, но серверов с 2008 R2 и 2012 R2 вокруг ещё немало. На 2008 R2 максимум PowerShell 4.0 через WMF 4.0 - WMF 5.x на эту ОС не устанавливается. Писать код с классами, который должен работать на разнородном парке - значит держать в голове матрицу совместимости или делать два варианта. Пока мы решаем это просто: новые скрипты пишем на 5.1, старые трогаем только если есть конкретная задача.

Ещё один момент - документация по классам в PowerShell скудноватая. Есть официальная документация Microsoft и несколько статей, но глубоких примеров меньше чем хотелось бы. Приходится экспериментировать.

Где мы сейчас

Из восьми намеченных скриптов переписали три. Результат нравится: код читается, добавление новых параметров не вызывает желания написать автору оригинала письмо с вопросами. Остальные пять - в очереди, приоритет по частоте изменений.

GA Windows Server 2016 по всем признакам близко - RC уже был, финальные версии компонентов фиксируются. Так что вкладываться в PowerShell 5.1 сейчас - не работа впустую.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.