Есть вещь, которую я до сих пор считаю лучшим, что случалось с развёртыванием Windows: ты подходишь к машине, выбираешь в начале всё, что тебе нужно, — и больше её не касаешься. Идёшь пить чай. Через несколько минут возвращаешься к готовому компьютеру с именем, в домене, с драйверами и софтом.
Это MDT. И его больше нет.

Дальше — история о том, как я в него влюбился, как он у меня разваливался, как я упёрся в стену, и что из этого получилось.


Как всё начиналось

Я устроился на первую работу техником — совсем зелёным. В контору регулярно завозили новые моноблоки и компьютеры, и каждый требовал ручной установки Windows: флешка, мастер установки, драйверы, имя, ввод в домен, софт. В месяц приходило от 5 до 30 машин — предсказать поток или подготовиться к нему заранее было нельзя. Нас было трое техников, но даже этого объема сил не хватало.

Узким местом был сам установочный носитель. На части машин для загрузки с нашей флешки приходилось заходить в UEFI, отключать Secure Boot, а после установки — не забывать включить его обратно. Были и совсем бытовые мелочи: флешку надо найти, принести, не оставить в очередном кабинете и не забыть забрать после установки. Каждая такая забота кажется ерундой, пока не повторяется десятки раз.

Второе узкое место оказалось неожиданнее. Чтобы довести установку до конца, технику нужно было имя для машины — а выдавал его администратор. Сам шаг занимал секунды. Но он был синхронным: надо было найти админа, отвлечь его от своих дел, дождаться ответа.

То есть на установку одного компьютера фактически требовалось двое людей. Пусть второй и участвовал мгновение — без него процесс не двигался. Запомните этот момент. К нему я ещё вернусь. Довольно быстро стало понятно, что так жить нельзя.

Первое решение: WDS

Поднять WDS и раздавать установку по сети оказалось делом одного вечера. WDS убрал физический носитель из процесса. Машина загружалась по UEFI PXE с включённым Secure Boot, получала установочный образ и ставила Windows. Техник больше не таскал одну и ту же флешку между рабочими местами. Отлично. И тут выяснилось, что лёгкая часть кончилась.

Потому что «поставить Windows» — это 30% работы. Остальное: дать машине правильное имя из корпоративной последовательности, ввести её в домен, накатить драйверы под конкретную модель, поставить обязательный софт и понимать, что происходит с установкой прямо сейчас.

Здесь нужна честная поправка спустя годы: WDS умел больше, чем я тогда понимал.
Он мог генерировать имена по заданной политике, назначать им OU и параметры ввода в домен. Неизвестную машину можно было оставить в ожидании, а затем одобрить после PXE‑запроса, уже тогда задав ей имя и место в AD но это всё‑равно требовало будто доступа с двух сторон, а хотелось как оператор станка — где администратор лишь в исключительных случаях, а не на каждый Апрув. Тогда я этого не знал. Но это не отменяет следующего шага: мне нужен был не набор отдельных возможностей сервера, а один операторский процесс, похожий на тот, который позже дал MDT.

У конкретной машины оператор должен был в одном месте выбрать физический диск, образ, драйверпак, набор программ, имя и сам факт ввода в домен, а затем видеть весь путь установки до конца. В WDS эти решения были распределены между политиками сервера, answer‑файлами, Active Directory, предварительной подготовкой объектов и действиями администратора.
Поэтому WDS решил для меня транспорт и часть автоматизации, но не заменил целостный сценарий развёртывания под оператора.

Пятая попытка

WDS у меня заработал, и начальство выдало следующую задачу. Вместе с ней — MDT, с пояснением, что до меня за него уже брались другие техники. И у них не получилось.
Сервер, который выделили под меня, назывался: предприятие‑wds-05.
Пятый. Я был пятой попыткой заставить нашу схему нормально вводить машины в домен.
По‑хорошему, это должно было насторожить. Но признаться во словно заиграл азарт и вызов, хоть и некое чувство отвественности провала взложилась невольно на мои плечи.

Когда мне открыли доступ к наработкам предшественников, ощущение было буквально как в той сцене из «Аватара», где Аанг в видении оборачивается и видит уходящую вдаль вереницу прошлых Аватаров.

MDT: то, ради чего всё затевалось

Так я пришёл к Microsoft Deployment Toolkit.
И вот тут случилась любовь. Флоу MDT устроен ровно так, как я описал в начале: оператор задаёт параметры на старте, дальше машина всё делает сама. Никакого «подойди через десять минут и нажми Далее». Выбрал, а вернулся к готовому.
двумя словами:

Для автоматической генерации имён я взял известный скрипт с deploymentresearch — префикс плюс порядковый номер. Какое‑то время всё работало. А потом начало сыпаться.

Трещина первая: имена машин

Сам по себе скрипт работал. Повторная установка той же машины проблемой не была — имя тянулось из таблицы. Беда была в другом: счётчик жил отдельно от реальности.

Работы было много, а настроить пересылку PXE‑запросов до WDS во всех нужных сетевых сегментах времени не хватало. Поэтому часть машин ставилась мимо WDS и MDT, потому что надо было прямо сейчас. Такие машины появлялись в сети и в домене, но счётчик о них не знал.

С другой стороны, были компьютеры, которые ставились через WDS и MDT но в домен вообще не вводились. А счётчик всё равно считал, что имя израсходовано, и уходил на +1. В обе стороны расхождение. И каждый раз я лез править счётчик руками.

Оглядываясь, проблема была не в скрипте. Проблема была в том, что источником правды считался счётчик, а не домен. Счётчик знает только то, что прошло через MDT. Домен знает всё. Фрустрация копилась.

Трещина вторая: а на какой диск ставим?

Вот это, пожалуй, довело меня сильнее всего.
В штатном мастере MDT не было нативного выбора физического диска оператором. Номер диска можно было заранее задать через правила или последовательность задач, но решение принимал администратор до запуска, а не оператор, который стоит перед конкретной машиной. Даже UDI в связке с Configuration Manager штатно предлагал форматирование Disk 0, а не список физических накопителей для выбора.

Практический результат в моей тогдашней реализации: я держал два разных варианта развёртывания под разные случаи. Не потому что машины принципиально разные, а потому что диск в них нумеровался по‑разному. Я пробовал прикрутить кастомные скрипты с форумов. Не завелось.

Не верите, что это распространённая боль? Вбейте в поиск mdt choose disk. Там сотни людей, которые ищут ровно это. Вот, например, обсуждение на serverfault — посмотрите, как выглядит ответ. И вот здесь я сформулировал для себя мысль, которая позже стала архитектурной:

MDT просит администратора предугадать конфигурацию железа заранее. Я хотел спросить оператора, который стоит перед машиной.

Это не разница в интерфейсе. Это разница в том, кто принимает решение и в какой момент.

Трещина третья: интерфейс и мониторинг

Тут будет субъективно.
GUI MDT был мне отчасти враждебен. Я допускаю, что дело в неопытности — но факт остаётся: я тратил время на борьбу с интерфейсом вместо работы.

Отдельно раздражал мониторинг стадий установки. Мне хотелось видеть, что происходит с машиной прямо сейчас, и вроде как этапы показывались но это требовало доступа к оснастке MDT, т.е нельзя дать это операторам в удобном виде и хоть недавно я прочитал, что доступ то выдать можно на отдельные сегменты но даже реализация выдачи доступов в разные разделы мне показалась вреждебной. А если посмотреть устаревшие Деплои то... чаще пустота и главное, что я тогда не мог понять: где мои ошибки, а где баги MDT. Конструкция становилась нестабильнее по мере жизни, а я, будучи джуном, не мог отличить одно от другого. Это худшее положение, в котором можно оказаться: ты не знаешь, чинить себя или инструмент.

Стена

В какой‑то момент я решил пересобрать сервер с нуля. По‑хорошему, с чистого листа, с учётом всех набитых шишек и опыта. И обнаружил, что MDT закончился.

Его нельзя официально скачать. Нет, серьёзно — попробуйте. Остаются неофициальные сайты и надежда, что в архиве нет ничего лишнего.
Это, кстати, тот самый момент, ради которого я пишу эту статью. Если вы сегодня поддерживаете инфраструктуру развёртывания на MDT — вы в подвешенном состоянии. Оно работает, пока работает. А как только понадобится переставить сервер, вы окажетесь там же, где оказался я.
И не только я. Вот тред в r/MDT: человек обнаружил ровно это, а в комментариях подтягиваются остальные с тем же открытием. Почитайте, там всё узнаваемо.

Что было вокруг

Раз MDT кончился, надо было смотреть по сторонам:

  • SCCM. Платный, требует инфраструктуры. Для парка, который у меня был, это из пушки по воробьям, да и денег никто не даст.

  • Intune / Autopilot. Подписка и облако. Те же деньги, плюс зависимость, которая мне не подходила.

  • FOG. Бесплатный, живой, но у него принципиально другая модель — capture. И вот здесь стоит остановиться, потому что это водораздел.

Capture против deploy from stock

Модель capture (FOG, Clonezilla, в прошлом Ghost): собираешь эталонную машину, снимаешь с неё образ, раскатываешь этот образ дальше. Быстро при развёртывании.

Но: образ стареет. Его надо пересобирать при обновлении Windows, при новом поколении железа, при смене набора софта. Sysprep капризничает. Золотой образ превращается в отдельный объект, который сам требует обслуживания.

Модель deploy from stock (MDT, SCCM OSD): берётся чистый установочный образ Windows, а всё остальное — драйверы, софт, имя, домен — накладывается в момент развёртывания.

Медленнее на одну машину. Зато компоненты обслуживаются отдельно: установочный образ, драйверы и софт. Ради обновления одного из них не нужно пересобирать целую эталонную машину. Мне ближе вторая. Именно её я любил в MDT, и именно из‑за неё FOG мне не подошёл — не потому что он плохой, а потому что он про другое.

Решение

Итого: среди решений, которые я рассмотрел, я не нашёл живого бесплатного инструмента под тот локальный deploy‑from‑stock сценарий, который мне был нужен.
Я решил написать свой.

Мои амбиции в этот момент
Мои амбиции в этот момент

Но прежде чем перейти к технике

Есть ещё одна причина, по которой этот пост вообще существует, и будет нечестно её умолчать. Мне очень хочется выговориться. Проект я делал один (предчувствую взгляд чата гпт). Обсудить, почему решение принято так, а не иначе, было буквально не с кем — а когда варишься в своей голове месяцами, перестаёшь понимать, где у тебя разумный компромисс, а где просто привычка.

Поэтому я публикую это не только как «смотрите, что получилось». Я прошу посмотреть на это со стороны и сказать, где криво. Мне сейчас полезнее услышать «вот здесь у тебя странное решение, и вот почему», чем «молодец». Идеи, замечания по архитектуре, «а почему не вот так» — всё это я читаю с большим интересом, чем плюсы в карму.

Ну и, если совсем начистоту, это ещё и способ утолить социальный голод. Разработка в одиночку — очень тихое занятие. Проект называется IronDeploy. Дальше — как он устроен.

Архитектура

Четыре компонента, у каждого своя зона ответственности:

Компонент

За что отвечает

SetupWeb

одноразовая локальная страница первичной настройки

IronAPI

control plane: авторизация, план развёртывания, состояние, интеграция с AD

WinPE

интерфейс оператора, стирание диска, применение образа

Post‑install

доустановка софта в уже поставленной системе

WDS/PXE или загрузочный носитель только доставляют WinPE. Тяжёлые payload’ы — образы, драйверы, установщики — едут по SMB. Во время развёртывания IronAPI не проксирует эти файлы: он выдаёт план и реквизиты доступа, а WinPE забирает их напрямую с SMB.

Разделение намеренное. Гонять двухгигабайтный пак драйверов через Python‑приложение — плохая идея, когда рядом есть SMB, который делает это лучше.

WinPE и интерфейс

Интерфейс оператора — WPF‑приложение, запускаемое внутри WinPE в STA‑режиме.
Сразу оговорка: WPF в WinPE официально не поддерживается Microsoft. Это не уязвимость само по себе, но совместимость не гарантируется. На используемых мной сборках ADK интерфейс работает, и для альфы я этот риск принял.

Из ограничений среды вытекает всё остальное: в WinPE нет.NET в привычном виде, нет большинства служб, а сама среда работает с системного RAM‑диска X:. Это накладывает жёсткие рамки на то, что можно себе позволить, и по‑своему дисциплинирует.

Отдельно я сделал так, что если WPF не стартовал — скрипт честно падает с ошибкой в консоль WinPE и не запускает никакого альтернативного сценария развёртывания. Мне не нужен инструмент, который в непонятной ситуации начинает что‑то делать с диском.

Там же живёт защита от запуска не в том месте:

if (
    $env:SystemDrive -ine "X:" -or
    !(Test-Path -LiteralPath "HKLM:\SYSTEM\CurrentControlSet\Control\MiniNT")
) {
    throw "IronDeploy deploy.ps1 may only run inside Windows PE."
}

Два условия: системный диск должен быть X: и должен присутствовать ключ реестра MiniNT. Обычный запуск этого скрипта на рабочей Windows будет остановлен до разрушительной фазы. Это дополнительный предохранитель, а не замена нормальной проверке выбранного диска.

Выбор диска

То, ради чего половина истории. Оператор видит список физических дисков с номером, моделью и размером. Выбирает, подтверждает.

Но выбрать мало. Между моментом выбора и моментом стирания есть зазор, и в этом зазоре может разъехаться что угодно — перечисление, нумерация, подключённая флешка. Поэтому перед разрушительной фазой WinPE заново опрашивает выбранный диск и останавливается, если его номер, модель или размер не совпали с тем, что подтвердил оператор. Логика простая: если экран показал одно, а в системе оказалось другое — значит, оператор согласовывал вслепую, и никакого согласия на самом деле не было. Снимок выбора (номер, модель, размер) при этом уходит в IronAPI и сохраняется вместе с развёртыванием. Потом видно, на что именно ставили.

Образ

Применение — обычный DISM:

dism.exe /Apply-Image /ImageFile:<путь> /Index:<индекс> /ApplyDir:C:\

Никакой магии. Разбивка диска под UEFI/GPT через diskpart, применение образа, запись unattend.xml.

Поддерживаются WIM и ESD. ESD можно развернуть напрямую, но для регулярной работы лучше сконвертировать в WIM — это делается на сервере через интерфейс IronAPI. Перед применением WinPE сверяет размер файла с тем, что заявил API. Дёшево и отсекает очевидно оборванную передачу образа.

Драйверы

Драйверы инжектятся в оффлайн‑образ до первой загрузки. Оператор выбирает один пакет под определённое железо или отказывается от драйверов вовсе.

Пакеты лежат на SMB, а не в образе. Это и есть главное преимущество модели deploy from stock: чтобы добавить поддержку новой модели, не нужно ничего пересобирать — положил папку с драйверами, и всё.

Загрузка драйверов через веб‑интерфейс обвешана ограничениями: количество файлов, глубина вложенности, длина полного пути, минимум свободного места. Пак драйверов от вендора умеет быть очень странным, и лучше отказать на загрузке, чем упасть посреди развёртывания.

Offline Domain Join

Самая недооценённая часть всей темы. Про неё мало кто знает, а вещь красивая.
Задача: ввести машину в домен, когда она ещё даже не загрузилась.
Решение существует в самой Windows — djoin.exe. На сервере, от имени учётной записи с делегированными правами, выполняется:

djoin.exe /provision /domain <домен> /machine <имя> /machineou <OU> /savefile <файл>

Windows создаёт или переиспользует в AD учётную запись компьютера и формирует блоб — файл, содержащий всё необходимое для вступления в домен. Дальше этот блоб применяется к оффлайн‑образу через DISM, и машина загружается уже членом домена. Без ручного ввода, без перезагрузки ради этого.

Важная деталь, которая определяет всё остальное: в блобе лежит секрет машинной учётной записи. Это не метаданные, это credential.
Отсюда обращение с ним:

  • блоб живёт в защищённом каталоге на сервере, не на SMB;

  • у него короткое время жизни — по умолчанию пять минут между созданием и выдачей;

  • если развёртывание не забрало блоб за это время, он не переиспользуется: IronAPI либо провижинит заново, либо вычищает сироту;

  • после подтверждения применения блоб удаляется;

  • при зафиксированной ошибке или истечении срока развёртывания он тоже удаляется и не остаётся лежать бессрочно.

Причина короткого TTL не в паранойе: djoin /provision сбрасывает пароль машинной учётки при каждом вызове. Старый блоб не просто небезопасен — он ещё и может быть уже невалиден.

И отдельно: IronDeploy не хранит отдельные доменные учётные данные в своей конфигурации или базе. Ни LDAP‑поиск, ни djoin.exe не используют отдельный пароль — они работают от имени той учётной записи Windows, под которой запущена служба IronAPI. Пароль самой службы хранит и обслуживает Windows Service Control Manager. Меньше секретов в приложении — меньше поводов для беспокойства.

Имена машин

Та самая боль, с которой всё началось. И решение здесь ровно одно: выкинуть счётчик.
IronAPI не хранит номер последней выданной машины. Вместо этого при каждом запросе он спрашивает у Active Directory: какие объекты компьютеров с этим префиксом сейчас существуют и какой у них максимальный номер? Дальше прибавляет единицу.

PC00001, PC00002, PC00003 — но не потому, что кто‑то считал, а потому что PC00002 в домене уже лежит.
Из этого само собой следует то, чего мне не хватало:

  • машину поставили руками мимо всякого развёртывания и ввели в домен — она видна в AD, и её номер учтён;

  • машину развернули без ввода в домен — в AD её нет, номер не израсходован, ничего не сдвинулось;

  • отдельному счётчику больше не с чем рассинхронизироваться, потому что его нет. Домен и есть источник.

Править руками больше нечего — нет того, что можно было бы испортить.

Оговорка: предложенное имя пока не резервируется атомарно. Если две машины одновременно запросят следующее имя до создания объекта в AD, они могут получить одинаковый ответ. В моём сценарии развёртывания почти всегда запускаются последовательно, поэтому гонка редкая, но для альфы это известное ограничение.

Отдельно оператору показываются имена, которые эта конкретная железка носила раньше: по серийному номеру и MAC из истории развёртываний. Это не влияет на предложение имени, просто полезно знать, что машина уже была PC00042, когда переставляешь её же.

Цена решения — без настроенного LDAP предложения имён не работает. По‑моему, честный размен: последовательность имён и существует только в контексте домена.
А теперь обещанное возвращение к началу.

Помните, что на установку одной машины требовалось двое людей — техник и администратор, который выдаёт имя?
Администратора в этой схеме больше нет. Оператор видит предложенное имя прямо на экране WinPE, вместе с историей этой железки, и подтверждает его сам. Никого не надо искать, отвлекать и ждать.

Для меня это, пожалуй, важнее девяти минут. Минуты — это про скорость. А тут исчезла целая зависимость от другого человека.

Безопасность

Инструмент, который стирает диски, ходит в AD и раздаёт учётные данные для SMB, обязан относиться к себе серьёзно.

Один вход — одно развёртывание. Оператор авторизуется в WinPE и получает bearer‑токен. Токен привязывается к конкретному развёртыванию атомарной операцией, и повторно использовать его нельзя. Есть жёсткий таймаут от начала до конца, не сдвигаемый.

Учётная запись для SMB должна иметь только чтение. Это отдельная identity, не совпадающая с учётной записью службы. Права на чтение необходимо задать и в SMB, и в NTFS. Она нужна WinPE, чтобы забрать образ и драйверы, и больше ни для чего.

Служба не работает от LocalSystem. Раньше такой вариант был как fallback с предупреждением. Я его выпилил полностью: теперь принимается только выделенная учётная запись — доменная или локальная, если AD не используется. Встроенные системные identity, домен BUILTIN и встроенная учётная запись Administrator с RID 500 отклоняются по SID. Проверка не анализирует членство обычной учётки в привилегированных группах, поэтому требование использовать действительно отдельную служебную учётку остаётся на администраторе.

Транспорт. В рабочей сети IronAPI должен использовать HTTPS с включённой проверкой сертификата. HTTP и отключённая проверка сертификата предназначены только для изолированного тестового стенда: через API передаются bearer‑токен и реквизиты read‑only SMB‑учётки.

Установщики проверяются по SHA-256. Хеш считается на сервере, сверяется после копирования на машину и ещё раз перед запуском. Несовпадение — отказ выполнять.

Active Directory опциональна. Если домена нет, LDAP‑поля и настройки ODJ оставляются пустыми, и обе функции отключаются. Развёртывание в рабочей группе работает штатно, без выдумывания фиктивных значений.


Результат

HP 27-cx, M.2 NVMe, гигабитная сеть. Windows 11 25H2, пакет драйверов на 2 ГБ и пять программ: PDF24, Битрикс24, 7-Zip, Zoom и Google Chrome.

Девять минут от подтверждения параметров в WinPE до момента, когда Windows, драйверы и выбранный софт уже установлены, а система переходит к применению групповых политик перед первым входом пользователя. Время применения самих политик в замер не входит.

Но дело даже не в минутах. Дело в том, что вернулось то самое ощущение, ради которого всё затевалось:

ты в начале выбрал, что хотел — и дальше не касаешься машины.

Она сама разметит диск, поставит систему, вкатит драйверы, возьмёт имя, войдёт в домен и доустановит софт. А ты в это время занимаешься чем‑нибудь другим.

Что не работает

Это альфа, и я не буду делать вид, что это не так.

  • Развёртывание рассчитано на x64 UEFI/GPT. BIOS/MBR нет.

  • Выбранный диск стирается полностью. Существующие разделы пока не показываются перед подтверждением — только номер, модель и размер.

  • Захвата эталонной машины нет и не планируется. Это осознанный отказ, а не пробел.

  • Управлением машинами после развёртывания проект не занимается. Обновления, политики, инвентаризация — это соседняя категория продуктов.

  • Проверено на моём парке. Ваше железо, прошивки, сеть и образы отличаются.

И самое главное: IronDeploy безвозвратно стирает выбранный диск. Тестируйте на виртуалке или на машине, которую не жалко. Не на своём рабочем компьютере.

Зачем я это выкладываю

Проект под Apache 2.0, лежит на GitHub: https://github.com/Syrinoxsis/IronDeploy

Мне нужно не «звёздочку поставьте». Мне нужны отчёты о железе. (хотя звёздочку хочу увидеть как молодой разраб)

Самое ценное, что можно прислать, — это рассказ о том, что сломалось на вашей модели: производитель, модель, SKU, на какой стадии упало, кусок лога, был ли включён ввод в домен, какой пакет драйверов выбирали. Такие вещи невозможно найти, сидя на одном парке техники.

Сообщения о том, что модель развернулась чисто, тоже полезны — из них складывается список проверенного железа.

На этапе альфы я не готов принимать изменения в разрушительном deployment‑пути без воспроизводимого стенда и сценария проверки. Код, который стирает диски или обрабатывает секреты машинных учёток, недостаточно проверить чтением — его надо прогнать на стенде с ADK и доменом. Зато документация, тест‑кейсы и точные отчёты об ошибках приветствуются: здесь хорошее описание проблемы часто ценнее диффа.

И если вы сейчас сидите на MDT и думаете, что делать дальше — напишите, чем закончилось. Мне правда интересно, кто как выкручивается.

Честно говоря в моменте осознал, что толком ничего не рассказал про интерфейс самого IronApi и то как это вообще управляется, а я смотрю на статью и её чудовищную огромность испытываю лишь:

Поэтому держите просто нарезков фрагов...ой т.е интерфейса:

Удачи всем!