Back to blog
FILE 0x76·SLIPSTREAMING SSH INTO A PXE-BOOTED WINDOWS INSTALL

Slipstreaming SSH into a PXE-booted Windows install

May 7, 2026 · homelab, windows, pxe, automation

PXE booting a Windows 11 install is fine; getting a freshly installed VM to accept SSH from my homelab without me touching it is the actual interesting part.

What was happening

After the unattended install finished, every new VM needed manual setup: create an admin user, enable RDP or OpenSSH, turn off the firewall so my LXC could reach it, drop authorized keys, disable sleep so the box didn't disappear after 20 minutes. Doing that by hand defeats the point of having a PXE menu.

What I found

autounattend.xml runs scripts in the oobeSystem pass after first boot. That's the right hook for everything that has to happen "once the OS is alive but before the user logs in." The pieces that needed to land:

The fix

The relevant chunk of the unattend, sketched:

<SynchronousCommand wcm:action="add">
  <Order>1</Order>
  <CommandLine>powershell -ExecutionPolicy Bypass -Command "
    Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0;
    Start-Service sshd;
    Set-Service -Name sshd -StartupType Automatic;
    New-Item -ItemType Directory -Path C:\ProgramData\ssh -Force;
    Set-Content C:\ProgramData\ssh\administrators_authorized_keys -Value 'ssh-ed25519 AAAA... lxc-admin';
    icacls C:\ProgramData\ssh\administrators_authorized_keys /inheritance:r /grant 'Administrators:F' 'SYSTEM:F';
    Set-NetFirewallProfile -All -Enabled False;
    powercfg -change -standby-timeout-ac 0;
    powercfg -change -hibernate-timeout-ac 0;
    powercfg -change -monitor-timeout-ac 0;
    New-Item C:\Users\Public\provisioned.txt -Force;
  "</CommandLine>
</SynchronousCommand>

The administrators_authorized_keys path with the locked-down ACL is the specifically-correct location for ed25519 keys when the user is in the local Administrators group — Microsoft's OpenSSH ignores ~/.ssh/authorized_keys for admins by design.

OpenSSH installs via FoD so the VM needs internet during first boot. If you need a truly offline install, bundle the OpenSSH MSI on the ISO and install that instead. I left it online because the boot path through the local PXE menu means a working LAN by definition.

The completion marker means I can poll from any other box:

until ssh -o StrictHostKeyChecking=no admin@new-vm 'type C:\Users\Public\provisioned.txt'; do
  sleep 10
done

and only start the next step once the VM has actually finished provisioning.

What I'd do differently

I should have rotated the throwaway admin password as the last step of the unattend, not left it static. It's fine for an isolated VLAN, but it's a papercut waiting to happen if one of these VMs ever gets a routable IP. Easy fix: generate a per-VM password during unattend, write the hash to the local SAM, drop the plaintext somewhere I can pick it up over SSH, then have the SSH session rotate the SSH host keys too.