← Все заметки
Windows

Автоматическая установка и обновление ПО через GPO: PowerShell, MSI и SHA256 на примере 7-Zip

Практический кейс развёртывания 7-Zip 26.04 через Group Policy Preferences и Scheduled Tasks: SYSVOL, manifest.json, проверка SHA256, mutex, диагностика GPO и репликации DC.

Статус: актуальноОпубликовано: 2026-10-14Обновлено: 2026-10-17

Проверено на: 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. При каждом запуске он должен:

  1. Получить общий mutex, чтобы одновременные задачи не устанавливали пакет параллельно.
  2. Прочитать манифест из доверенного UNC-пути и определить архитектуру ОС (x64/x86).
  3. Определить установленную версию 7-Zip, например по Uninstall-записям реестра, не используя Win32_Product для инвентаризации.
  4. Пропустить установку, если текущая версия уже соответствует целевой или новее.
  5. Проверить размер/существование MSI и SHA256 до запуска, затем вызвать msiexec без пользовательского интерфейса.
  6. Записать результат в 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 не заменяет пилотной установки и проверки безопасности.

Ссылки