Skip to content

fix(windows): ship Cloudbase-Init in templates and apply cipassword o…#6

Open
gabbelitoV2 wants to merge 1 commit into
ConvoyPanel:mainfrom
gabbelitoV2:fix/windows-cloudbase-init
Open

fix(windows): ship Cloudbase-Init in templates and apply cipassword o…#6
gabbelitoV2 wants to merge 1 commit into
ConvoyPanel:mainfrom
gabbelitoV2:fix/windows-cloudbase-init

Conversation

@gabbelitoV2

Copy link
Copy Markdown
Contributor

…n clones

Windows templates built "successfully" without Cloudbase-Init and without sysprep. Root cause: Packer's default powershell execute_command ends with exit $LastExitCode - when a provisioner starts right after a windows-restart and the uploaded script path is not readable yet (post-reboot settle race), the CommandNotFoundException leaves $LastExitCode unset and the provisioner "succeeds" with exit 0 without running the script. Finalize.ps1, the last provisioner, was skipped this way in every build: the plugin shut the VM down seconds later and shipped an image with no Cloudbase-Init, no cleanup and no sysprep.

Fixing the silent skip then exposed three more latent bugs in scripts that had never actually executed:

  • sysprep was a no-op: the /unattend:"C:\Program Files..." argument quoting breaks under Start-Process, sysprep logs "Unable to parse command-line arguments" yet exits 0. Copy the answer file to a space-free path and require Sysprep_succeeded.tag afterwards.
  • the Proxmox cloud-init password was never applied on clones: CreateUserPlugin sets a RANDOM password; the metadata admin_pass is applied by SetUserPasswordPlugin, which was missing from cloudbase-init.conf's plugin list.
  • winrm set ... @{...} never worked from PowerShell (the hashtable argument is stringified to "System.Collections.Hashtable"); route the calls through cmd.exe /c with literal text.

Changes:

  • recipes: hardened execute_command (waits up to 120 s for the uploaded script, runs under ErrorActionPreference=Stop so any failure aborts the build loudly) + pause_before=30s on every post-restart powershell provisioner, in all three Windows recipes
  • all scripts end with an explicit exit 0 so a stale $LastExitCode from an earlier native command cannot fail a successful run
  • Cloudbase-Init is installed in Finalize.ps1, after the last Windows Update pass and right before sysprep, instead of in Install.ps1 (survives Server 2025's UpdateAgent checkpoint-CU OS re-deploy)
  • cloudbase-init.conf: add SetUserPasswordPlugin, replace deprecated config_drive_* options with the [config_drive] section, set local_scripts_path (LocalScriptsPlugin was a no-op without it), check_latest_version=false, quieter default_log_levels
  • scripts are kept pure ASCII: PowerShell 5.1 reads BOM-less files as ANSI, where an em-dash byte-decays into a closing quote and breaks parsing

Verified end-to-end on a live clone: the configdrive admin_pass is applied (local credential validation passes), the hostname is set, the volume is extended, and the image generalizes.

…n clones

Windows templates built "successfully" without Cloudbase-Init and without
sysprep. Root cause: Packer's default powershell execute_command ends with
`exit $LastExitCode` - when a provisioner starts right after a
windows-restart and the uploaded script path is not readable yet
(post-reboot settle race), the CommandNotFoundException leaves
$LastExitCode unset and the provisioner "succeeds" with exit 0 without
running the script. Finalize.ps1, the last provisioner, was skipped this
way in every build: the plugin shut the VM down seconds later and shipped
an image with no Cloudbase-Init, no cleanup and no sysprep.

Fixing the silent skip then exposed three more latent bugs in scripts
that had never actually executed:

- sysprep was a no-op: the /unattend:"C:\Program Files\..." argument
  quoting breaks under Start-Process, sysprep logs "Unable to parse
  command-line arguments" yet exits 0. Copy the answer file to a
  space-free path and require Sysprep_succeeded.tag afterwards.
- the Proxmox cloud-init password was never applied on clones:
  CreateUserPlugin sets a RANDOM password; the metadata admin_pass is
  applied by SetUserPasswordPlugin, which was missing from
  cloudbase-init.conf's plugin list.
- `winrm set ... @{...}` never worked from PowerShell (the hashtable
  argument is stringified to "System.Collections.Hashtable"); route the
  calls through cmd.exe /c with literal text.

Changes:

- recipes: hardened execute_command (waits up to 120 s for the uploaded
  script, runs under ErrorActionPreference=Stop so any failure aborts
  the build loudly) + pause_before=30s on every post-restart powershell
  provisioner, in all three Windows recipes
- all scripts end with an explicit `exit 0` so a stale $LastExitCode
  from an earlier native command cannot fail a successful run
- Cloudbase-Init is installed in Finalize.ps1, after the last Windows
  Update pass and right before sysprep, instead of in Install.ps1
  (survives Server 2025's UpdateAgent checkpoint-CU OS re-deploy)
- cloudbase-init.conf: add SetUserPasswordPlugin, replace deprecated
  config_drive_* options with the [config_drive] section, set
  local_scripts_path (LocalScriptsPlugin was a no-op without it),
  check_latest_version=false, quieter default_log_levels
- scripts are kept pure ASCII: PowerShell 5.1 reads BOM-less files as
  ANSI, where an em-dash byte-decays into a closing quote and breaks
  parsing

Verified end-to-end on a live clone: the configdrive admin_pass is
applied (local credential validation passes), the hostname is set, the
volume is extended, and the image generalizes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant