The new NAT virtual switch that can be created on Windows 10 for Hyper-V virtual machines is a wonderful thing if you're an on-the-go evangelist like myself. For more information on how to create one, see Thomas Maurer's post on the subject.
This post is not about creating a new NAT switch. It is, however, about recreating one and the pitfalls that occur, and how I now run my virtual environment with some hack PowerShell and a useful DHCP server utility.
Problems Creating Nat Switch? Check Assigned IP Addresses
I spent a frustrating amount of time this week trying to recreate a NAT switch after deleting it. Try as I might, every time I executed the command to create the new switch it would die. After trial and error I found that the issue was down to the address range I was using. If I created a new switch with a new address range everything worked, but only that one time: If I deleted the switch and tried again, any address range that I'd used would fail.
This got me digging.
I created a new switch with a new address range. The first thing I noticed was that I had a very long routing table. Get-netroute showed routes for all the address ranges I had previously created. That let me to look at the network adapter created by the virtual switch. When you create a new nat switch the resulting adapter gets the first IP address in the range bound to it (so 192.168.1.0/24 will result in an IP of 192.168.0.1). My adapter had an IP address for every single address range I'd created and then deleted.
Obviously, when the switch is removed the IP configuration is being stored by windows somewhere. When a new switch is created all that old binding information is reapplied to the new switch. I'm not certain whether this is related to the interface index, name or what, since when I remove and re-add the switch on my machine it always seems to get the same interface index.
A quick bit of PowerShell allowed me to rip all the IP addresses from the adapter at once. The commands below are straightforward. The first allows me to find the adapter by name (shown in the Network Connections section of control panel) - replace the relevant text with the name of your adapter. From that I can find the interface index, and the second command gets all the IPv4 addresses (only IPv4 seems to have the problem here) and removes them from the interface - again, swap your interface index in here. I can then use PowerShell to remove the VMswitch and associated NetNat object.
Get-NetAdapter -Name "vEthernet (NATSwitch)"
Get-NetIPAddress -InterfaceIndex 13 -AddressFamily IPv4 | Remove-NetIPAddress
Once that's done I can happily create new virtual switches using NAT and an address range I've previously had.
Using DHCP on a NAT switch for ease
My next quest was for a solution to the IP addressing conundrum we all have when running VMs: IP addresses. I could assign each VM a static address, but then I have to keep track of them. I also have a number of VMs in different environments that I want to run and I need external DNS to work. DHCP is the answer, but Windows 10 doesn't have a DHCP server and I don't want to build a VM just to do that.
I was really pleased to find that somebody has already written what I need: DHCP Server for Windows. This is a great utility that can run as a service or as a try app. It uses an ini file for configuration and by editing the ink file you can manage things like address reservations. Importantly, you can choose which interface the service binds to which means it can be run only against the virtual network and not a use issues elsewhere.
There's only one thing missing: DNS. Whilst the DHCP serer can run it's own DNS if you like, it still has a static configuration for the forwarder address. In a perfect world I'd like to be able to tell it to had my PCs primary DNS address to clients requesting an IP.
Enter PowerShell, stage left...
Using my best Google-fu I tracked down a great post by Lee Homes from a long time ago about using PowerShell to edit ini files through the old faithful Windows API calls for PrivateProfileString. I much prefer letting Windows deal with my config file than write some complex PowerShell parser.
I took Lee's code and created a single PowerShell module with three functions as per his post which I called Update-Inifiles.psm1. I then wrote another script that used those functions to edit the ini file for DHCPserver.
It's dirty and not tested on anything but my machine, but here it is:
$dnsaddr = (Get-DnsClientServerAddress -InterfaceIndex (get-netroute -DestinationPrefix 0.0.0.0/0).ifIndex -AddressFamily IPv4).ServerAddresses
if ($dnsaddr.Length -gt 0)
Set-PrivateProfileString "C:\Program Files\DHCPSrv\dhcpsrv.ini" GENERAL DNS_0 $dnsaddr
Set-PrivateProfileString "C:\Program Files\DHCPSrv\dhcpsrv.ini" GENERAL DNS_0 220.127.116.11
The second line is the one that may catch you out. It gets the DNS server information for the interface that is linked to the default IPv4 route. On my machine there are multiple entries returned by the get-netroute command, so I grab the first one from the array.
Similarly, there are multiple DNS servers returned and I only want the first one of those, too. I should really expand the code and check what's returned, but this is only for my PC - edit as you need!
Just in case I get nothing back I have a failsafe which is to set the value to the Google public DNS server on 18.104.22.168.
Now I run that script first, then start my DHCP server and all my VMs get valid IP information and can talk on whatever network I am connected to, be it physical or wireless.