VBScript to uniquely assign MAC addresses across servers
A while ago I was asked if I had a script that could assign a unique static MAC address to a virtual machine. I did not - and thought I would write one - which turned out to be a lot more effort than I thought!
To give some background here - Virtual Server allows you to configure a virtual machine with a dynamically generated MAC address or a statically configured MAC address. The problem with dynamically generated MAC addresses is that there is no guarantee of uniqueness across multiple servers. In these cases it is useful to use static MAC addresses - but then you have to manage the MAC addresses yourself.
This script will:
- Connect to multiple instances of Virtual Server
- Enumerate all the used MAC addresses for all configured virtual machines
- Iterate through the specified MAC address range looking for a unique MAC address
LowerMAC and UpperMAC specify the start and end of the range of MAC addresses to use. gatherMACs takes a parameter of a VirtualServer com object and then enumerates all used MAC addresses - adding them to the MACArray array. getNewMac returns a unique MAC address (after checking it against MACArray).
Option Explicit
dim localVS, remoteVS1, MACArray, LowerMAC, UpperMAC, vm, vmNic
'Specify MAC address range to use
LowerMAC = "10-03-FF-00-00-00"
UpperMAC = "10-03-FF-FF-FF-FF"
'Connect to Virtual Servers
Set localVS = CreateObject("VirtualServer.Application")
Set remoteVS1 = CreateObject("VirtualServer.Application", remoteServerName)
'Connect to the specified virtual machine on the local server
set vm = localVS.FindVirtualMachine(WScript.Arguments.Item(0))
'Go through each network adapter on the virtual machine
for each vmNic in vm.NetworkAdapters
'Clear information about known MAC addresses
MACArray=Array()
'Generate new MAC known MAC address list
gatherMACs(localVS)
gatherMACs(remoteVS1)
'Turn off dynamic MAC addresses and set a new static address
vmNic.IsEthernetAddressDynamic = False
vmNic.EthernetAddress = getNewMAC
next
'Create list of used MAC addresses on a given server
sub gatherMACs(vs)
dim vm, vnic
for each vm in vs.VirtualMachines
for each vnic in vm.NetworkAdapters
'Grow the Array by one slot for the new MAC address
redim preserve MACArray(UBound(MACArray) + 1)
'Convert the MAC address to a Double decimal number
MACArray(UBound(MACArray)) = CDbl("&H" & Replace(vnic.EthernetAddress, "-", ""))
next
next
end sub
'Return new known unique MAC address
function getNewMAC()
dim newMacAddress, unique, complete, knownMAC
complete = false
'Start with the decimal version of the first MAC address in the range
newMacAddress = CDbl("&H" & Replace(LowerMAC, "-", ""))
while complete = false
'Assume that the new value is unique
unique = true
'Check the new value against the known MAC addresses
for each knownMAC in MACArray
if newMacAddress = knownMAC then
unique = false
end if
next
'Try the next number, or say that we are done.
if unique = false then
newMacAddress = newMacAddress +1
else complete = true
end if
'Check to make sure that we have not exceeded the upper limit of the range
if newMacAddress = CDbl("&H" & Replace(UpperMAC, "-", "")) then
wscript.echo "No unique MAC address could be generated"
wscript.quit
end if
wend
'Return the new value (once it has been VS formatted)
getNewMAC = VSMacHex(newMacAddress)
end function
'Takes a decimal Dbl and returns a Virtual Server formatted MAC address
Function VSMacHex(Number)
Dim tempHexValue
Const HexChars = "0123456789ABCDEF"
If Number = 0 Then
tempHexValue = "00-00-00-00-00-00"
Exit Function
End If
'Quick (large number friendly) decimal to hex conversion
While Number > 0
tempHexValue = Mid(HexChars, 1 + (Number - 16 * Fix(Number / 16)), 1) & tempHexValue
Number = Fix(Number/16)
WEnd
'Now to put the "-" in the right place and return a string
VSMacHex = Mid(tempHexValue,1,2) + "-" + Mid(tempHexValue,3,2) + _
"-" + Mid(tempHexValue,5,2) + "-" + Mid(tempHexValue,7,2) + _
"-" + Mid(tempHexValue,9,2) + "-" + Mid(tempHexValue,11,2)
End Function
There are a couple of things that should be noted about the difficulty of handling MAC addresses in VBScript. The first problem is that VBScript does not support Hexadecimal mathematics - so you need to convert any hex values to decimal values before operating on them. The next problem is that translating a MAC address from hex to decimal results in a value that is rather large - so it needs to be stored in a double (integer and long are too small). The third problem is that using the Hex() command to convert from decimal to hex is not possible - as it uses integers internally, and will overflow. The final problem is that Virtual Server puts hyphens in the MAC addresses. In order to handle all of this I use the following command to convert a Virtual Server MAC address (with hyphens) to a double:
CDbl("&H" & Replace(UpperMAC, "-", ""))
The replace command replaces all hyphens in the string with empty strings, and appending "&H" to the string allows CDbl to recognize the value as a hexadecimal number. Going from a double to a Virtual Server MAC address is trickier - and the VSMacHex() function exists to provide this functionality.
Cheers,
Ben