6

I have installed Ubuntu 18.04 in Hyper-V on a Windows 10 host and exposed functionality on some ports, which are available to other programs running on the host when accessing http://vm-ip-number:port (i.e. http://192.168.0.1:12345). Works very well.

I would like to use a name instead of the ip-number of the VM to connect to it (for all the usual reasons as this will go in configuration files). For full Linux or MacOS servers I would let the host announce itself using zeroconf, but some searching indicated that this doesn't work well for Windows 10 across VM boundaries.

Another approach might be having Hyper-V assign the VM a name provided by me which is then available when the VM is up, but I am not familiar enough with Hyper-V to know if this is possible or not. Installing additional tools just for this in the guest is acceptable, in the host only if necessary.

All I want is to be able to use http://my-own-name to access the VM when running without hardcoding the IP-number anywhere.

Suggestions?

6 Answers6

5

The following PowerShell script will generate a hosts file that contains the IP-addresses and VM-names for all running VMs:

get-vm | Get-VMNetworkAdapter | ? IPAddresses -ne $null | % {Write-Output "$($_.ipaddresses[0]) $($_.VMName)"} | Set-Content -Path ".\hosts"

Some points to note:

  • The script needs to run as Administrator.

  • The Set-Content command needs a better Path parameter, as currently it will over-write the hosts file in the current folder.

  • If you don't want the hosts file to be over-written, but appended, use Add-Content command instead. This way you can start from an initial hosts file and just append to it.

  • The Write-Output command outputs the first entry in the ipaddresses field. If both IPv4 and IPv6 are enabled, the list will contain two entries, where the first entry is usually that of IPv4.

An example hosts file that was generated for my one running VM by this script:

172.17.223.121 Windows10Test

(It's probably better to avoid blanks in the VM names, or more work is needed on the script.)

harrymc
  • 498,455
4

Add the IP address in Windows HOSTS file. The full path is C:\Windows\System32\drivers\etc\hosts. Run Notepad (or any text editor) as administrator and open that file with Ctrl + O. Then add the IP address. The format will be IP address then guest name. Here is a sample:

# This is a comment
# 127.0.0.1 localhost loopback
# ::1 localhost

192.168.0.1 myguest

For further information, read Wikipedia: hosts(file).

Biswapriyo
  • 11,584
4

I found a way to to connect by name to guest VMs from other guest VMs or the host, using names that are automatically managed by Hyper-V, as follows:

  1. Connect the guest to the Default Switch (this is the default for a new VM).
  2. Set the IP address to DHCP on the guest (this is the default for a new VM).
  3. Set the computer name / hostname within the guest OS to something unique, e.g. myfirstvm. The DNS suffix doesn't matter.
  4. From another VM or the host, try contacting <host_name>.mshome.net, e.g. if you set the hostname to myfirstvm, try pinging myfirstvm.mshome.net from the host.

Explanation:

On my system I found a file in the same directory as the hosts file called hosts.ics. It appeared to contain entries for all my guests attached to Default Switch and an entry for the base system, but all with the suffix mshome.net. I believe that network adapters connected to the default switch use the Internet Connection Sharing feature, which automatically creates and maintains this hosts.ics file. When your guest boots, it requests an IP address by DHCP and attempts to register itself with the DNS server built into Hyper-V. I believe that Hyper-V takes the name that the guest tries to register and stores it in this hosts.ics file with a new suffix of mshome.net. If two of your guest VMs both try to register the same name then this process will break.

Not sure if this is a new feature or has always been here. Found on Windows 11 Pro, Hyper-V 10.0.22000.1

knuckles
  • 171
1

This script is based on https://superuser.com/a/1472928/90752 but it automatically backs up your hosts file and creates a new one in the appropriate place based on the ${env:SystemRoot} environment variable. It also cleans up any spaces or dots in your vm names and gives it a .local DNS name.

#Requires -RunAsAdministrator
Set-StrictMode -Version 3.0
$ErrorActionPreference = "Stop"

$hostsFile = "${env:SystemRoot}\System32\drivers\etc\hosts"

$datestring = (Get-Date -Format "o") -Replace '[:\-]','-'
$backupHostsFile = "$hostsFile.${datestring}.bak"

echo "Backing up hosts file to $backupHostsFile"
cp $hostsFile $backupHostsFile

$content = ''

foreach ($vm in (get-vm | Get-VMNetworkAdapter)) {
    $vmname = $vm.VMName.ToLower() -replace '[. ]',''
    $hostName = "${vmname}.local"
    if ($vm.IPAddresses -ne $null) {
        echo "Writing hosts entry for $vm"
        $address = $vm.IPAddresses[0]
        $content += "$address $hostName `n"
    } else {
        echo "Ignoring $vm"
    }
}

Set-Content -Path "$hostsFile" -Value $content
1

This powershell script builds on Luke Schlather's answer (https://superuser.com/a/1485949/280673) to this question.

The most important difference is that it doesn't overwrite the entire hosts file with your VMs IP addresses, but instead appends new entries if there are new VMs and overwrites the addresses of preexisting entries. This should remove the need for making backups, and preserves other entries in the hosts file you might have made for local development or other purposes.

#Requires -RunAsAdministrator

Based on: https://superuser.com/a/1485949

Write-Host "Fix entries for local HyperV VMs in hosts file."

Set-StrictMode -Version 3.0 $ErrorActionPreference = "Stop" $hostsFile = "${env:SystemRoot}/System32/drivers/etc/hosts"

$hostsFileContentOriginal = (Get-Content $hostsFile) $hostsFileContent = $hostsFileContentOriginal

Write-Debug "Original hosts file content:" Write-Debug ($hostsFileContent | Out-String) Write-Debug "nn"

foreach ($vm in (Get-VM | Get-VMNetworkAdapter)) { $vmName = $($vm.VMName) $vmNameSanitized = $vm.VMName.ToLower() -replace '[. ]','' $hostName = "${vmNameSanitized}.local" Write-Verbose "Porcessing $vm." if (0 -lt $vm.IPAddresses.Length) { Write-Verbose "VM ${vmname} has IP addresses, updating hosts file." $address = $vm.IPAddresses[0] Write-Verbose "Selected IP Address for ${vmname}: $address." Write-Debug "All IP Addresses for ${vmname}: $($vm.IPAddresses)."

  $vmPreviouslyAddedToHostsFile = $hostsFileContent -match &quot;.*$hostName.*&quot;
  if($vmPreviouslyAddedToHostsFile)
  {
     Write-Verbose &quot;VM $vmName already present in hosts, updating entry.&quot;
     # # Regex explanation
     # - `([^\d]*)` is a grouping `()` of 0 or more of the character group `[]*` that contains no digits `^\d`.
     # - `(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})` is a grouping `()` of 4 collections of 1 to 3 digits `\d{1,3}`, separated by a dot `\.`, which should match an ipv4 address.
     # - `(\s+$hostName.*)` is a grouping `()` that matches 1 or more spaces `\s+`, followed by the content of the hostname variable `$hostName`, followed by 0 or more of any characters `.*`.
     # # Substitution expression explanation
     # `${n}` is used to access the n-th (1 indexed) grouping from the regex.
     # Because we want to access the `$address` variable we can't use literal strings `''` but have to use normal strings instead `&quot;&quot;`.
     # This results in having to escape the `$` character when doing the group substitution access.
     # # Caveat emptor
     # This only works for ipv4 addresses as written.
     # If you want to support ipv6 addresses, consider using `([a-f0-9:]+:+)+[a-f0-9]+` from https://stackoverflow.com/a/37355379 .
     # It will fail in interesting ways if the entry is commented out and there are digits in the comment in a position preceding the address.
     $hostsFileContent = $hostsFileContent -replace &quot;([^\d]*)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(\s+$hostName.*)&quot;,&quot;`${1}$address`${3}&quot; -join &quot;`r`n&quot;
  }
  else
  {
     Write-Verbose &quot;VM $vmName not previously in hosts, appending entry.&quot;
     $hostsFileContent += &quot;$address $hostName&quot; -join &quot;`r`n&quot;
  }

} else { Write-Verbose "VM ${vmname} doesn't have IP addresses, skipping." } }

$originalAsString = ($hostsFileContentOriginal | Out-String) $updatedAsString = ($hostsFileContent | Out-String)

Write-Debug "nnUpdated hosts file content:" Write-Debug "$updatedAsString"

if($originalAsString -ne $updatedAsString) { Write-Host "There are new addresses for VMs, updtating hosts file."

$datestring = (Get-Date -Format "o") -replace '[:-]','-'

$backupHostsFile = "$hostsFile.${datestring}.bak"

Write-Verbose "Backing up hosts file to $backupHostsFile."

Copy-Item $hostsFile $backupHostsFile

Write-Verbose "Setting new hosts file content."

Set-Content -Path $hostsfile -Value $hostsFileContent -Encoding UTF8 } else { Write-Host "No new VMs or addresses, exiting." }

Notes

Depending on your VM, you might have to install additional packages inside the VM, for instance hyperv on arch, before you can dig out the IP that gets assigned to the VM from HyperV and powershell. This will likely vary between different operating systems, so you will likely have to look up a guide for your system (or post a new question) if you can't pick up the address from the host side.

I've left the backup section and just commented it out, to make it easier to experiment with and iterate on the script. Also to make it easier to make backups if they're wanted at a more sensible place (no need for backups if no changes are being done, so don't make them until right before the changes are written).

I have also put the commentary on how the regex for substituting the address works in the code, instead of outside here in the post body. This is to make it easier to follow the code should the reference to this post be lost after some copy-pasting, and hopefully be of help should someone want to make changes to it.

simonra
  • 175
0

The internal address shouldn't change (statically set within the OS or DHCP reserved - in Hyper-V you can configure a specific MAC address as well if you wish to be extra sure the IP never changes), so there should be no fear of "hardcoding" the IP address somewhere.

You can use a HOSTS file as suggested by @biswapriyo, but that you would need to configure on every client that needs access to this service. The real answer is an internal DNS server. You can run this on Windows Server or Linux (there are tons of resources on how to do this - I have used PiHole in Hyper-V in the past and it works well for this), and then configure any name for this device.