Автоматическая установка и обновление ПО через GPO: PowerShell, MSI и SHA256 на примере 7-Zip
Практический кейс развёртывания 7-Zip 26.04 через Group Policy Preferences и Scheduled Tasks: SYSVOL, manifest.json, проверка SHA256, mutex, диагностика GPO и репликации DC.
Проверено на: Active Directory Domain Services · Windows 10/11 · Windows Server · PowerShell 5.1 · Group Policy Preferences · 7-Zip 26.04
Задача и результат
Нужно было централизованно устанавливать и обновлять 7-Zip на доменных рабочих станциях и терминальных серверах, без ручного обхода компьютеров и без зависимости от входа пользователя. Компьютеры включаются в разное время, а MSI уже может выполняться другим процессом. Повторный запуск не должен приводить к бесконечной переустановке.
В октябре 2026 года была внедрена схема GPO → Scheduled Task → PowerShell → manifest.json → проверенный MSI. На тестовых станциях подтверждено обновление 7-Zip 26.03 → 26.04, в том числе после применения GPO и без последующего ручного вмешательства в установку. Отдельные ошибки обнаружены и устранены во время раскатки.
Обезличивание: внутренние домены, реальные названия GPO, компьютеров, OU и сетей не раскрываются. ad.example.com, PC-A, PC-B и показанные имена политик — примеры. Названия задач Corp-7Zip-AutoUpdate и Corp-7Zip-Install-Now — также условные. Фрагменты кода ниже — адаптированные иллюстрации алгоритма, а не буквальная публикация производственного Install-7Zip.ps1.
Почему не обычное назначение MSI в GPO
Классическая Group Policy Software Installation умеет назначать MSI, но для этой задачи требовались управляемая периодичность, собственная проверка версии/хеша, повторные попытки, подробный лог и защита от гонки двух заданий. Поэтому GPO здесь распространяет задачу, а установку делает скрипт под NT AUTHORITY\SYSTEM.
AD / GPO, связанная с OU компьютеров
|
v
Group Policy Preferences
|
Scheduled Task (SYSTEM)
|
v
Install-7Zip.ps1 из NETLOGON
| | |
| | +--> Global mutex
| +-----------> manifest.json / SHA256
+-------------------> проверка версии
|
v
msiexec /i /qn /norestart
|
v
локальный deploy.log / exit code
Это подход для управляемого развёртывания MSI-приложений, а не замена полноценной системы endpoint management. Для крупных пакетов, тысяч клиентов и сложных зависимостей могут лучше подходить выделенный файловый ресурс, пакетный репозиторий или специализированный агент.
1. Репозиторий пакетов
В пилоте использовался общий каталог, доступный доменным компьютерам через NETLOGON:
\\ad.example.com\NETLOGON\Software\7Zip\
├── manifest.json
├── 7z2604-x64.msi
├── 7z2604.msi
└── Scripts\
└── Install-7Zip.ps1
Содержимое для 7-Zip 26.04 включало два пакета: 7z2604-x64.msi (2 009 088 байт) и 7z2604.msi (1 574 400 байт). В настоящем манифесте для каждого указывалась SHA256-сумма. Нельзя вставлять фиктивные хеши: в начале тестов именно такая ошибка вызывала отказ установки.
Условное представление полей манифеста (формат JSON ниже иллюстративный: при применении нужен согласованный парсер в PowerShell):
{
"Version": "26.04",
"Packages": {
"x64": {"File": "7z2604-x64.msi", "SHA256": "<полный SHA256 из доверенного источника>"},
"x86": {"File": "7z2604.msi", "SHA256": "<полный SHA256 из доверенного источника>"}
}
}
Разделите права: компьютерам — только чтение, изменять манифест, MSI и скрипт могут только назначенные администраторы. NETLOGON обслуживается контроллерами домена, его содержимое реплицируется через SYSVOL; размещение крупных дистрибутивов там увеличивает репликационную нагрузку. Для корпоративного масштаба лучше использовать отдельный read-only SMB-репозиторий, сохранив GPO для доставки конфигурации.
2. Логика установочного скрипта
Задача скрипта не сводится к msiexec /i. При каждом запуске он должен:
- Получить общий mutex, чтобы одновременные задачи не устанавливали пакет параллельно.
- Прочитать манифест из доверенного UNC-пути и определить архитектуру ОС (x64/x86).
- Определить установленную версию 7-Zip, например по Uninstall-записям реестра, не используя
Win32_Productдля инвентаризации. - Пропустить установку, если текущая версия уже соответствует целевой или новее.
- Проверить размер/существование MSI и SHA256 до запуска, затем вызвать
msiexecбез пользовательского интерфейса. - Записать результат в
C:\ProgramData\Corp\7Zip\deploy.logи завершиться с осмысленным кодом.
Иллюстрация проверки хеша, с проверкой формата ожидаемого значения:
$expected = [string]$package.SHA256
if ($expected -notmatch '^[0-9a-fA-F]{64}$') {
throw 'Invalid expected SHA256 in manifest'
}
$actual = (Get-FileHash -LiteralPath $msiPath -Algorithm SHA256).Hash
if ($actual -ine $expected) {
throw "SHA256 mismatch: $msiPath"
}
В PowerShell значения версий 26.04 и 26.4 могут отображаться по-разному. Не сравнивайте их как обычные строки; нормализуйте номера и используйте корректный алгоритм сравнения версий.
Защита от параллельного запуска в пилоте строилась на именованном mutex Global\Corp-7Zip-Deploy. Оба задания используют одно имя, освобождают mutex в finally и корректно обрабатывают истечение времени ожидания. Этот mutex защищает собственные задания, но не отменяет конфликт с чужим MSI-процессом (код 1618).
Пример вызова установщика после проверки версии и целостности:
$msiLog = 'C:\ProgramData\Corp\7Zip\msi-install.log'
$args = '/i "{0}" /qn /norestart /L*v "{1}"' -f $msiPath, $msiLog
$result = Start-Process -FilePath 'msiexec.exe' -ArgumentList $args -Wait -PassThru
switch ($result.ExitCode) {
0 { 'Installed successfully' }
3010 { 'Installed, reboot required' }
default { throw "MSI exit code: $($result.ExitCode)" }
}
Безопасность: -ExecutionPolicy Bypass в задаче — не контроль целостности. Доверие обеспечивают ограничение доступа на запись, проверенная поставка MSI и скрипта, контроль SHA256 и при необходимости подпись кода. Хеш из того же каталога защищает от случайной порчи, но не спасёт, если злоумышленник может одновременно заменить MSI и манифест.
3. GPO и Scheduled Task
Политика привязывается к OU компьютерных объектов, а не к OU пользователей. В данном внедрении проблему отсутствующей задачи обнаружили именно из-за неполной привязки GPO; после привязки к OU=Workstations и обновления компьютерной политики задача стала появляться на клиентах.
Путь в редакторе GPO:
Computer Configuration
> Preferences
> Control Panel Settings
> Scheduled Tasks
Параметры периодического задания:
| Параметр | Значение в пилоте |
|---|---|
| Task | Corp-7Zip-AutoUpdate |
| Учетная запись | NT AUTHORITY\SYSTEM, highest privileges |
| Действие | powershell.exe с -NoProfile -NonInteractive -ExecutionPolicy Bypass -File ... |
| Trigger | Каждый день в 03:00 |
| Random delay | До 1 часа (избежать одновременного старта клиентов) |
| Источник | \\ad.example.com\NETLOGON\Software\7Zip\Scripts\Install-7Zip.ps1 |
Строка аргументов действия:
-NoProfile -NonInteractive -ExecutionPolicy Bypass -File "\\ad.example.com\NETLOGON\Software\7Zip\Scripts\Install-7Zip.ps1"
Для ускорения первичной установки использовалось дополнительное задание Corp-7Zip-Install-Now. Оно запускает тот же скрипт и тот же mutex, а не отдельную реализацию логики установки. В новых установках предусмотрите выполнение пропущенного задания при следующей возможности, а при необходимости — при старте компьютера: иначе выключенный ночью ПК будет ожидать следующего окна.
Важно: поскольку задание работает под SYSTEM, при обращении к UNC удалённая сторона видит, как правило, учётную запись компьютера. Проверьте права чтения доменных компьютеров на шаре и NTFS, а не только права интерактивного администратора.
4. Проверка GPO на клиенте
После создания и линковки политики на тестовой рабочей станции (PowerShell с правами администратора):
gpupdate /target:computer /force
gpresult /scope computer /r
Get-ScheduledTask -TaskName 'Corp-7Zip-AutoUpdate' |
Select-Object TaskName, State
Get-ScheduledTaskInfo -TaskName 'Corp-7Zip-AutoUpdate' |
Select-Object LastRunTime, NextRunTime, LastTaskResult
Start-ScheduledTask -TaskName 'Corp-7Zip-AutoUpdate'
Get-Content 'C:\ProgramData\Corp\7Zip\deploy.log' -Tail 40
Start-ScheduledTask запускает задание асинхронно; нельзя сразу принимать старый LastTaskResult за итог новой установки. Дождитесь завершения задачи и проверьте журнал и фактически установленную версию. Успешное создание задачи ещё не доказывает успешной установки.
Число 267011 = 0x41303 в LastTaskResult означает «задание ещё ни разу не запускалось», а не ошибку MSI. Для задач, запускающих PowerShell, код 0 сам по себе также недостаточен: скрипт обязан выставлять ненулевой exit code при необработанной ошибке и вести собственный лог.
5. Реальные проблемы внедрения
| Симптом | Причина / проверка | Что помогло |
|---|---|---|
На PC-A нет Corp-7Zip-AutoUpdate |
Не было корректной GPO-ссылки на OU компьютерного объекта | Добавили link к OU=Workstations; gpupdate, повторная проверка и при необходимости перезагрузка |
Задание Ready, но установка не происходила |
Task создан, но scheduled run ещё не наступил; часть клиентов была недоступна ночью | Ручной запуск для пилотной проверки и задание первой установки |
LastTaskResult = 267011 |
Задание ещё не выполнялось | Проверили LastRunTime, NextRunTime, выполнили тестовый запуск |
Invalid SHA256 |
В манифесте остались неверные контрольные суммы | Пересчитали SHA256 для фактических MSI и исправили manifest |
| Разные версии скрипта на DC | Задержка/несогласованность репликации SYSVOL/NETLOGON | Сверили файл на обоих DC, дождались репликации, повторили проверку |
| Ошибка доступа при проверке Task Scheduler | Команда выполнялась без прав администратора на ПК | Повторили проверку с повышенными правами |
Потенциальная гонка Install-Now и AutoUpdate |
Оба задания могут запускать MSI почти одновременно | Общий именованный mutex и обработка кодов MSI |
При диагностике SYSVOL используйте:
repadmin /replsummary
repadmin /showrepl *
repadmin показывает состояние репликации Active Directory, но не является прямым тестом здоровья DFSR/SYSVOL. Для подтверждения синхронизации файлов дополнительно сравнивайте размеры, версии и SHA256 на каждом DC, проверяйте состояние DFSR и соответствующий журнал событий. В пилоте расхождение было заметно по разному размеру скрипта — примерно 2 181 и 3 693 байта на двух контроллерах.
Пример проверки файлов на двух DC (адреса заменены):
$relative = 'NETLOGON\Software\7Zip\Scripts\Install-7Zip.ps1'
'dc01.ad.example.com', 'dc02.ad.example.com' | ForEach-Object {
$path = '\\' + $_ + '\' + $relative
[pscustomobject]@{
Server = $_
Length = (Get-Item -LiteralPath $path).Length
SHA256 = (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash
}
}
6. Итог и ограничения
После исправления привязки GPO, контрольных сумм и расхождений между DC обновление до 7-Zip 26.04 подтвердилось на тестовых компьютерах. Для отдельных машин потребовались gpupdate, перезагрузка или принудительный первый запуск; позднее установка прошла и на компьютерах без ручного запуска задания. Локальный лог фиксировал MSI exit code: 0 и повторные проходы вида Already current: 26.4.
Результат не следует трактовать как доказательство стопроцентного обновления всего домена: для этого нужна централизованная инвентаризация конечных версий и ошибок по всем целевым хостам.
Перед промышленным применением нужны пилотная OU, проверка x64/x86, контроль прав на UNC, отказоустойчивость источника пакетов, журналирование ошибок, сценарий rollback и отчёт по версии ПО. Процедура отката должна учитывать, что изменение манифеста не гарантирует автоматический downgrade MSI.
Основной вывод: GPO обеспечивает доставку и периодическое выполнение, но надёжность развёртывания определяется идемпотентным скриптом, контролем пакетов, наблюдаемостью результата и исправной репликацией каталога/файлов.
Исходники PowerShell, GitHub и защита RDS
Исходный код и вспомогательные материалы опубликованы в отдельном репозитории 7zip-gpo-deployment. В нём находятся основной PowerShell-установщик, диагностика, исторические версии и CHANGELOG.
Отдельный сценарий для терминальных серверов (RDS): Install-7Zip-RDS.ps1 запрашивает пользовательские сессии через Windows Terminal Services API и откладывает запуск основного установщика, если присутствует хотя бы одна сессия, включая отключённую. Он не завершает сеансы и не инициирует перезагрузку. Код возврата 0 при таком откладывании не означает, что обновление было выполнено: состояние нужно проверить позднее.
Граница применимости: опубликованные скрипты обезличены, содержат демонстрационные пути и не являются побайтовыми копиями производственных версий. Они не проходили отдельное исполнение на Windows после адаптации. Статическая проверка в GitHub Actions не заменяет пилотной установки и проверки безопасности.