Migrating Windows Deployments from Curtin to Eikon¶
This page covers the Windows-specific notes for moving from Curtin to Eikon — image considerations, first-boot credentials, and verification. Do the migration itself (switch the application profile → deploy → roll back) from the shared Migrating from Curtin to Eikon guide. Your existing Windows image and its parameters carry over unchanged — nothing below is required to migrate; it explains the Windows specifics and one credential gotcha to avoid.
Image compatibility¶
You do not need to rebuild your Windows golden images to move to Eikon — the same generalized (sysprepped) image deploys under either engine, and your existing Curtin images stay valid as-is (see the note at the end of this section). Both engines stage drpcli and the first-boot scripts by writing into the image offline at deploy, so neither needs those baked in. What differs is a small set of image prerequisites that a profile swap can't change.
Base-image prerequisites: what changes¶
| What the golden image needs | Curtin | Eikon |
|---|---|---|
| Generalized / sysprepped | required | required |
| Hardware drivers for the target baked in | required | required |
| cloudbase-init installed + configured | required — deploy aborts if it's missing | required only on the cloud-init path; droppable on the unattend-only path |
C:\curtin directory pre-baked |
required | not required — Eikon creates it at deploy |
| What drives Windows first-boot | cloudbase-init (reads a NoCloud seed) | unattend.xml always; cloudbase-init only on the cloud-init path |
| Image package format | dd-gz / dd-tgz / dd-xz / dd-raw (no zstd) |
superset — adds dd-zst (recommended), tar-zst, qcow |
Two things that are not migration differences: both engines inject
drpcli, the runner scripts, and (on the cloud-init path) the NoCloud seed into the image at deploy time, so neither requires them pre-baked. And neither engine uses WinRM to reconnect a deployed machine — that's the drpcli agent + join-up. (If you seeWaiting for WinRMin a log, that's a Packer image-build step, not a deploy step.)
Net effect: Eikon relaxes the image prerequisites. When you migrate you can optionally slim the golden image:
- Drop the
C:\curtinpre-bake — Eikon creates it at deploy on the cloud-init path and ignores it on the unattend path. - Drop cloudbase-init only if you adopt the unattend-only path
(
image-deploy/use-cloud-init=false). Keep it if you want cloud-init to apply hostname / SSH keys /cloud-init/user-dataat first boot.
Your existing images still work unchanged. Slimming is optional. A current Curtin golden image — with
C:\curtinand cloudbase-init already baked in — deploys under Eikon as-is: the bakedC:\curtinis harmless (Eikon reuses it), and a present cloudbase-init is simply used on the cloud-init path and ignored on the unattend path. Migrate first, slim later (or never).
Configure Windows first-boot (Administrator password + agent)¶
On both paths, eikon's eikon-win-prep injects C:\Windows\Panther\unattend.xml
(plus C:\Windows\Setup\Scripts\SetupComplete.cmd, which runs an agent batch that
fetches and runs join-up) and C:\Windows\drpcli.exe. So the Administrator
password is set the same way regardless of path — via the unattend:
drpcli machines set <machine-uuid> param eikon/win-unattend-username to Administrator
drpcli machines set <machine-uuid> param eikon/win-unattend-password to '<password>' # Secure param
Password complexity matters. The unattend sets it via a
LocalAccountin theoobeSystempass, which enforces the Windows policy (≥6 chars, 3 of {upper, lower, digit, symbol}, not the username). A non-compliant password is silently dropped → locked out (only trace:C:\Windows\Panther\setuperr.log).
What image-deploy/use-cloud-init (default true) actually controls is
whether cloudbase-init is also configured at deploy — not the password:
true(cloud-init path) — eikon additionally writes a cloudbase-init NoCloud seed (meta-data/user-data+drpcli.ps1); cloudbase-init then applies hostname, SSH keys, and anycloud-init/user-dataat first boot. Requires cloudbase-init installed in the image. Where the seed lands is set byimage-deploy/cloud-init-base-url:file:///curtin/(default) → baked intoC:\curtin(offline-capable; the dir name is inherited from curtin, created at deploy);http(s)://…(e.g.{{.Machine.Url}}/) → cloudbase-init fetches the seed from DRP at boot (NoCloudNet); nothing baked in.
false(unattend-only path) — no cloudbase-init; just the unattend.xml + SetupComplete agent.
Do NOT seed
image-deploy/admin-username/admin-passwordon the cloud-init path — it locks you out of Administrator. Mechanism (root-caused from the cloudbase-init log, not a guess): the seed value is fine, but seedingadmin-username: Administratormakes cloudbase-init'sCreateUserPluginseize the built-in Administrator and give it a throwaway password (CreateUser only ensures the account; applying the metadata password isSetUserPasswordPlugin's job — and that plugin does not run in the MAIN stage on a typical image). Soadmin-passwordis never applied and Administrator is left unusable. Withadmin-*unset, cloudbase-init falls back to its own configured account and never touches Administrator, so the unattend-set password stands. Set the Administrator password only viaeikon/win-unattend-password; let cloudbase-init own hostname/SSH-keys/user-data, not the password. (Verified on Server 2022 + 2025: seedingadmin-*→ locked out on both;win-unattend-passwordalone → clean login on both the cloud-init and unattend paths.)
Curtin → Eikon reference (Windows)¶
| Concern | Curtin (image-deploy) |
Eikon (eikon) |
|---|---|---|
| Engine selected by | universal-application-image-deploy |
universal-application-eikon-image-deploy |
| Workflow | universal-image-deploy |
universal-image-deploy (same) |
| Image type / URL | image-deploy/image-type + image-deploy/image-url |
carry over — Eikon derives eikon/image-* from them (set eikon/image-type only to adopt dd-zst etc. post-migration) |
| Golden-image dir | requires C:\curtin baked in |
optional; created at deploy if cloud-init path used |
| Windows first-boot | cloudbase-init (NoCloud, file:///curtin/) |
cloud-init (default) or unattend.xml (use-cloud-init) |
| Admin credentials | image-deploy/admin-username / -password |
eikon/win-unattend-username / -password on both paths (do not seed image-deploy/admin-password — see Troubleshooting) |
Troubleshooting (Windows)¶
See also the common troubleshooting in the shared guide.
- Windows boots but you can't log in (WinRM/RDP/console all reject the password) — two common causes:
- The password failed Windows complexity in the unattend
oobeSystempass and was dropped — pick a compliant password (see first-boot). Trace:C:\Windows\Panther\setuperr.log. - You seeded
image-deploy/admin-passwordon the cloud-init path — cloudbase-init took over the password and clobbered the unattend value. Removeimage-deploy/admin-username/image-deploy/admin-passwordand redeploy; set the password only viaeikon/win-unattend-password.
Verification¶
- After deploy, the machine reaches
BootEnv: localand reboots into Windows. - Windows completes specialize and sets the Administrator password.
- The machine reconnects to DRP via join-up (appears Runnable, agent online), and WinRM (5985) / RDP accept the configured Administrator credentials.
Lab-verified on DRP (universal image-deploy pipeline) deploying zstd raw Windows Server 2022 and 2025 images via Eikon on both the unattend.xml and cloud-init first-boot paths.