- Dwarfsoft [GPA] - http://www.dwarfsoft.com/blog -

Network Interface Removal and Renaming

So we had some Blade Chassis and implemented some Firmware upgrades. Surely this should not impact the blades themselves, or the OS on which they run? You’ve got to be kidding right. As soon as the Blade Chassis firmware has been upgraded we lost all four Broadcom NICs, and we had four new Broadcom NICs added in their place. This means that any IP Configuration has been lost and so has, in our case, Team configuration. Due to the four original NICs being removed, their Connection Names have also become unusable, because there are hidden devices that are still using those names.

Firstly, how do we remove devices via PowerShell. The Answer can be found on any Windows 7 workstation in the C:\Windows\Diagnostics\System\Device\CL_Utility.ps1. There is a function there called RemoveDevice.

function RemoveDevice([string]$DeviceID)
{
$RemoveDeviceSource = @"
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Microsoft.Windows.Diagnosis
{
    public sealed class DeviceManagement_Remove
    {
        public const UInt32 ERROR_CLASS_MISMATCH = 0xE0000203;
 
        [DllImport("setupapi.dll", SetLastError = true, EntryPoint = "SetupDiOpenDeviceInfo", CharSet = CharSet.Auto)]
        static extern UInt32 SetupDiOpenDeviceInfo(IntPtr DeviceInfoSet, [MarshalAs(UnmanagedType.LPWStr)]string DeviceID, IntPtr Parent, UInt32 Flags, ref SP_DEVINFO_DATA DeviceInfoData);
 
        [DllImport("setupapi.dll", SetLastError = true, EntryPoint = "SetupDiCreateDeviceInfoList", CharSet = CharSet.Unicode)]
        static extern IntPtr SetupDiCreateDeviceInfoList(IntPtr ClassGuid, IntPtr Parent);
 
        [DllImport("setupapi.dll", SetLastError = true, EntryPoint = "SetupDiDestroyDeviceInfoList", CharSet = CharSet.Unicode)]
        static extern UInt32 SetupDiDestroyDeviceInfoList(IntPtr DevInfo);
 
        [DllImport("setupapi.dll", SetLastError = true, EntryPoint = "SetupDiRemoveDevice", CharSet = CharSet.Auto)]
        public static extern int SetupDiRemoveDevice(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData);
 
        [StructLayout(LayoutKind.Sequential)]
        public struct SP_DEVINFO_DATA
        {
            public UInt32 Size;
            public Guid ClassGuid;
            public UInt32 DevInst;
            public IntPtr Reserved;
        }
 
        private DeviceManagement_Remove()
        {
        }
 
        public static UInt32 GetDeviceInformation(string DeviceID, ref IntPtr DevInfoSet, ref SP_DEVINFO_DATA DevInfo)
        {
            DevInfoSet = SetupDiCreateDeviceInfoList(IntPtr.Zero, IntPtr.Zero);
            if (DevInfoSet == IntPtr.Zero)
            {
                return (UInt32)Marshal.GetLastWin32Error();
            }
 
            DevInfo.Size = (UInt32)Marshal.SizeOf(DevInfo);
 
            if(0 == SetupDiOpenDeviceInfo(DevInfoSet, DeviceID, IntPtr.Zero, 0, ref DevInfo))
            {
                SetupDiDestroyDeviceInfoList(DevInfoSet);
                return ERROR_CLASS_MISMATCH;
            }
            return 0;
        }
 
        public static void ReleaseDeviceInfoSet(IntPtr DevInfoSet)
        {
            SetupDiDestroyDeviceInfoList(DevInfoSet);
        }
 
        public static UInt32 RemoveDevice(string DeviceID)
        {
            UInt32 ResultCode = 0;
            IntPtr DevInfoSet = IntPtr.Zero;
            SP_DEVINFO_DATA DevInfo = new SP_DEVINFO_DATA();
 
            ResultCode = GetDeviceInformation(DeviceID, ref DevInfoSet, ref DevInfo);
 
            if (0 == ResultCode)
            {
                if (1 != SetupDiRemoveDevice(DevInfoSet, ref DevInfo))
                {
                    ResultCode = (UInt32)Marshal.GetLastWin32Error();
                }
                ReleaseDeviceInfoSet(DevInfoSet);
            }
 
            return ResultCode;
        }
    }
}
"@
 
    Add-Type -TypeDefinition $RemoveDeviceSource
 
    $DeviceManager = [Microsoft.Windows.Diagnosis.DeviceManagement_Remove]
 
    $ErrorCode = $DeviceManager::RemoveDevice($DeviceID)
    return $ErrorCode
}

Looking into the function we can see we are using an imported DLL via C# from SetupAPI.dll. This seems to have everything we need to remove a device, but what is the DeviceID that we need to pass it, and how do we find out this value from Network Adapters on the System?

gwmi Win32_NetworkAdapter|Select NetConnectionId,PNPDeviceID

So here we can see what NetConnectionID (display name in the Network Adapters window in Control Panel) relates to what Plug-and-Play Device ID. This string is exactly what the RemoveDevice Function relies on to remove devices from the system. What you will notice though is that for devices that are no longer connected to the system, the MACAddress, NetConnectionId and PNPDeviceID are all blank. With a little digging, though, we can get the DeviceID.

gwmi Win32_NetworkAdapter¦?{($_.Name -like "Broad*") -and ($_.MACAddress -eq $null)}¦%{
  $index = ("0000"+$_.Index)
  $index = $index.Substring($index.Length-4)
  $nic = gp "HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\$index"
  $guid = $nic.NetCfgInstanceId
  $devid = $nic.DeviceInstanceId
  RemoveDevice($devid)
  $netcfg = gp "HKLM:\SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\$devguid\Connection"
  if ($netcfg.PnpInstanceID -eq $devid)
  {
    Remove-Item "HKLM:\SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\$devguid" -Recurse
  }
}

The first part here gets a list of NICs that have been removed from the system. Your criteria may vary, but this limits the interfaces I am looking for to be Broadcom devices with no MAC Address. For each of these we identify the Index of the NIC, and get the valid information out of the registry. HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0015, for example, is the registry key for an Interface with an Index of 15. The two values we extract from here is DeviceInstanceId, which is the string that we can use to remove the device. Additionally we extract the NetCfgInstanceId which is a GUID that describes the Network Interface. We can use this GUID $nics = gwmi Win32_NetworkAdapter|?{$_.Name -like “Broad*”}|Sort -Property MACAddress $nics[0].NetConnectionId = “Production_NIC1” $nics[1].NetConnectionId = “Heartbeat_NIC1” $nics[2].NetConnectionId = “Heartbeat_NIC2” $nics[3].NetConnectionId = “Production_NIC2” $nics|%{$_.Put()}

So our list of NICs is assumed to be four, feel free to add in any checks against that yourself before continuing. We have Sorted by MACAddress which means that the NICs are ordered the same as they are in the Blade Chassis Management Controller. We then name our NICs manually, and finish with running the Put() function on each NIC to set the values that we have changed. From here it becomes a matter of applying Team Configuration (or adding the appropriate NICs to the Appropriate Team) and ensuring that everything is functional.

I hope this has been of some benefit to others, as it took me some time to track this all down and test it successfully.

Cheers, Chris.