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