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