"winrm.port" does not work in Vagrantfile

Hi,

I am trying to spin up a Windows box using the following fairly basic Vagrant file:


# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  ENV['VAGRANT_DEFAULT_PROVIDER'] = 'virtualbox'
  
  # Disable default port forwarding rules (We will define custom ones per host later)
  config.vm.network :forwarded_port, guest: 3389, host: 3389, id: "rdp", disabled: "true"
  config.vm.network :forwarded_port, guest: 22, host: 2222, id: "ssh", disabled: "true"
  config.vm.network :forwarded_port, guest: 5895, host: 55895, id: "winrm", disabled: "true"
  config.vm.network :forwarded_port, guest: 5896, host: 55896, id: "winrm-ssl", disabled: "true"
  # Create custom forwarding ports
  config.vm.network :forwarded_port, guest: 5985, host: 58951, id: "winrm-wsl2", auto_correct: true
  config.vm.network :forwarded_port, guest: 3389, host: 33891, id: "rdp-wsl2", auto_correct: true
  
  config.vm.guest = :windows
  config.vm.communicator = "winrm"
  config.winrm.retry_limit = 10
  config.winrm.retry_delay = 5
  config.winrm.username = "redacted"
  config.winrm.password = "redacted"
  config.vm.box = "StefanScherer/windows_2022"
  config.vm.hostname = "dc01"
  config.vm.network "private_network", ip: "192.168.100.10"
  
  # 172.30.192.1 is the gateway of the WSL2 device which will get NATed to the Windows host, after which it will reach virtualbox where it is NATed again to the guest
  config.winrm.host = "172.30.192.1"
  config.winrm.port = 58951
  # Tried this as well, did not work either
  #config.winrm.guest_port = 5985

The issue is with the WinRM port. For some reason, Vagrant refuses to utilize the custom port I set with the config.winrm.port = 58951 setting. The reason I want to use a custom port is that I want to run multiple boxes using WinRM and want to keep it a little bit cleaner by specifying increasing port numbers (58951, 58952, …). I also need a predictable WinRM port so it can be defined in the Ansible configuration. Regardless of the reason, it should work the way I am implementing it currently, but I don’t see why it is not taking it.

As you can see on the following screenshot, it uses the winrm.host option just fine, but no matter what I try, it keeps using the default port 5985 instead of the custom port I configured.

A WireShark capture also shows that it is actually trying to connect on port 5985, and not on port 59851.

If I just let it use port 5985 without and add a port forwarding rule for that, then vagrant successfully connects to the box with WinRM. (Which is fine for one box, but not for multiple. And I don’t want to rely on the auto_correct feature for all of the boxes. The config.winrm.port setting is there for a reason, so there should be a way to make this work right?)

After some reverse engineering of the Vagrant source code, I identified the following bug which is causing this behavior:

# Line 22
info[:port] ||= winrm_port(machine, info[:host] == "127.0.0.1")

# ...

# Line 49 - 54
def self.winrm_port(machine, local=true)
        host_port = machine.config.winrm.port
        if machine.config.winrm.guest_port
          # If we're not requesting a local port, return
          # the guest port directly.
          return machine.config.winrm.guest_port if !local

To summarize, if the winrm.host setting is set to 127.0.0.1, the host local variable will be true and the subsequent lines of code will figure out the actual port that we want to use by checking the port-forwarding settings.

However, when winrm.host is anything other than 127.0.0.1, it will just ignore the port forwarding rules and instantly return the value of winrm.guest_port (which is weird because according to the documentation, that is not what this setting is meant to be used for… It should only be used to attempt to automatically resolve the correct NAT port…)

The catch is that all of this should only ever happen when winrm.guest_port is actually set in the config due to the line 51: if machine.config.winrm.guest_port. However, even though I am not manually setting winrm.guest_port anywhere in my vagrant configuration, it will automatically be set indirectly to its default value 5985 if it is not specified, which results in always going down this code path.

TLDR; If your winrm.host is not set to 127.0.0.1, vagrant will always ignore your winrm.port setting and use winrm.guest_port instead (defaulting to 5985 if winrm.guest_port is not set)