Packer’s communicators, SSH and WinRM, are the unsung heroes that bridge the gap between your Packer build process and the target machine.

Let’s watch Packer connect to a machine using SSH and provision it.

packer build \
  -var 'ssh_username=packer' \
  -var 'ssh_password=password' \
  -var 'ssh_host=192.168.1.100' \
  -var 'ssh_port=22' \
  template.json

Here’s a template.json that uses SSH to connect:

{
  "builders": [
    {
      "type": "amazon-ebs",
      "region": "us-east-1",
      "source_ami": "ami-0c55b159cbfafe1f0",
      "instance_type": "t2.micro",

      "ssh_username": "{{user `ssh_username`}}",


      "ssh_password": "{{user `ssh_password`}}",

      "communicator": "ssh",

      "ami_name": "my-packer-image-{{timestamp}}"

    }
  ],
  "provisioners": [
    {
      "type": "shell",
      "inline": [
        "echo 'Hello from Packer!'"
      ]
    }
  ]
}

When Packer builds this, it first launches an EC2 instance. Once the instance is running and accessible via SSH, Packer attempts to connect using the credentials and host details provided. If successful, it then executes the provisioners.

The communicator field in the builder block is key here. It tells Packer how to talk to the machine after it’s provisioned. For Linux and macOS, ssh is the default and most common choice. For Windows, winrm is the standard.

WinRM, or Windows Remote Management, is a Microsoft protocol for remote management. Packer uses it to execute commands and transfer files to Windows machines.

Let’s see a WinRM example.

packer build \
  -var 'winrm_username=Administrator' \
  -var 'winrm_password=SuperSecretPassword' \
  -var 'winrm_host=192.168.1.101' \
  -var 'winrm_port=5985' \
  -var 'winrm_timeout=10m' \
  windows_template.json

And the corresponding windows_template.json:

{
  "builders": [
    {
      "type": "amazon-ebs",
      "region": "us-east-1",
      "source_ami": "ami-0a5c65742382847f0",
      "instance_type": "t2.micro",
      "communicator": "winrm",

      "winrm_username": "{{user `winrm_username`}}",


      "winrm_password": "{{user `winrm_password`}}",


      "winrm_timeout": "{{user `winrm_timeout`}}",


      "ami_name": "my-windows-packer-image-{{timestamp}}"

    }
  ],
  "provisioners": [
    {
      "type": "powershell",
      "inline": [
        "Write-Host 'Hello from Packer on Windows!'"
      ]
    }
  ]
}

The winrm_timeout variable is crucial for Windows builds. Windows instances can take longer to boot up and become ready for WinRM connections than typical Linux instances for SSH. Setting a generous timeout, like 10m (10 minutes), prevents premature build failures.

The winrm_port is typically 5985 for HTTP and 5986 for HTTPS. If you’re using HTTPS, you’ll also need to configure certificate handling, which can be more complex. For simplicity, many start with HTTP.

The most surprising thing about Packer communicators is how they abstract away the network protocol details, allowing you to focus on what you want to provision, not how to connect. You just define the builder with the communicator type and its associated credentials, and Packer handles the handshake.

Packer’s communicator configuration is a direct mapping of the underlying protocol’s requirements. For SSH, it’s the username, password (or key), host, and port. For WinRM, it’s username, password, host, port, and importantly, the timeout.

The winrm_insecure option is another critical setting for WinRM. When set to true, it bypasses SSL certificate validation. This is often necessary in initial setup phases or when dealing with self-signed certificates, but it comes with security implications. Always be mindful of this when enabling it.

You can also specify ssh_private_key_file for SSH authentication, which is generally more secure than password-based authentication.

This level of abstraction means that if you’re building a Linux image, you can swap out the builder from amazon-ebs to docker or virtualbox and, as long as the new builder supports SSH, your ssh_username, ssh_password, and ssh_port settings might remain largely the same, with only minor adjustments for the new target environment.

The next step after successfully configuring your communicator is often dealing with complex provisioning scenarios, like bootstrapping Docker or configuring custom software repositories.

Want structured learning?

Take the full Packer course →