Windows Disk LUN WWID
by dwarfsoft on Aug.28, 2014, under Scripting, Storage, Tweet, Work
One of my main issues with attempting to identify storage on Windows Systems is that the Bus/LUN Id can be stored in multiple places, and trying to track that back to a Volume is pretty hard. So after doing some fairly deep investigation into DeviceIOControl calls to query Page 80 (Device information such as Vendor and type) and Page 83 (which returns the WWID object) I have managed to wrangle the C# into a Type usable from a PowerShell Script.
What is a WWID?
NAA
|
Name
|
Use
|
Example
|
1
|
IEEE 803.2 standard 48 bit ID
|
Standard WWN Addressing
|
10:00:00:00:c9:22:fc:01 |
2
|
IEEE 803.2 extended 48-bit ID
|
Allows some Vendor defined sections to allow better identification of Ports or extend the serial number
|
20:00:00:e0:8b:05:05:04 |
5
|
IEEE Registered Name
|
WWN FC address for a named device. Allows vendors to create unique Identifiers without having to maintain a database of Serial Number codes.
|
50:06:04:81:D6:F3:45:42 |
6
|
IEEE Extended Registered Name
|
Identifying objects such as disks
|
60:06:0e:80:05:34:2a:00:00:00:34:2a:00:00:10:00 |
More information about NAA 1, 2 and 5 can be obtained from http://howto.techworld.com/storage/156/how-to-interpret-worldwide-names/, which was my first point of call when starting my deep dive into WWIDs. http://www.computerworld.com.au/article/54436/how_interpret_san_worldwide_names/ also provides much the same information, and http://storagemeat.blogspot.com.au/2012/08/decoding-wwids-or-how-to-tell-whats-what.html talks more directly about NAA 6 WWIDs and how to decode information from them for HDS arrays.
Scripts for dealing with Disk WWID (NAA=6)
Function SAN-DefineStorageDevice() { return @' // [StorageDevice.Program]:: using Microsoft.Win32.SafeHandles; using System; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace StorageDevice { public class Program { // For CreateFile to get handle to drive private const uint FILE_SHARE_READ = 0x00000001; private const uint FILE_SHARE_WRITE = 0x00000002; private const uint OPEN_EXISTING = 3; private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080; private const Int32 IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400; private const Int32 PropertyStandardQuery = 0; private const Int32 StorageDeviceProperty = 0; private const Int32 StorageAdapterProperty = 1; private const Int32 StorageDeviceIdProperty = 2; private const Int32 StorageDeviceUniqueIdProperty = 3; public enum StorageIdCodeSet { Reserved = 0, Binary = 1, Ascii = 2, Utf8 = 3 } public enum StorageIdType { VendorSpecific = 0, VendorId = 1, EUI64 = 2, FCPHName = 3, PortRelative = 4, TargetPortGroup = 5, LogicalUnitGroup = 6, MD5LogicalUnitIdentifier = 7, ScsiNameString = 8 } public enum StorageIdAssoc { Device = 0, Port = 1, Target = 2 } // CreateFile to get handle to drive [DllImport("kernel32.dll", SetLastError = true)] private static extern SafeFileHandle CreateFileW( [MarshalAs(UnmanagedType.LPWStr)] string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile); // For control codes private const uint IOCTL_VOLUME_BASE = 0x00000056; private const uint METHOD_BUFFERED = 0; private const uint FILE_ANY_ACCESS = 0; private static uint CTL_CODE(uint DeviceType, uint Function, uint Method, uint Access) { return ((DeviceType << 16) | (Access << 14) | (Function << 2) | Method); } [StructLayout(LayoutKind.Sequential)] private struct STORAGE_PROPERTY_QUERY { public Int32 PropertyId; public Int32 QueryType; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public byte[] AdditionalParameters; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct STORAGE_IDENTIFIER { public StorageDevice.Program.StorageIdCodeSet CodeSet; public StorageDevice.Program.StorageIdType Type; public UInt16 IdentifierSize; public UInt16 NextOffset; public StorageDevice.Program.StorageIdAssoc Association; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)] public string Identifiers; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct STORAGE_DEVICE_ID_DESCRIPTOR { public UInt32 Version; public UInt32 Size; public UInt32 NumberOfIdentifiers; //[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)] //public String Identifiers; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10240)] public System.Byte[] Identifiers; //public UChar Identifiers; } [StructLayout(LayoutKind.Sequential)] public struct STORAGE_DEVICE_DESCRIPTOR { public Int32 Version; public Int32 Size; public System.Byte DeviceType; public System.Byte DeviceTypeModifier; public System.Byte RemovableMedia; public System.Byte CommandQueueing; public Int32 VendorIdOffset; public Int32 ProductIdOffset; public Int32 ProductRevisionOffset; public Int32 SerialNumberOffset; public System.Byte BusType; public Int32 RawPropertiesLength; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10240)] public System.Byte[] RawDeviceProperties; } // For DeviceIoControl to get disk extents [StructLayout(LayoutKind.Sequential)] private struct DISK_EXTENT { public uint DiskNumber; public long StartingOffset; public long ExtentLength; } [StructLayout(LayoutKind.Sequential)] private struct VOLUME_DISK_EXTENTS { public uint NumberOfDiskExtents; [MarshalAs(UnmanagedType.ByValArray)] public DISK_EXTENT[] Extents; } // DeviceIoControl to get disk extents [DllImport("kernel32.dll", EntryPoint = "DeviceIoControl", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool DeviceIoControl( SafeFileHandle hDevice, uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize, ref VOLUME_DISK_EXTENTS lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped); // DeviceIoControl to get disk extents [DllImport("kernel32.dll", EntryPoint = "DeviceIoControl", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool DeviceIoControl( SafeFileHandle hDevice, uint dwIoControlCode, ref STORAGE_PROPERTY_QUERY lpInBuffer, uint nInBufferSize, ref STORAGE_DEVICE_DESCRIPTOR lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped); [DllImport("kernel32.dll", EntryPoint = "DeviceIoControl", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool DeviceIoControl( SafeFileHandle hDevice, uint dwIoControlCode, ref STORAGE_PROPERTY_QUERY lpInBuffer, uint nInBufferSize, ref STORAGE_DEVICE_ID_DESCRIPTOR lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped); [DllImport("kernel32.dll", EntryPoint = "DeviceIoControl", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool DeviceIoControl( SafeFileHandle hDevice, uint dwIoControlCode, ref STORAGE_PROPERTY_QUERY lpInBuffer, uint nInBufferSize, //[MarshalAs(UnmanagedType.LPArray, SizeConst = 10240)]byte[] lpOutBuffer, IntPtr lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped); // For error message private const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; [DllImport("kernel32.dll", SetLastError = true)] static extern uint FormatMessage( uint dwFlags, IntPtr lpSource, uint dwMessageId, uint dwLanguageId, StringBuilder lpBuffer, uint nSize, IntPtr Arguments); public static void Main(string[] args) { Console.WriteLine("Input logical drive letter:"); char cDrive = Console.ReadLine()[0]; if (char.IsLetter(cDrive)) { GetDiskExtents(cDrive); } Console.ReadLine(); } // Method for disk extents private static void GetDiskExtents(char cDrive) { DriveInfo di = new DriveInfo(cDrive.ToString()); if (di.DriveType != DriveType.Fixed) { Console.WriteLine("This drive is not fixed drive."); } string sDrive = "\\\\.\\" + cDrive.ToString() + ":"; SafeFileHandle hDrive = CreateFileW( sDrive, 0, // No access to drive FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero); if (hDrive == null || hDrive.IsInvalid) { string message = GetErrorMessage(Marshal.GetLastWin32Error()); Console.WriteLine("CreateFile failed. " + message); } uint IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS = CTL_CODE( IOCTL_VOLUME_BASE, 0, METHOD_BUFFERED, FILE_ANY_ACCESS); // From winioctl.h VOLUME_DISK_EXTENTS query_disk_extents = new VOLUME_DISK_EXTENTS(); uint returned_query_disk_extents_size; bool query_disk_extents_result = DeviceIoControl( hDrive, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, IntPtr.Zero, 0, ref query_disk_extents, (uint)Marshal.SizeOf(query_disk_extents), out returned_query_disk_extents_size, IntPtr.Zero); hDrive.Close(); if (query_disk_extents_result == false || query_disk_extents.Extents.Length != 1) { string message = GetErrorMessage(Marshal.GetLastWin32Error()); Console.WriteLine("DeviceIoControl failed. " + message); } else { Console.WriteLine("The physical drive number is: " + query_disk_extents.Extents[0].DiskNumber); } } // Method for error message private static string GetErrorMessage(int code) { StringBuilder message = new StringBuilder(255); FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, (uint)code, 0, message, (uint)message.Capacity, IntPtr.Zero); return message.ToString(); } public static string GetStorageWWID(string sDrive) { STORAGE_DEVICE_DESCRIPTOR sdd = QueryPage80(sDrive); if (sdd.Size == 0) { return null; } byte[] bySDI = QueryPage83(sDrive); STORAGE_DEVICE_ID_DESCRIPTOR devId = DiskUniqueIdstoStorageDeviceIdDescriptor(bySDI); if (devId.NumberOfIdentifiers > 0) { STORAGE_IDENTIFIER[] sdi = DiskUniqueIdstoStorageId(bySDI); for (int i = 0; i<devId.NumberOfIdentifiers;++i) { if (sdi[i].Type == StorageDevice.Program.StorageIdType.FCPHName) { string rs = ""; string[] parts = sdi[i].Identifiers.Trim().Split(' '); foreach (string s in parts) { UInt32 u = Convert.ToUInt32(s,16); if (rs != "") rs += ":"; rs += u.ToString("x2"); } return rs; } } } return ""; } public static STORAGE_IDENTIFIER[] GetStorageIdentifiers(string sDrive) { STORAGE_DEVICE_DESCRIPTOR sdd = QueryPage80(sDrive); if (sdd.Size == 0) { return null; } byte[] bySDI = QueryPage83(sDrive); STORAGE_DEVICE_ID_DESCRIPTOR devId = DiskUniqueIdstoStorageDeviceIdDescriptor(bySDI); //Console.WriteLine("Not Null"); if (devId.NumberOfIdentifiers > 0) { STORAGE_IDENTIFIER[] sdi = DiskUniqueIdstoStorageId(bySDI); return sdi; } return null; } // Method for Query Page 83 public static byte[] QueryPage83(string sDrive) { //unsafe { // The input parameter STORAGE_PROPERTY_QUERY spq = new STORAGE_PROPERTY_QUERY(); spq.PropertyId = StorageDeviceIdProperty; spq.QueryType = PropertyStandardQuery; //uint iBytesReturned = 0; // Prepare a large output buffer (good enough in our sample code) //bOutputBuffer; //STORAGE_DEVICE_DESCRIPTOR sdd = new STORAGE_DEVICE_DESCRIPTOR(); byte[] sdi;// = new byte[10240]; uint returned_query_disk_page83_size; //uint returnedLength; //string sDrive = "\\\\.\\PhysicalDrive1"; SafeFileHandle hDrive = CreateFileW( sDrive, 0, // No access to drive FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero); IntPtr result = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(byte))*10240); bool query_disk_page83_result = DeviceIoControl( hDrive, IOCTL_STORAGE_QUERY_PROPERTY, ref spq, (uint)Marshal.SizeOf(spq), (result), (uint)10240, out returned_query_disk_page83_size, IntPtr.Zero); if (query_disk_page83_result) { //Console.WriteLine ("83 Size: {0}",returned_query_disk_page83_size); sdi = new byte[returned_query_disk_page83_size]; Marshal.Copy(result,sdi,0,(Marshal.SizeOf(typeof(byte))*(int)returned_query_disk_page83_size)); Marshal.FreeHGlobal(result); return sdi; } else { Marshal.FreeHGlobal(result); return null; } //} } // Method for Query Page 80 public static STORAGE_DEVICE_DESCRIPTOR QueryPage80(string sDrive) { // The input parameter STORAGE_PROPERTY_QUERY spq = new STORAGE_PROPERTY_QUERY(); spq.PropertyId = StorageDeviceProperty; spq.QueryType = PropertyStandardQuery; //uint iBytesReturned = 0; // Prepare a large output buffer (good enough in our sample code) //bOutputBuffer; STORAGE_DEVICE_DESCRIPTOR sdd = new STORAGE_DEVICE_DESCRIPTOR(); //STORAGE_DEVICE_ID_DESCRIPTOR sdi = new STORAGE_DEVICE_ID_DESCRIPTOR(); //uint returnedLength; //string sDrive = "\\\\.\\PhysicalDrive1"; SafeFileHandle hDrive = CreateFileW( sDrive, 0, // No access to drive FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero); uint returned_query_disk_page80_size,returned_query_disk_page83_size; bool query_disk_page80_result = DeviceIoControl( hDrive, IOCTL_STORAGE_QUERY_PROPERTY, ref spq, (uint)Marshal.SizeOf(spq), ref sdd, (uint)Marshal.SizeOf(sdd), out returned_query_disk_page80_size, IntPtr.Zero); //Console.WriteLine ( returned_query_disk_page80_size); if (query_disk_page80_result) { unsafe { //byte[] b = sdd.RawDeviceProperties; //Byte* b = &(sdd.RawDeviceProperties); string s = System.Text.Encoding.Default.GetString(sdd.RawDeviceProperties); return sdd; //char*p = &(sdd.RawDeviceProperties); //STORAGE_DEVICE_DESCRIPTOR* p = &sdd; if (sdd.VendorIdOffset != 0) //Console.WriteLine("- Page80.VendorId: {0}\n", *(p+sdd.VendorIdOffset)); //Console.WriteLine("- Page80.VendorId: {0}\n",(string)sdd.RawDeviceProperties); Console.WriteLine("- Page80.VendorId: {0}\n",s); Console.Write("\""); int row = 0; Console.Write("{0:d3}: \"",row); for (int i = 0; i < (sdd.VendorIdOffset+80); i++) { if (sdd.RawDeviceProperties[i] == 0) { ++row; Console.Write("\"\n"); Console.Write("{0:d3}: \"",row); } else { Console.Write("{0}",(char)sdd.RawDeviceProperties[i]); } } Console.Write("\"\n"); } } return sdd; } public static STORAGE_DEVICE_ID_DESCRIPTOR DiskUniqueIdstoStorageDeviceIdDescriptor(byte[] DiskUniqueIds) { IntPtr ArrayAddress = Marshal.UnsafeAddrOfPinnedArrayElement(DiskUniqueIds, 0); STORAGE_DEVICE_ID_DESCRIPTOR devId = (STORAGE_DEVICE_ID_DESCRIPTOR)Marshal.PtrToStructure( ArrayAddress, typeof(STORAGE_DEVICE_ID_DESCRIPTOR)); return devId; } public static STORAGE_IDENTIFIER[] DiskUniqueIdstoStorageId(byte[] DiskUniqueIds) { STORAGE_DEVICE_ID_DESCRIPTOR devId = DiskUniqueIdstoStorageDeviceIdDescriptor(DiskUniqueIds); IntPtr ArrayAddress = Marshal.UnsafeAddrOfPinnedArrayElement(DiskUniqueIds, 0); IntPtr AddressOfIdentifiers = (IntPtr) ((long)ArrayAddress + (long)(Marshal.OffsetOf( typeof(STORAGE_DEVICE_ID_DESCRIPTOR), "Identifiers"))); IntPtr OffsetOfIdentifiersStrings = Marshal.OffsetOf( typeof(STORAGE_IDENTIFIER), "Identifiers"); STORAGE_IDENTIFIER[] ArrayofSTORAGE_IDENTIFIER = new STORAGE_IDENTIFIER[devId.NumberOfIdentifiers]; IntPtr CurrentAddressOfIdentifiers=AddressOfIdentifiers; for (int i = 0; i < devId.NumberOfIdentifiers; i++) { ArrayofSTORAGE_IDENTIFIER[i] = (STORAGE_IDENTIFIER)Marshal.PtrToStructure( CurrentAddressOfIdentifiers, typeof(STORAGE_IDENTIFIER)); IntPtr AddressOfIdentifiersString = (IntPtr)((long)CurrentAddressOfIdentifiers + (long)OffsetOfIdentifiersStrings); switch (ArrayofSTORAGE_IDENTIFIER[i].CodeSet) { case StorageIdCodeSet.Ascii: ArrayofSTORAGE_IDENTIFIER[i].Identifiers = Marshal.PtrToStringAnsi(AddressOfIdentifiersString, (Int32)ArrayofSTORAGE_IDENTIFIER[i].IdentifierSize); break; case StorageIdCodeSet.Utf8: ArrayofSTORAGE_IDENTIFIER[i].Identifiers = Marshal.PtrToStringUni(AddressOfIdentifiersString, (Int32)ArrayofSTORAGE_IDENTIFIER[i].IdentifierSize); break; default: for (int x=0; x < ArrayofSTORAGE_IDENTIFIER[i].IdentifierSize; x++) { ArrayofSTORAGE_IDENTIFIER[i].Identifiers = String.Format("{0} {1:X}", ArrayofSTORAGE_IDENTIFIER[i].Identifiers, Marshal.ReadByte(AddressOfIdentifiersString, x)); } break; } CurrentAddressOfIdentifiers = (IntPtr) ((long)CurrentAddressOfIdentifiers + (long)ArrayofSTORAGE_IDENTIFIER[i].NextOffset); } return ArrayofSTORAGE_IDENTIFIER; } public static string GetWWIDOfStorageIdentifiers(STORAGE_IDENTIFIER[] sdi) { return ""; } } } '@ } # Return a list of WWIDs for each Physical Disk Function SAN-GetDiskWWIDs { [CmdletBinding()] Param ( ) $cp = new-object codedom.compiler.compilerparameters $cp.CompilerOptions = "/unsafe" $cp.WarningLevel = 0 Add-Type -TypeDefinition (SAN-DefineStorageDevice) -CompilerParameters $cp #-IgnoreWarnings $dds = gwmi win32_diskdrive|Sort DeviceId $list = @() foreach ($d in $dds) { $obj = ""|Select Disk,WWID $obj.Disk = $d.DeviceId $wwid = "" Write-Debug $d.DeviceId try { $wwid = [StorageDevice.Program]::GetStorageWWID($d.DeviceId); } catch { # do nothing as $wwid is already "" } if ($wwid -ne "0") { $obj.WWID = $wwid } $list += $obj } return $list } Function SAN-GetVolumeWWID { [CmdletBinding()] Param ( $Volume = "\\.\C:" ) if ($Volume[-1] -eq "\") { # Remove trailing \ as that breaks the StorageDevice Query $Volume = $Volume.Substring(0,$Volume.Length-1) } if ($Volume -match ".:[\\]?") { $Volume = "\\.\$Volume" } if ($Volume -notlike "\\?\*") { return $null } $obj = ""|Select Volume,WWID $obj.Volume = $Volume $wwid = "" Write-Debug $Volume try { $wwid = [StorageDevice.Program]::GetStorageWWID($Volume); } catch { # do nothing as $wwid is already "" } if ($wwid -ne "0") { $obj.WWID = $wwid } return $obj } |
Outputs
The output that can be expected from these functions are as follows.
SAN-GetVolumeWWID
Get a list of Volumes to work with
PS W:\> GWMI Win32_Volume |Select Name Name ---- \\?\Volume{a0312498-c120-11e3-b9e6-806e6f6e6963}\ E:\ \\?\Volume{daa5adfe-c53b-11e3-b8b4-001b21ac3d80}\ \\?\Volume{daa5ae01-c53b-11e3-b8b4-001b21ac3d80}\ \\?\Volume{daa5ae04-c53b-11e3-b8b4-001b21ac3d80}\ \\?\Volume{daa5ae07-c53b-11e3-b8b4-001b21ac3d80}\ C:\ D:\ F:\ G:\ Z:\ Y:\
Get WWID Information for some of these Volumes
PS W:\> "C:","D:","E:","F:","G:"|%{ SAN-GetVolumeWWID $_ } Volume WWID ------ ---- \\.\C: 67:82:bc:b0:27:47:1d:00:15:72:d9:2f:06:54:e5:aa \\.\D: 67:82:bc:b0:27:47:1d:00:15:72:d9:2f:06:54:e5:aa \\.\E: 67:82:bc:b0:27:47:1d:00:1a:df:d8:3c:5d:50:da:53 \\.\F: \\.\G:
The C: and D: drives are Volumes created on two different partitions on the same Physical Disk. The E: drive is on a separate Physical Disk. The WWIDs reflect this.
Query the WWID of one of the \\?\Volume* devices
PS W:\> GWMI Win32_Volume |Select -Expand Name|?{$_ -like "*Volume*"}| %{ SAN-GetVolumeWWID $_ } Volume WWID ------ ---- \\?\Volume{a0312498-c120-11e3-b9e6-806e6f6e6963} 67:82:bc:b0:27:47:1d:00:15:72:d9:2f:06:54:e5:aa \\?\Volume{daa5adfe-c53b-11e3-b8b4-001b21ac3d80} 60:06:0e:80:05:34:2a:00:00:00:34:2a:00:00:10:00 \\?\Volume{daa5ae01-c53b-11e3-b8b4-001b21ac3d80} 60:06:0e:80:05:34:2a:00:00:00:34:2a:00:00:10:d0 \\?\Volume{daa5ae04-c53b-11e3-b8b4-001b21ac3d80} 60:06:0e:80:05:5f:a3:00:00:00:5f:a3:00:00:10:ef \\?\Volume{daa5ae07-c53b-11e3-b8b4-001b21ac3d80} 60:06:0e:80:05:5f:a3:00:00:00:5f:a3:00:00:11:00
Query more information about a WWID
PS W:\> SAN-GetVolumeWWID "\\?\Volume{daa5adfe-c53b-11e3-b8b4-001b21ac3d80}\"|%{ SAN-WhatsThatWWID -wwid $_.WWID } Format : 6 WWID : 60:06:0e:80:05:34:2a:00:00:00:34:2a:00:00:10:00 CleanWWID : {6, 0, 0, 6...} OUI : 0060E8 Vendor : HDS Model : USP-V Serial : 13354 Type : Enterprise Family : USP-V LDEV : 00:10:00
Here I have run the Volume through another of my scripts to determine some information about the WWID that was returned. It gives me a surprising amount of information about the disk including the Serial Number of the Array and the LDEV ID. This is capable for HDS storage, whereas EMC storage has obfuscated the Array ID through some form of Hashing algorithm.
SAN-GetDiskWWIDs
PS W:\> SAN-GetDiskWWIDs Disk WWID ---- ---- \\.\PHYSICALDRIVE0 67:82:bc:b0:27:47:1d:00:15:72:d9:2f:06:54:e5:aa \\.\PHYSICALDRIVE1 67:82:bc:b0:27:47:1d:00:1a:df:d8:3c:5d:50:da:53 \\.\PHYSICALDRIVE2 60:06:0e:80:05:34:2a:00:00:00:34:2a:00:00:10:00 \\.\PHYSICALDRIVE3 60:06:0e:80:05:34:2a:00:00:00:34:2a:00:00:10:d0 \\.\PHYSICALDRIVE4 60:06:0e:80:05:5f:a3:00:00:00:5f:a3:00:00:10:ef \\.\PHYSICALDRIVE5 60:06:0e:80:05:5f:a3:00:00:00:5f:a3:00:00:11:00 \\.\PHYSICALDRIVE6 \\.\PHYSICALDRIVE7
Not all drives return WWIDs. Disk 6 and 7 are the Dell DRAC Virtual Floppy and Virtual CD devices. They are unmounted which may be causing the lack of WWID, but WWID of local disk is not really relevant to this discussion.
I hope this has been somewhat informative, and useful. Now it is far more easy to identify which specific disk will need to be removed or extended on a Windows Host the same way that a disk can be identified on Solaris/Linux/AIX/ESXi for these purposes.
Cheers, Chris.
Really interesting script.
Does it run to a remote server like gwmi -class “win32_volume” -computername “Server”, or does it need to be copied locally to remote server ?
Really interesting script
Could it be run on remote server like gmwi -class “” -computername “”, or does it need to be copied locally onto remote server, or with invoke-command ?
Look’s like it doesn’t work for volume mount point.