Packer: Build Windows with SSH communicator

Hashicorp’s Packer allows you to build VM images automatically from code based on a fresh installation of the OS. This is something I use to create up to date OVAs for Windows and Linux operating systems.

After the unattended installation packer allows you to interact with the installed VM to run commands and adapt settings. When searching for ways of interaction with a Windows system you’ll mostly stumble upon the Packer WinRM communicator while for Linux SSH is the natural choice.

With Windows lately supporting the installation of OpenSSH I wanted to use SSH instead of WinRM for the communication when I set up my build scripts for Windows Server 2022 (which I’ll publish fully soon).

How it works

The automated Packer build for Windows consists basically of two phases. The first is the unattended installation of Windows, the second is running any kind of changes/scripts/commands on the freshly installed Windows through Packer.

Unattended installation with OpenSSH setup

A default Windows installation has no single way for remote connecting to the system and managing it, so in the first step of the unattended installation I have to install the OpenSSH server and let it make it’s way through the Windows Firewall.

To do this you must include the OpenSSH setup in the windows autounattend.xml which you use for the unattended Windows Server setup. You can use the Windows System Image Manager (Windows SIM) tool from the Windows Assessment and Deployment Kit (Windows ADK) to build these answer files.

I added the commands to setup OpenSSH in the section 7 oobeSystem / amd64_Microsoft-Windows-Shell-Setup_neutral / FirstLogonCommands:

Windows SIM showing where to place shell commands in autounattend.xml

Just right click on th “FirstLogonCommands” and click on “Insert New SynchronousCommand” to add a new command.

This is the resulting XML for the commands I used to set up SSH access to my fresh Windows installation:

...
            <FirstLogonCommands>
                <SynchronousCommand wcm:action="add">
                    <CommandLine>%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command Add-WindowsCapability -Online -Name "OpenSSH.Client~~~~0.0.1.0"</CommandLine>
                    <Description>Install the OpenSSH Client</Description>
                    <Order>1</Order>
                    <RequiresUserInput>true</RequiresUserInput>
                </SynchronousCommand>
                <SynchronousCommand wcm:action="add">
                    <CommandLine>%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command Add-WindowsCapability -Online -Name "OpenSSH.Server~~~~0.0.1.0"</CommandLine>
                    <Description>Install the OpenSSH Server</Description>
                    <Order>2</Order>
                    <RequiresUserInput>true</RequiresUserInput>
                </SynchronousCommand>                
                <SynchronousCommand wcm:action="add">
                    <CommandLine>%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command Start-Service sshd</CommandLine>
                    <Description>Start SSHD service</Description>
                    <Order>3</Order>
                    <RequiresUserInput>true</RequiresUserInput>
                </SynchronousCommand>
                <SynchronousCommand wcm:action="add">
                    <CommandLine>%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command Set-Service -Name sshd -StartupType 'Automatic'</CommandLine>
                    <Description>Configure SSHD for automatic start</Description>
                    <Order>4</Order>
                    <RequiresUserInput>true</RequiresUserInput>
                </SynchronousCommand>

               // ... some other commands not related to SSH

                <SynchronousCommand wcm:action="add">
                    <CommandLine>%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22</CommandLine>
                    <Description>Add firewall exception for SSHD - last step so packer SSH connection waits until here</Description>
                    <Order>11</Order>
                    <RequiresUserInput>true</RequiresUserInput>
                </SynchronousCommand>
            </FirstLogonCommands>
...

After running the first 4 commands for installing SSH client & server and starting and enabling the SSHD service only in the very last command I will open the firewall port for SSH (TCP 22). This is because packer tries to connect to the SSH service after receiving the IP address of the VM (which it has already at that moment in time) and would start issuing commands to the Windows installation. I prevent Packer from interacting with the system too early by just allowing SSH access as the very last step in my shell setup process.

Packer config for using SSH communicator

Using the SSH communicator in Packer for the Windows system is then straightforward. Just put the normal SSH config to your builders section like this:

{  
  ....
  "builders": [
    {      
      "communicator": "ssh",
      "ssh_username": "{{user `os_user`}}",
      "ssh_password": "{{user `os_password`}}",
      "ssh_timeout": "1h",
      "ssh_clear_authorized_keys": true,
      ...    
    }
  ],
  "provisioners": [
    {
      "type": "powershell",
      "scripts": [
        "scripts/win-update.ps1"
      ],
      "valid_exit_codes": [ 0, 2300218 ]
    },
    {
      "type": "windows-restart",
      "pause_before": "30s",
      "restart_timeout": "30m"
    },
    ...
  ]
}

ssh_username and ssh_password uses credentials configured also in the autounattend.xml. As you see you can then already use the normal powershell provisioner which now works via SSH instead of WinRM, leaving your build VM image much more secure.

Stay tuned for another article where I present my full Windows Server 2022 packer build and also release the source code.

One Reply to “Packer: Build Windows with SSH communicator”

Leave a Reply

Your email address will not be published.

13 − 9 =