PowerShell 5.0 preview: DSC-классы как попытка сделать Windows-автоматизацию читаемой
WMF 5.0 preview добавил DSC-классы и PackageManagement. Переписали несколько конфигураций с MOF на классы - разница в читаемости заметная, тестировать тоже проще.
PowerShell 5.0 Preview (WMF 5.0) вышел с DSC-классами и модулем PackageManagement
Microsoft выпустил WMF 5.0 preview - Windows Management Framework с PowerShell 5.0 внутри. Из заметного: DSC-классы на синтаксисе PowerShell вместо MOF, модуль PackageManagement для установки пакетов из разных источников и улучшения в дебаггере. Мы занялись этим сразу, потому что DSC у нас в работе уже несколько месяцев и вопрос читаемости конфигураций стоит остро.
Что было не так с MOF
DSC в PowerShell 4.0 строился на MOF - Managed Object Format. Для тех кто не работал с WMI вплотную, MOF - это отдельный язык с отдельным синтаксисом, который надо изучать поверх PowerShell. Описываешь ресурс в одном синтаксисе, логику в другом, конфигурацию в третьем. Получается многослойный пирог, который сложно читать и ещё сложнее отлаживать.
Конкретный пример - ресурс для управления службой Windows. В схеме MOF это выглядит как класс с типизированными свойствами, потом отдельно PowerShell-скрипты для Get/Set/Test, потом всё это надо правильно упаковать в модуль с манифестом. Минимальный кастомный ресурс - пять-шесть файлов. При этом если что-то пошло не так, сообщение об ошибке нередко указывает не туда.
На сопровождении у нас несколько клиентов на Windows Server, и конфигурации DSC там появились ещё в прошлом году. Когда конфигурации начали разрастаться - стало понятно что поддерживать это сложно. Не потому что DSC плохой, а потому что MOF добавляет ненужный слой сложности между намерением и реализацией.
Классы меняют ситуацию
В PowerShell 5.0 можно описать DSC-ресурс прямо на PowerShell с атрибутом [DscResource()]:
[DscResource()]
class WindowsServiceConfig {
[DscProperty(Key)]
[string] $ServiceName
[DscProperty(Mandatory)]
[string] $StartupType
[DscProperty()]
[bool] $Ensure = $true
[void] Set() {
Set-Service -Name $this.ServiceName -StartupType $this.StartupType
}
[bool] Test() {
$svc = Get-Service -Name $this.ServiceName -ErrorAction SilentlyContinue
if (-not $svc) { return $false }
return $svc.StartType -eq $this.StartupType
}
[WindowsServiceConfig] Get() {
$svc = Get-Service -Name $this.ServiceName
$this.StartupType = $svc.StartType
return $this
}
}
Это один файл. Без MOF-схемы, без отдельных папок под каждый метод. Логика Get/Set/Test живёт рядом, видно что откуда берётся. Инженер смотрит на класс и понимает что происходит - не надо держать в голове где лежит нужный скрипт.
Что переписали и что заметили
Взяли несколько существующих ресурсов и переписали на классы. Не всё подряд - только то где MOF-слой реально мешал. Наблюдения:
Читаемость выросла заметно. Новый человек в команде берёт класс и разбирается без вводной лекции про структуру MOF-ресурса. Раньше это был разговор минут на двадцать минимум.
Тестирование упростилось. Класс можно инстанцировать в Pester-тесте напрямую, вызвать Test() или Get() и проверить результат. С MOF-ресурсом приходилось либо поднимать конфигурацию целиком, либо городить обёртки. Сейчас юнит-тесты под DSC-ресурс пишутся почти как обычные.
Отладка стала предсказуемее. Стектрейс указывает на строку в классе, а не куда-то в глубины WMI-стека. Это кажется мелочью пока не сидишь полчаса с отладчиком над ресурсом который падает с невнятным CIM-исключением.
Ограничения есть. Наследование работает, но с нюансами. Если базовый класс помечен [DscResource()], дочерний должен переопределить все три метода - иначе ошибка при компиляции. Можно сделать базовый класс без атрибута DSC и использовать его как хелпер - это работает нормально. Несколько кастомных ресурсов мы так и разложили: общая логика в базовом классе, конкретные ресурсы наследуют и добавляют специфику.
PackageManagement: пощупали, пока осторожно
Второй заметный кусок WMF 5.0 - модуль PackageManagement (бывший OneGet). Идея простая: единый интерфейс для разных пакетных менеджеров. Поставил провайдер под Chocolatey - управляешь Chocolatey-пакетами через стандартный Install-Package. Поставил NuGet-провайдер - те же командлеты, другой источник.
В теории это решает давнюю боль: на одних машинах Chocolatey, на других корпоративный NuGet-репозиторий, и надо писать разную логику в DSC-конфигурациях. С PackageManagement можно унифицировать.
На практике мы пока используем осторожно. Провайдеры в preview-версии работают неровно, и несколько пакетов установились с ошибками которых не было при прямом вызове Chocolatey. Для production-конфигураций пока держим прямые вызовы, PackageManagement тестируем на стендах.
Где это относительно Puppet и Chef
Вопрос закономерный. DSC-классы приближают Windows-автоматизацию к модели Puppet/Chef - описываешь желаемое состояние на нормальном языке, движок делает сходимость. Читаемость конфигураций сопоставимая, структура ресурсов понятная.
Разница в зрелости экосистемы. Под Puppet тысячи готовых модулей, сообщество, устоявшиеся практики. DSC-ресурсов значительно меньше, и качество у них разное. Но для чисто Windows-инфраструктуры DSC работает нативно, без лишних агентов и зависимостей. WinRM есть везде начиная с Server 2008 R2 - никакого Ruby, никакого Puppet Master если не нужен.
Если у клиента смешанная среда - Linux и Windows - Ansible или Puppet дают единый язык для всего. Если Windows-only с Group Policy в основе - DSC с PowerShell 5.0 выглядит разумным выбором.
Итого
WMF 5.0 - пока preview, финальный релиз Microsoft не объявил. Для production мы ждём GA. Но классовый синтаксис DSC уже достаточно стабилен чтобы писать новые ресурсы на нём и не бояться. Переписывать рабочие MOF-ресурсы ради переписывания смысла нет - только там где поддержка реально болит.
Следим за PackageManagement: если провайдеры доведут до стабильного состояния, это закрывает один из неудобных кусков Windows-автоматизации. Пока - стенды.