4

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?

4
  • The http://localhost address never changes. Why not use it?
    – harrymc
    Commented Aug 19, 2019 at 9:15
  • @harrymc Can I reach a client from the host like that? Commented Aug 19, 2019 at 12:57
  • I misread your question, so added an answer below.
    – harrymc
    Commented Aug 19, 2019 at 15:43
  • Somewhere http://my-own-name will have to point at an IP address. If the IP changes you have to update wherever it is registered. The only way around having that as static or needing to update it all the time is to use a system where the server notifies the system holding the DNS record of changes... Dynamic DNS ?
    – Smock
    Commented Aug 23, 2019 at 13:12

6 Answers 6

5
+75

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.)

7
  • For the path issue, that could be solved easily, but it's not a beautiful solution and would work best in a script: make a backup of hosts (hosts.bak), keep -Path as specified, then concatenate hosts.bak to hosts: cmd /c type 'hosts.bak' > 'hosts' (PowerShell does not recognize type, so it must be passed as a cmd command)
    – JW0914
    Commented Aug 19, 2019 at 16:11
  • Didn’t get to test it before the expiry period ended but peeking inside is probably the best approach. Hence accepted. Commented Aug 27, 2019 at 9:50
  • Checked it now. Seems to work as expected with all running vm's talking to the standardswitch. I just need to be sure that there is no spaces in the vm names :) Commented Aug 27, 2019 at 13:06
  • But... what if the IP address changes? Is that even possible? Or will Hyper-V always assign it the same IP?
    – Michael
    Commented Aug 13, 2022 at 22:35
  • Works just beautifully. +1
    – Sabuncu
    Commented Sep 10, 2023 at 11:15
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).

3
  • Nice trick! However, this may not work anymore if the IP got updated dynamically. IP will need to be static in this case.
    – Ares Li
    Commented Aug 14, 2019 at 14:18
  • @AresLi - You have only two solutions to that problem. Cnfigure a DNS provider internal to your network, and set your DNS provider on each device, to your internal DNS server The other choice is to make your internal IP address static.
    – Ramhound
    Commented Aug 14, 2019 at 16:21
  • without hardcoding the IP-number anywhere.” - and this does exactly that... Commented Aug 14, 2019 at 16:36
3

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

1
  • Amazing. This should absolutely be the correct answer. The key part (hosts.ics and the mshome.net suffix) is almost lost in the large wall of text, but the answer is in there!
    – Greg Woods
    Commented Jul 19, 2023 at 6:38
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 "`n`n"

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 ".*$hostName.*"
      if($vmPreviouslyAddedToHostsFile)
      {
         Write-Verbose "VM $vmName already present in hosts, updating entry."
         # # 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 `""`.
         # 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 "([^\d]*)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(\s+$hostName.*)","`${1}$address`${3}" -join "`r`n"
      }
      else
      {
         Write-Verbose "VM $vmName not previously in hosts, appending entry."
         $hostsFileContent += "$address $hostName" -join "`r`n"
      }
   }
   else
   {
      Write-Verbose "VM ${vmname} doesn't have IP addresses, skipping."
   }
}

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

Write-Debug "`n`nUpdated 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.

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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .