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.
http://localhost
address never changes. Why not use it?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 ?