If you’ve spent any time deploying Windows Server 2008 using VMware vCenter’s Customization Specifications, you’ve probably run into a weird issue where the OS doesn’t retain its default gateway after a reboot. This is actually a known bug in Windows 2008, and it occurs when the gateway is set with netsh. When that happens, a null character is inserted before the gateway IP address in the registry. You can see this by going to HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\%GUID%\DefaultGateway.
Normally, you might fix this by going into the registry, deleting the offending value, resetting the gateway IP address, and then rebooting. This is irritating at best when you encounter it once or twice, but when you’ve got 40+ servers with this issue, it becomes a major problem. And that’s where PowerCLI comes into play.
PowerCLI 4.1 comes with a cool little cmdlet called Get-VMGuestNetworkInterface. The cmdlet works by running a batch file on the guest server to extract the necessary information from ipconfig /all. This works great for Windows 2003, Windows XP, & RHEL 5 (the only supported OS’es), but doesn’t work at all on Windows 2008, because of the additional networking information that ipconfig /all provides – IPv6 interfaces and others.
When it first came to my attention that I had a large number of broken servers (they were quickly provisioned, and then we realized afterwards they were broken), I tried modifying the batch file that Get-VMGuestNetworkInterface uses, with the hope that I could fix the batch file for Set-VMGuestNetworkInterface as well. The batch file works by piping the output of ipconfig /all to a temp file, removing extraneous characters such as “. . .” and going from there.
This approach works great when you’ve only got one interface, but it completely breaks down with multiple interfaces. My next idea was to use PowerShell’s Get-WmiObject and the Win32_NetworkAdapterConfiguration class to get & set the correct default gateway using the class’ SetGateways() method.
Unfortunately, that doesn’t work either because it doesn’t persist through reboots. The method’s return code shows successful, and pinging to/from the server works, but after a reboot the VM goes back to a unresponsive network state. Setting the gateway via that method ultimately doesn’t fix the null character issue.
Which brings me to the solution to this little puzzle: using PowerShell’s default registry PS-drive to access, clear, and reset the offending value. My solution is still modeled after the original Get-VMGuestNetworkInterface, which involves two parts: a script to connect to the guest, set PowerShell’s execution policy to RemoteSigned, copying the script that fixes the gateway to the guest, then executing that script.
Reset_VM_Gateway.ps1
[powershell]
#VM or naming convention to search for
$name = Read-Host "Please enter the VM or the naming convention to search for"
$location = Read-Host "Please enter the vCenter folder containing VM’s you’d like to fix"
#Paths to the scripts to copy & run on guest
$srcscript = ‘C:\Support\Scripts\VMware\Development\Reset_VM_Gateway_GuestScript.ps1’
$dstscript = ‘C:\Support\Scripts\Reset_VM_Gateway_GuestScript.ps1’
$dest = ‘C:\Support\Scripts\’
#Get ESX(i) host credentials – will not work if ESXi host is in lockdown mode
$hostcred = $Host.UI.PromptForCredential("vSphere host credentials", "Please enter the vSphere host’s root password","root","")
#Get the VM guest administrative credentials
$guestcred = $Host.UI.PromptForCredential("VM guest credentials","Please enter the guest’s administrative credentials","","")
#Get the vCenter credentials, if there’s not already a $Credential loaded from profile, etc
if(!$Credential){$Credential = Get-Credential}
#FQDN for vCenter server
$vcenter = Read-Host "FQDN of the vCenter server"
#Connecting to vCenter server
if(!$viserver){Connect-VIServer $vcenter -Protocol https -Credential $Credential}
$vms = Get-VM -Location $location | where {$_.Name -like "*$name*" -and $_.PowerState -eq "PoweredOn"}
foreach ($vm in $vms) {
$ping = New-Object System.Net.NetworkInformation.Ping
try
{
$status = [string]($ping.Send($vm)).Status
}
catch [System.Net.NetworkInformation.PingException]{$status = $null}
switch($status){
Success {}
Default {
Write-Host "$vm not responding. Attempting to fix."
Invoke-VMScript -ScriptText "Set-ExecutionPolicy RemoteSigned" -VM $vm -HostCredential $hostcred -GuestCredential $guestcred
Copy-VMGuestFile -Source $srcscript -Destination $dest -VM $vm -Force -HostCredential $hostcred -GuestCredential $guestcred -LocalToGuest
Invoke-VMScript -ScriptText $dstscript -VM $vm -HostCredential $hostcred -GuestCredential $guestcred
Restart-VMGuest $vm -Confirm:$false
}
}
}
if($viserver){Disconnect-VIServer $vcenter}
[/powershell]
Reset_VM_Gateway_GuestScript.ps1
[powershell]
#VMXnet & Enhanced VMXnet NIC’s use the service name "VMXNET". e1000’s is "E1G60"
$nic = Get-WmiObject Win32_NetworkAdapterConfiguration | where {$_.ServiceName -eq "VMXNET"}
$ipaddr = [string]$nic.IPAddress
$octet = $ipaddr.Split(".")
#creating the gateway address. Works best with /24 netmasks.
$gatewayaddr = $octet[0] + "." + $octet[1] + "." + $octet[2] + ".1"
#Assigning the interface’s GUID to a variable
$nic_settingid = [string]$nic.SettingID
#Clearing the offending value
Clear-ItemProperty -Path "HKLM:\System\CurrentControlSet\Services\TcpIP\Parameters\Interfaces\$nic_settingid" -Name DefaultGateway
#Setting the correct value
Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Services\TcpIP\Parameters\Interfaces\$nic_settingid" -Name DefaultGateway -value $gatewayaddr
[/powershell]
[…] determine which VM’s are not responding on the network. This is a slight modification from my Windows 2008 Guest Networking: PowerCLI […]