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

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. Там объём побольше, но подход уже понятен.

Контакт

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

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