Docs
← Back to the builder

Sysprep workflows A, B, C, and D

Sysprep is fine until you sysprep an image that's already been sysprepped. Then it fails silently and you spend an afternoon reading setupact.log. This page covers all four workflows — A (fresh install), B (capture-ready golden), C (audit mode), D (image refresh) — and the rearm-count gotcha that bites teams whose documentation stopped at workflow B.

Workflow overview

Sysprep is Windows System Preparation Tool. Its job is to strip machine-specific state (SID, computer name, hardware IDs, activation state) from a Windows installation so the image can be deployed to other machines or resealed for OOBE. The unattend.xml you give to sysprep tells Windows how to reconfigure itself on the next boot.

The four workflows differ in what passes get populated, what sysprep switches get used, and what the output is used for. Choosing the wrong one produces an image that either fails to deploy, double-rearmed itself to death, or leaks state from the previous build.

Are you installing Windows fresh on one machine?
  YES --> Workflow A (autounattend.xml on install media)

Are you building a golden image for the first time?
  YES, will deploy to identical hardware --> Workflow B + keep_drivers ON
  YES, will deploy to varied hardware    --> Workflow B + keep_drivers OFF

Need to add/patch an existing captured golden image?
  YES --> Workflow D (sysprep over sysprep)

Need to build an image through Audit mode with manual
configuration before sealing?
  YES --> Workflow C (auto-boot into audit, you seal at the end)
Sysprep workflow comparison
AxisA — Fresh installB — Golden imageC — Audit modeD — Image refresh
Use caseInstall Windows on one machine, unattended.Build a reference image and capture it for mass deploy.Build image, customize manually, then seal.Sysprep over sysprep — refresh an existing golden.
Bootable mediaYes (autounattend.xml on USB/ISO root)Yes (boot once, then capture)No (you boot once, then live in audit)No (boot existing image in a VM)
Passes populatedwindowsPE + specialize + oobeSystemwindowsPE + specialize + generalize + oobeSystemwindowsPE + specialize + auditUser (optional) + oobeSystem (Reseal=Audit)auditUser + generalize + oobeSystem
DISM capture targetNoYes (the whole point)Optional (audit gives you the box to customize before capture)Yes (after the refresh re-seal)
Manual sysprep stepNoRequired before captureRequired at end (you run sysprep from audit)Required (the whole workflow is one sysprep run)
Rearm count0 (no generalize)1 per generalize (use SkipRearm=1)1 per generalize at the end (use SkipRearm=1)1 per refresh — the rearm trap lives here
DriversInject via DriverPaths in windowsPEDriverPaths + PersistAllDeviceInstallsDriverPaths at install, plus pnputil at audit timePersistAllDeviceInstalls + pnputil /delete-driver
Output destination\autounattend.xml (media root)C:\Windows\Panther\unattend.xml\autounattend.xml (media root)C:\Windows\Panther\unattend.xml

Workflow A — Fresh install (boot-from-media)

The 80% case. You have a Windows ISO or USB key. You want Windows to install itself unattended on a clean machine — language, locale, disk partitioning, user account, OOBE skipped — without a human touching a keyboard.

The file is named autounattend.xml and lives at the root of the install media. Windows Setup finds it automatically during the windowsPE phase, before any disk formatting happens.

Pass sequence

[Boot from media]
       |
       v
  windowsPE pass       <-- disk layout, image selection, driver injection
       |
       v
  specialize pass      <-- computer name, domain join, network config
       |
       v
  oobeSystem pass      <-- user account creation, OOBE page skips, first-run commands
       |
       v
  [Windows desktop]

File placement

USB root or ISO root:
  \autounattend.xml           <-- Setup finds this automatically
  \sources\install.wim        <-- standard Windows install image

What the builder generates

Selecting Workflow A populates all three passes. The windowsPE pass handles disk configuration (wipe + GPT layout or install-to-existing-partition). The specialize pass sets computer name and optionally joins a domain or workgroup. The oobeSystem pass creates the local admin account, applies OOBE bypass settings (Section 6), any Win 11 hardware bypasses (Section 7), and runs FirstLogonCommands.

No sysprep involved. Workflow A is a clean install — sysprep never runs. The unattend.xml drives Windows Setup directly. The sysprep passes (generalize, auditUser) are irrelevant here.

Workflow B — Capture-ready golden image (first build)

You are building a reference image that will be captured with dism /Capture-Image or imagex /capture and deployed to many machines. This workflow prepares the image for capture by running sysprep with /generalize /oobe /shutdown before the capture step.

Pass sequence

[Install Windows fresh -- Workflow A or manual install]
       |
       v
[Boot into Windows, install software, configure settings]
       |
       v
[Run: sysprep /generalize /oobe /shutdown /unattend:unattend.xml]
       |
       v
  generalize pass      <-- strips SID, hardware IDs, activation state
       |
       v
  [Machine powered off -- ready to capture]
       |
       v
[dism /Capture-Image ...]
       |
       v
[Deploy to target machines -- triggers oobeSystem pass on first boot]

Key fields: SkipRearm and PersistAllDeviceInstalls

The generalize pass in Workflow B carries two settings that matter:

SettingDefault in builderWhat it doesWhen to change it
SkipRearm=1ONDoes not consume one of the three activation rearm counts during generalizeLeave ON unless you specifically need rearm to fire
PersistAllDeviceInstalls=trueONKeeps all currently installed drivers in the captured imageTurn OFF when deploying to hardware with different driver requirements

These are the same two settings that become critical in Workflow D. For a first-build Workflow B, leaving both ON is correct for most environments. The rearm trap (described below) is primarily a Workflow D problem, but understanding it here makes Workflow D easier to reason about.

What the builder generates

Workflow B adds a generalize pass to the Workflow A pass set. The generated file goes to C:\Windows\Panther\unattend.xml before you run sysprep — or you pass its path explicitly with /unattend:.

sysprep /generalize /oobe /shutdown /unattend:"C:\Windows\Panther\unattend.xml"

Workflow C — Audit-mode build

You want to install Windows, log in as Administrator in audit mode, manually install software and configure the box, then sysprep to seal it for capture or deployment. Workflow C is the unattend for that — Setup boots into audit mode automatically; the rest is human time. Pre-staging auditUser commands is optional. When you're done customizing, you run sysprep from the audit-mode desktop to seal the image.

When to use Workflow C

Each workflow exists because the others can't do this one thing. Here's where C sits:

  • Use C over A when you need to install GUI software, register licenses, or click through vendor installers that can't be scripted silently. A lands you on the end-user desktop with no built-in Administrator; C lands you in audit as Administrator with no user profile baggage to clean up later.
  • Use C over B when "build the image" requires manual steps. B assumes you can fully script the build (FirstLogonCommands, SetupComplete.cmd, etc.) and just sysprep at the end. C is the answer when scripting everything isn't realistic — proprietary installers, GPO snap-in tools, weird OEM utilities.
  • Use C, not D, for the first build. D is for refreshing an image that has already been captured at least once. If you don't have a golden yet, C is how you make one with human-in-the-loop customization.
  • Don't use C if everything you do is scriptable. B is faster, reproducible, and doesn't depend on a human remembering to click "Next" the same way next time.

Anatomy of the emitted unattend.xml (Workflow C)

The builder writes the file to \autounattend.xml at the root of your install media. The pass set:

  • windowsPE — disk layout, image selection, locale, driver injection. Same shape as Workflow A.
  • specialize — computer name and time zone. Domain join is deliberately deferred to after sealing; you don't want the build VM joined.
  • auditUser (optional) — pre-staged commands that run the first time Administrator logs into audit. Useful for things you always do at the start of a build: copy in a tools share, pre-install your AV, mount a script volume. Leave it empty if you'd rather start from a clean desktop.
  • oobeSystem — carries the Reseal=Audit directive in the <Reseal> component. That's what tells Setup "skip OOBE, drop the user into audit mode" instead of running the user-account wizard.

Manual sealing step

When you've finished customizing in audit mode, you seal the image yourself. From an elevated command prompt on the audit-mode desktop:

sysprep /generalize /oobe /shutdown

Pick the right reseal switch for what comes next:

  • /oobe — next boot runs OOBE. Use this when you're done customizing and want the captured image to land end users on the welcome screen. This is the normal "I'm finished, capture me" case.
  • /audit — next boot goes straight back to audit mode. Use this when you want to capture mid-build (e.g., snapshot a "base + apps" checkpoint) and keep customizing. The image you capture here is not end-user-ready.
  • /quit — sysprep exits without rebooting. Useful for chained automation where another script runs immediately after. Rare in the C workflow.
Generalize consumes one rearm. Each sysprep /generalize burns one of the three activation rearms on retail and MAK images. If you reseal twice during a single C build (e.g., audit checkpoint then final seal), that's two rearms gone. See The 3-rearm-count trap for the full breakdown — and set SkipRearm=1 in the generalize pass if your channel supports it.

auto_reseal_after_audit

Section 11 of the builder exposes an auto_reseal_after_audit toggle. When ON, the emitted unattend chains sysprep /generalize /oobe /shutdown into the audit pass so the box seals itself the moment your pre-staged commands finish. When OFF (the default and the recommendation), Setup parks at the audit desktop and waits for you to seal manually.

Leave it OFF in most cases. The whole point of Workflow C is the human-in-the-loop window between install and seal — auto-resealing collapses that window to zero and turns C into a slower Workflow B. Only turn it ON if your auditUser commands do every single customization and you want a fully hands-off build.

Section 4 (User Accounts) is ignored for Workflow C. Audit logs in as the built-in Administrator account — no local user gets created during the build, and the OOBE pass doesn't run a user wizard. Anything you configured in the Accounts section will not appear in the emitted file. Create end-user accounts at deploy time via the post-sysprep OOBE, or pre-stage them with a FirstLogonCommand in a Workflow B re-seal of the captured C image.

Related docs

  • Adding drivers — C often pairs with offline driver injection during the windowsPE pass, plus pnputil at audit time for vendor packages that need a running OS.
  • First-run snippets — audit_commands take the same shape as RunSynchronousCommand entries. The patterns there drop in directly.
  • Command reference — PowerShell invocation rules (always wrap with powershell -ExecutionPolicy Bypass -File, never bare .ps1) apply the same way in audit as in OOBE.

Workflow D — Image refresh (sysprep over sysprep)

You have a golden image in production. A new CVE drops, or a vendor pushes a mandatory update, or you need to change the default browser. You do not want to rebuild the image from scratch — that takes hours or days and risks introducing regressions. You want to boot the existing image, make the change, and re-seal it.

This is "sysprep over sysprep": running sysprep on a machine that has already been generalized and deployed. It works, but it has four failure modes that will silently corrupt your image if you are not aware of them.

Pass sequence

[Deploy previous golden image to a build VM]
       |
       v
[Boot into Windows -- OOBE runs, creates local admin account]
       |
       v
[Log in, make changes: patch Firefox, apply GPO, update registry]
       |
       v
[Run: sysprep /generalize /oobe /shutdown /unattend:"...\unattend.xml"]
  ^
  This is the "sysprep over sysprep" step.
  The Workflow D unattend.xml controls what happens here.
       |
       v
  auditUser pass       <-- triggers cleanup: clear SetupComplete, clean event logs
  generalize pass      <-- SkipRearm=1 critical here (see below), strip SID
       |
       v
  [Machine powered off -- ready to capture]
       |
       v
[dism /Capture-Image -- new version of the golden image]
       |
       v
[Deploy updated image -- oobeSystem runs on target machines]

Why this is the hard case

The documentation for sysprep nearly always covers Workflow B (first build) and stops. Workflow D involves the same tools and the same commands, but four things break silently if you don't know to look for them:

  1. The activation rearm counter decrements — three refreshes and activation breaks on retail/MAK-keyed images (see qualification below).
  2. Drivers installed in the previous build persist and can conflict.
  3. SetupComplete.cmd from the previous build runs again on deployed targets.
  4. Office activation state carries forward in ways that require a reset.

Each of these is covered in its own section below. The builder surfaces all four as explicit toggles with inline warnings. The defaults are set to the safe values.

Workflow D fields in the builder

FieldDefaultPurpose
SkipRearm=1ONPrevents rearm counter decrement. Leave ON.
Keep drivers (PersistAllDeviceInstalls=true)ONPreserves installed drivers. Turn OFF for cross-hardware deploys.
Reseal actionOOBE + shutdownWhat sysprep does after generalize: shut down ready for capture, or restart to run OOBE.
Clear SetupComplete.cmdONRemoves leftover setup script before re-seal to prevent double execution.
Clear Windows.oldONFrees disk space before capture.
Clean event logsONWipes the previous install's audit trail before redistribution.
Reset Office activationOFFOnly turn ON if Office was activated in the previous build. See below.

The 3-rearm-count trap

Windows activation has a rearm counter. Each time sysprep's generalize pass runs without SkipRearm=1, it decrements that counter. The counter starts at 3. When it hits 0, the machine will not activate — not with KMS, not with a MAK key, not with a volume license.

Teams who do Workflow B once (first build) never hit this. Their counter goes from 3 to 2. Teams who do Workflow D quarterly hit 0 within the first year and spend two weeks puzzling over why new deployments from their golden image fail activation.

"3 strikes" is the worst case — it depends on edition and activation channel. The counter starts at 3 by default, but rearm behaviour varies:
  • Retail and MAK keys: every sysprep /generalize without SkipRearm=1 consumes one rearm. Hits 0 fastest. This is the case the "three strikes" warning is written for.
  • KMS-activated machines: /generalize does NOT consume a rearm. The 180-day KMS activation timer resets on re-activation against the KMS host, but the rearm counter itself is not decremented.
  • Active Directory-Based Activation (ADBA): volume activation via ADBA also does not decrement the rearm counter on generalize. Safe by design for domain-joined volume-licensed images.
SkipRearm=1 is still the right default for Workflow D regardless of channel — it is a no-op on KMS/ADBA and prevents the trap on retail/MAK.

The fix: SkipRearm=1

Set SkipRearm=1 in the <generalize> pass of your unattend.xml. This tells sysprep to skip the rearm step — the SID and hardware IDs are still generalized, but the activation timer is not reset and the counter is not decremented.

<settings pass="generalize">
  <component name="Microsoft-Windows-Security-SPP-UX"
             processorArchitecture="amd64"
             publicKeyToken="31bf3856ad364e35"
             language="neutral"
             versionScope="nonSxS">
    <SkipRearm>1</SkipRearm>
  </component>
</settings>

The builder sets this to 1 by default in both Workflow B and Workflow D. If you turn it OFF in the builder, you get an inline red banner reminding you of the consequences.

How to check your current rearm count

slmgr /dlv

Look for the "Remaining Windows rearm count" line. If it shows 0, you need to rebuild from a clean install (not from a Workflow D refresh) to reset the count.

There is no supported way to reset the rearm counter to 3 other than a clean install. If you have an image at 0, any further sysprep generalize without SkipRearm=1 will produce an image that activates on the first deployment but never again.

Driver leakage across cycles

When sysprep runs generalize without PersistAllDeviceInstalls=false, all currently installed drivers are kept in the image. For Workflow B on identical hardware, this is correct — drivers are already right for the target. For Workflow D, it depends on your environment.

When to keep drivers (default)

Leave PersistAllDeviceInstalls=true (the default) if:

  • All your target machines are the same hardware model (or very close).
  • You are deploying to VMs with consistent virtual hardware.
  • You deliberately injected specific drivers into the image and want them in the next version.

When to strip drivers

Set PersistAllDeviceInstalls=false if:

  • You deploy to a mix of hardware (different NIC, GPU, or storage controllers).
  • A driver from a previous build is causing problems on new hardware.
  • You want the cleanest possible generalized image and Windows to re-detect hardware on deployment.

The XML

<settings pass="generalize">
  <component name="Microsoft-Windows-PnpSysprep"
             processorArchitecture="amd64"
             publicKeyToken="31bf3856ad364e35"
             language="neutral"
             versionScope="nonSxS">
    <PersistAllDeviceInstalls>true</PersistAllDeviceInstalls>
  </component>
</settings>

Set the value to false to release the driver-to-device bindings. The builder exposes this as the "Keep drivers" toggle in Workflow B and D sections.

PersistAllDeviceInstalls=false does NOT strip drivers from the image. This is the single most common misconception about Microsoft-Windows-PnpSysprep. Setting it to false releases the bindings between installed devices and their driver packages — so Windows re-runs PnP detection on next boot — but the driver .inf files themselves remain in C:\Windows\System32\DriverStore\FileRepository\. If a driver from the previous build is causing problems on new hardware, =false alone will not remove it; the driver is still in the store and PnP can still pick it on a matching hardware ID. To actually remove a driver, run pnputil /delete-driver oemNN.inf /force before generalize. Use =false for what it is: a clean rebind on next boot, not a driver wipe.

SetupComplete.cmd ghosting

SetupComplete.cmd lives at C:\Windows\Setup\Scripts\SetupComplete.cmd. Windows runs it once after the initial install finishes, in SYSTEM context, before the first user logon. It is a common place to run post-install configuration: installing software, applying registry settings, joining domains.

The problem with Workflow D: if your first golden image (Workflow B) had a SetupComplete.cmd, that file is still present in the image after the initial deployment. When you boot the deployed image to do your Workflow D refresh, that script has already run. But when you re-seal with sysprep and deploy the updated image to new machines, Windows will run it again on each new deployment. That is usually the correct behavior for the commands you put there.

Where it goes wrong: if your Workflow D refresh adds a new SetupComplete.cmd through the unattend, and there is already a file at that path from the previous build, the content can get merged unexpectedly depending on how your scripts write the file. More commonly, you end up with duplicate commands running.

The fix: clear the file before re-sealing

The builder's "Clear SetupComplete.cmd" toggle (default ON in Workflow D) adds a command to the auditUser pass that deletes the existing file before sysprep generalizes. Your Workflow D unattend.xml then writes a fresh one in the oobeSystem pass.

<!-- In the auditUser pass, before generalize -->
<RunSynchronousCommand>
  <Order>1</Order>
  <Description>Clear previous SetupComplete.cmd</Description>
  <Path>cmd /c del /f /q "%SystemRoot%\Setup\Scripts\SetupComplete.cmd"</Path>
</RunSynchronousCommand>
Event logs. Similarly, event logs from the previous build can contain confusing entries when you inspect a deployed machine. The "Clean event logs" toggle (default ON) adds a command that clears all event logs before re-sealing using cmd.exe's for /f loop: for /f "tokens=*" %L in ('wevtutil el') do wevtutil cl "%L".

Office activation across refreshes

If the machine you are refreshing had Microsoft 365 or Office activated during the previous deployment period, that activation state is embedded in the image. When you re-seal and deploy the updated image to a new machine, Office may present a stale or conflicting activation state on first run.

The symptom is typically an activation error or a prompt to sign in with a different account on the first user logon on a freshly deployed machine.

The fix: ospprearm.exe

:: OSPPREARM.EXE is a native binary -- invoke it directly (no cscript).
"C:\Program Files\Microsoft Office\Office16\OSPPREARM.EXE"

:: Equivalent VBS path (this is what the builder emits):
cscript //nologo "C:\Program Files\Microsoft Office\Office16\ospp.vbs" /rearm

This resets the Office activation grace period so the deployed image activates cleanly on first run. Run it in the auditUser pass, before sysprep generalizes.

The builder's "Reset Office activation" toggle (default OFF) adds this command when enabled, with an inline yellow warning: "Required only if Office 365 was activated in the previous build. Resets activation grace; users re-activate on first run."

Adjust the path for your Office version. Office 2016 and 365 use Office16. Office 2013 uses Office15. Office 2021 and 2024 also use Office16. Check the path on the build machine before including this command.

Worked example: CVE patch cycle

The scenario: you deployed a golden image last quarter. A CVE drops affecting Firefox. You need to update Firefox in the image and redeploy. Here is the exact sequence.

Prerequisites

  • A VM hypervisor (Hyper-V, VMware, or Proxmox) with your current golden image available as a WIM or VHD.
  • The previous Workflow B or D unattend.xml — or generate a new Workflow D one from the builder.
  • Firefox updated installer (from your software repository).

Step 1: Boot the image in a VM

# If you have the WIM, apply it to a VHD and boot it:
dism /Apply-Image /ImageFile:"D:\images\golden-current.wim" ^
     /Index:1 /ApplyDir:"V:\"

# Or if the image is a VHD/VHDX, attach and boot directly in Hyper-V.
# The OOBE will run. Complete it to get to the desktop.

Step 2: Install the update

# Run as Administrator in the VM:
# Uninstall old Firefox
"C:\Program Files\Mozilla Firefox\uninstall\helper.exe" /S

# Install new Firefox (silent)
"\\fileserver\software\Firefox-Setup-125.0.1.exe" -ms

# Verify
"C:\Program Files\Mozilla Firefox\firefox.exe" --version

Make any other changes you need: registry edits, Group Policy updates, additional software. This is your only window before re-sealing.

Step 3: Generate a Workflow D unattend.xml from the builder

Open the builder, select Workflow D, and configure:

  • SkipRearm: ON (critical — leave it on)
  • Keep drivers: ON (unless you are changing hardware targets)
  • Reseal action: OOBE + shutdown
  • Clear SetupComplete.cmd: ON
  • Clear Windows.old: ON
  • Clean event logs: ON
  • Reset Office activation: only if Office was activated in the previous build

Download the file. Copy it to C:\Windows\Panther\unattend.xml in the VM.

Step 4: Run sysprep

sysprep /generalize /oobe /shutdown /unattend:"C:\Windows\Panther\unattend.xml"

Expected output in C:\Windows\System32\Sysprep\Panther\setupact.log:

SYSPREP: Sysprep will generalize the machine.
SYSPREP: Calling SysprepGeneralize...
...
SYSPREP: Sysprep operation completed successfully.
SYSPREP: Shutting down.

If you see errors, check C:\Windows\System32\Sysprep\Panther\setuperr.log. The most common failure is a third-party antivirus or DRM component that blocks generalize — check the Microsoft sysprep generalize documentation for the list of known incompatible components.

Step 5: Capture the updated image

# Boot into WinPE (from a separate USB or network boot)
# Then capture:
dism /Capture-Image ^
     /ImageFile:"D:\images\golden-2026-q2.wim" ^
     /CaptureDir:"C:\" ^
     /Name:"Golden Image Q2-2026" ^
     /Description:"Firefox 125 + Q2 patches" ^
     /Compress:fast

Step 6: Verify before wide deployment

# Apply the new image to a test VM:
dism /Apply-Image /ImageFile:"D:\images\golden-2026-q2.wim" ^
     /Index:1 /ApplyDir:"V:\"

# Boot it. OOBE should run. Complete it.
# Verify:
#   - Firefox 125 is installed
#   - Windows activation succeeds (slmgr /ato)
#   - SetupComplete.cmd ran once and only once (check event log Application source: SetupHost)
#   - No duplicate first-run commands

Once the test deployment looks correct, push the new WIM to your deployment share.