Using the wrong bind order for multihomed servers can cause serious problems. Applications not working, network segments flooded with unneccesary traffic and even security leaks are some of the dangers.
That’s why I’ve created a Powershell script that checks all your servers for you. Let me explain how it works:
The binding order is store in the following registry key:
HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Linkage
It contains a multi-string value Bind such as this one:
DeviceNetBT_Tcpip_{A0344E82-4E65-47A2-92FF-B3183A181473}
DeviceNetBT_Tcpip_{C34D5A39-DA8D-4D58-82D4-099098D5501C}
What we are interested in, is to identify each TCP/IP interface in this list. We can identify them by correlating the identifier (or key) between the curly brackets {} to an IP address. We find all relevant information in the following registry key:
SYSTEMCurrentControlSetServicesTcpipParametersInterfaces
It contains subkeys named after the identifiers of the different TCP/IP interfaces, with a multi-string value IPAddress which contains the IP addresses set on this interface.
So let’s read the second collection of registry keys and values first, and create a collection of objects representing the interfaces, having properties representing the identifier and the IP address:
$registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $serverName) # Open remote HKLM key
$baseKey2 = $registry.OpenSubKey(“SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces”)
$myCol = @()
$NICS = $baseKey2.GetSubKeyNames() # Set of interfaces
ForEach ($NIC in $NICS)
{
$myObj = “” | Select-Object Key, IP # Create object for relation between identifier and IP address
$key = $baseKey2.OpenSubKey(“$NIC”)
$myObj.Key = $key.name.split(“{“)[1].trimend(“}”) # Get identifier of interface without brackets
$myObj.IP = $key.GetValue(“IPAddress”) # Get IP address of interface
$myCol += $myObj # Create collection of interface objects
}
Nice. Now we have the $myCol variable holding a collection of objects like so:
PS D:Scripts> $myCol
Key IP
— –
1B68A2DD-B093-4E12-A347-CED975434F12 {0.0.0.0}
313D4DA6-B0A6-41B6-B34D-97C89A3E0D65 {10.2.0.112}
A0344E82-4E65-47A2-92FF-B3183A181473 {10.1.0.112}
C34D5A39-DA8D-4D58-82D4-099098D5501C {0.0.0.0}
Now let’s read the binding order:
$baseKey1 = $registry.OpenSubKey(“SYSTEM\CurrentControlSet\Services\Tcpip\linkage”)
$bindorder = $baseKey1.GetValue(“Bind”)
That’s easy! Now to match the correct IP address to the second object in the $bindorder array:
$wantedboundkey = $bindorder[$index].split(“{“)[1].trimend(“}”) # Get identifier of …th TCP/IP interface in binding order
$wantedbound = $myCol | Where {$_.Key -eq $firstboundkey} # Find matching interface in collection
$wantedbound.IP # Return matching IP address
And there you have it!
PS D:Scripts>$wantedbound.IP
10.2.0.112
Below is the entire script that queries Active Directory for computers and runs the above script (defined as a function).
You should be able to figure out how it works.
Enjoy!
$outputFile = “D:Scriptsoutput.txt” # Default output path
function Get-BoundIP
{
param([string]$serverName, [Int]$index)
$registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $serverName) # Open remote HKLM key
If ($registry)
{
$baseKey1 = $registry.OpenSubKey(“SYSTEM\CurrentControlSet\Services\Tcpip\linkage”)
$bindorder = $baseKey1.GetValue(“Bind”)
$baseKey2 = $registry.OpenSubKey(“SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces”)
$myCol = @()
$NICS = $baseKey2.GetSubKeyNames() # Set of interfaces
ForEach ($NIC in $NICS)
{
$myObj = “” | Select-Object Key, IP # Create object for relation between identifier and IP address
$key = $baseKey2.OpenSubKey(“$NIC”)
$myObj.Key = $key.name.split(“{“)[1].trimend(“}”) # Get identifier of interface
$myObj.IP = $key.GetValue(“IPAddress”) # Get IP address of interface
$myCol += $myObj # Create collection of interface objects
}
$wantedboundkey = $bindorder[$index].split(“{“)[1].trimend(“}”) # Get identifier of …th interface in binding order
$wantedbound = $myCol | Where {$_.Key -eq $wantedboundkey} # Find matching interface in collection
$wantedbound.IP # Return matching IP address
}
}
Write-Host “Grabbing server names …”
$servers = Get-QADComputer -SizeLimit 0 -SearchRoot “OU=Servers,DC=example,DC=com” | Sort-Object Name # Get all servers from AD
$myCollection = @()
ForEach ($server in $servers)
{
Write-Host “Processing server” $server.name
$myObject = “” | Select-Object Name, FirstIP, SecondIP # Create object for output organization
$myObject.Name = $server.name # Set server name
$myObject.FirstIP = Get-BoundIP $server.name 0 # Run function to get first bound IP address
$myObject.SecondIP = Get-BoundIP $server.name 1
$myCollection += $myObject # Build collection of objects
}
$output = $myCollection | Format-Table -AutoSize # Format output
# Create output
If ((Test-Path $outputFile) -eq $False) # If the output file does not exist
{
Write-Host “Creating output file …”
$creatingOutputFile = New-Item $outputFile -ItemType File # Create output file
}
Write-Host “Writing output to file …”
$output | Out-File -FilePath $outputFile -Force # Output file is overwritten with new output
Write-Host “Launching output file ($outputFile) …”
Invoke-Item $outputFile