Windows PowerShell 1.0: ставим на серверы и переписываем скрипты
PowerShell 1.0 вышел официально - и мы сразу пошли в продакшн. Три главных скрипта переписаны с VBScript: AD-экспорт, дисковый отчёт, создание пользователей.
Windows PowerShell 1.0 выходит 14 ноября 2006 как официальный компонент Windows
В апреле, когда вышел RC, мы уже писали про первые впечатления - тестировали на стенде, щупали синтаксис, жаловались на отсутствие нативных AD-командлетов. Теперь другая история: 14 ноября выходит финальный релиз, и мы к нему готовы. Скрипты написаны заранее, процедура развёртывания обкатана на трёх тестовых серверах. Как только появится RTM-сборка - идёт на все серверы управления.
VBScript в архив. Официально.
Почему именно сейчас, а не «подождём»
Три месяца после RC - достаточно, чтобы понять: скрипты, написанные на PowerShell, работают стабильно и читаются людьми, которые их не писали. Это уже само по себе аргумент. Второй аргумент - мы полгода занимаемся аудитом AD-прав, и каждый раз ощущается, как VBScript-инструменты упираются в потолок. Отчёт на двести строк кода, который делает одно дело и делает его хрупко.
Финальный релиз означает: можно регламентировать. Документировать. Требовать от коллег писать туда же, а не в отдельный vbs-файл с именем final_FINAL_2.vbs.
Три скрипта, которые переписали
Выбрали три задачи, которые запускаются по расписанию и реально болели.
Экспорт пользователей из AD. Старая версия - ADSI через VBScript, примерно сто двадцать строк с объявлениями переменных, обработкой Nothing и ручным формированием CSV через конкатенацию строк. Новая версия на PowerShell - двадцать строк, и это с комментариями:
$searcher = New-Object DirectoryServices.DirectorySearcher
$searcher.Filter = "(&(objectCategory=user)(enabled=TRUE))"
$searcher.PropertiesToLoad.AddRange(@("sAMAccountName","mail","department","manager"))
$searcher.FindAll() | ForEach-Object {
$p = $_.Properties
New-Object PSObject -Property @{
Login = $p["samaccountname"][0]
Email = $p["mail"][0]
Department = $p["department"][0]
Manager = $p["manager"][0]
}
} | Export-Csv -Path "C:\Reports\ad-users.csv" -NoTypeInformation
Export-Csv встроен. Не надо писать хедер вручную. Не надо думать про кавычки внутри значений. Просто работает.
Отчёт по дискам. Раньше - WMI-запрос на VBScript, который парсил Win32_LogicalDisk и собирал HTML-таблицу конкатенацией строк. Жуткое зрелище. Теперь:
$servers = Get-Content "C:\Config\servers.txt"
$servers | ForEach-Object {
Get-WmiObject -ComputerName $_ -Class Win32_LogicalDisk -Filter "DriveType=3" |
Select-Object @{n="Server";e={$_.__SERVER}}, DeviceID,
@{n="SizeGB";e={[math]::Round($_.Size/1GB,1)}},
@{n="FreeGB";e={[math]::Round($_.FreeSpace/1GB,1)}},
@{n="Free%";e={[math]::Round($_.FreeSpace/$_.Size*100,0)}}
} | Export-Csv "C:\Reports\disk-report.csv" -NoTypeInformation
Скрипт обходит список серверов из текстового файла, собирает данные по всем дискам и кладёт в CSV. Добавить сервер - одна строчка в servers.txt. Старая версия требовала редактировать массив прямо в коде.
Создание пользователей. Это был самый болезненный скрипт. Старый вариант - почти двести строк VBScript, который работал по принципу «не трогай, а то сломается». Создавал пользователя, добавлял в группы, устанавливал пароль, включал аккаунт - каждое действие через отдельный COM-вызов. Новый скрипт принимает CSV с параметрами нового сотрудника:
Import-Csv "C:\Input\new-users.csv" | ForEach-Object {
$ou = "OU=$($_.Department),OU=Users,DC=company,DC=local"
$upn = "$($_.Login)@company.local"
$dn = "CN=$($_.FullName),$ou"
$user = [ADSI]"LDAP://$ou"
$newUser = $user.Create("user", "CN=$($_.FullName)")
$newUser.Put("sAMAccountName", $_.Login)
$newUser.Put("userPrincipalName", $upn)
$newUser.Put("department", $_.Department)
$newUser.SetInfo()
$newUser.SetPassword($_.TempPassword)
$newUser.InvokeSet("AccountDisabled", $false)
$newUser.SetInfo()
Write-Host "Создан: $($_.Login)"
}
Нативных командлетов New-ADUser в 1.0 не завезли - это всё ещё ADSI под капотом. Но синтаксис читается, и логика видна. Старый VBScript-вариант требовал знать наизусть названия COM-методов, которые нигде нормально не документированы.
DirectorySearcher в продакшне
Get-ADUser - это будущий командлет из отдельного модуля, которого в 1.0 нет. Пока работаем через DirectorySearcher, что тоже считается. Главное - объектный пайп работает, отчёты генерируются, скрипты читаются людьми, которые не писали их в три часа ночи.
Один момент, который приятно удивил: Where-Object на объектах AD-записей работает так же, как на любых других объектах PowerShell. Не надо знать LDAP-фильтры для простой фильтрации на клиентской стороне - написал Where-Object { $_.Department -eq "IT" } и получил то, что нужно. Мелочь, но когда привыкаешь к этому, возврат к VBScript ощущается физически.
Развёртывание
PowerShell 1.0 не входит в Windows Server 2003 из коробки - отдельный установочный пакет. Добавили в процедуру сопровождения серверов: при вводе нового сервера в эксплуатацию - установка PowerShell обязательна. На существующих серверах управления раскатываем через GPO Software Installation. Execution Policy ставим RemoteSigned - скрипты из сетевых папок с подписью, локальные без.
Библиотека скриптов живёт в сетевой папке с версионированием через ручной changelog. Не идеально, но работает.
Месяц назад это был бы вопрос «стоит ли переходить». Сейчас - уже нет. Три скрипта переписаны, работают, понятны. VBScript-версии лежат в архивной папке на случай «вдруг что», но достать их оттуда уже не хочется. Следующая задача - автоматический отчёт по правам в AD на PowerShell. Там объём побольше, но подход уже понятен.