PVE 自带的 Cloud-Init 仅对 Linux 友好,Windows 需要安装 Cloudbase-Init 才能消费 PVE 注入的 ConfigDrive。本文用一个一键脚本完成 Cloudbase-Init 配置和 sysprep,做出可重复克隆的 Windows 模板。

整体流程

  1. PVE 上正常装好 Windows,做一次快照(出问题方便回滚)

  2. 在 VM 中安装 Cloudbase-Init安装结尾不要勾选 Run Sysprep

  3. 在 VM 中以管理员身份打开 PowerShell,一键执行:

    1Set-ExecutionPolicy Bypass -Scope Process -Force; iex (irm 'https://x.csz.net/scripts/Setup-CloudbaseInit.ps1')
    
  4. 看到提示输入 YES,脚本会自动跑 sysprep 并关机

  5. 在 PVE 把这台 VM 转模板:

    1qm set <id> --ide2 local-lvm:cloudinit
    2qm template <id>
    
  6. 克隆出新 VM,在 Cloud-Init 标签页把 User 填 cloudadmin、密码自定,点击 Regenerate Image

  7. 启动 VM,等约 1 分钟,用 cloudadmin + 设置的密码登录

改密码

PVE 控制台改 Password → Regenerate Image → 重启 VM → 新密码生效。注意一定要点 Regenerate Image,否则 ConfigDrive 还是旧的。

脚本说明

脚本做了几件事:

  • 备份 C:\Program Files\Cloudbase Solutions\Cloudbase-Init\conf 下原配置
  • 写入三个 base64 编码的配置文件(避免引号/编码被转码工具破坏):
    • cloudbase-init.conf:主配置,启用 ConfigDrive、用户 cloudadmin、加载完整插件链
    • cloudbase-init-unattend.conf:sysprep specialize 阶段使用,仅做磁盘扩展
    • Unattend.xml:sysprep 应答文件,跳过 OOBE、开启 RDP、设置时区为 China Standard Time
  • 设置 cloudbase-init 服务为自动启动
  • 清理用户安装但未 provisioned 的 AppX 包(最常见的 sysprep 阻塞原因)
  • 清空 Cloudbase-Init 的运行历史注册表,确保下次启动重新执行
  • 执行 sysprep /generalize /oobe /shutdown

脚本源码:Setup-CloudbaseInit.ps1

可选参数:

  • -SkipSysprep:只写配置不跑 sysprep
  • -SkipAppxCleanup:跳过 AppX 清理(确认环境干净时才用)

iex 不方便传参,需要带参数运行时改用:

1Set-ExecutionPolicy Bypass -Scope Process -Force
2$src = irm 'https://x.csz.net/scripts/Setup-CloudbaseInit.ps1'
3& ([scriptblock]::Create($src)) -SkipSysprep

常见坑

  • sysprep 报错退出:99% 是 AppX 阻塞,看 C:\Windows\System32\Sysprep\Panther\setupact.log 找到具体包名,加到清理逻辑或手动 Remove-AppxPackage
  • 克隆后登录失败:检查 PVE Cloud-Init 标签页是否点了 Regenerate Image,以及 ConfigDrive 设备是否挂上(qm config <id> 应能看到 ide2: ...,media=cdrom
  • 改密码不生效:同上,必须 Regenerate Image 后重启,Cloudbase-Init 默认每次开机都会读 ConfigDrive 重设密码
  • 想保留首次登录改密:脚本里 first_logon_behaviour=no 关闭了首次登录强制改密,需要的话改成 always