So here’s the scenario you need or want to figure out which LUN is which on your cluster… 

Well if you just use the Get-ClusterSharedVolume cmdlet it won’t get you very far…  However there’s another cmdlet Get-ClusterParameter that will give you enough data that when combined with some other PowerShell/.Net magic will.  This function will return the Cluster Object Private Properties for a given object you can look on MSDN to see all of them – in this case we are interested specifically in the Physical Disk Private Properties.

For this first example I am going to utilize the DiskUniqueIds property to extract information about the specific LUN.  Looking at the documentation for this property it returns a STORAGE_DEVICE_ID_DESCRIPTOR structure which has a set of identifiers – after some more digging around it turns out these identifiers are actually STORAGE_IDENTIFIER structures which have some interesting values.  For most of the storage I have in the lab at the very least the vendor ID (StorageIdTypeEUI64) which is actually a bit mask of the IEEE vendor ID along with some vendor specific data (the bit map is documented here and you can search for vendor ID’s here) as well as a VendorID string and some LUN information although some will tell you what port is being used etc…

In order to format all of this data I’m using some inline C# to define a class with the reprehensive structures and enumerations – just copied from MSDN and made pretty for .Net interop.  I also wrote a function in the C# class named DiskUniqueIdstoStorageId, this function takes the byte array returned from calling the Get-ClusterParameter cmdlet and marshel’s it or formats it into the C# classes thus allowing me to consume the now managed object’s in C#.  All in all it might be overkill so look for a shorter version coming soon.

Script

$StorageDevice= @"
using System;
using System.Runtime.InteropServices;

public static class StorageDevice
{
    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
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct STORAGE_IDENTIFIER
    {
        public StorageDevice.StorageIdCodeSet CodeSet;
        public StorageDevice.StorageIdType Type;
        public UInt16 IdentifierSize;
        public UInt16 NextOffset;
        public StorageDevice.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;
    }


    public static STORAGE_IDENTIFIER[] DiskUniqueIdstoStorageId(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));

        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;
    }
}
"@
add-type -TypeDefinition $StorageDevice

Import-Module FailoverClusters
$AllCSVLuns = Get-ClusterSharedVolume
foreach ($CSV in $AllCSVLuns)
{
   
$CSVParams = Get-ClusterParameter -InputObject $CSV
   
$DiskUniqueId = ($CSVParams | Where-object -FilterScript {$_.Name -eq "DiskUniqueIds"}).Value
   
$StorageId = [StorageDevice]::DiskUniqueIdstoStorageId($DiskUniqueId)
   
Write-Host "CSV ClusterResourceName: " $CSV.Name
   
Write-Host "Identifiers: "
   
$StorageId
   
Write-Host
   
Write-Host
}

 

Sample Output

Windows PowerShell
Copyright (C) 2009 Microsoft Corporation. All rights reserved.

PS C:\Users\taylorb>.\GetCSVDevInfo1.ps1

CSV ClusterResourceName:  Cluster_CSV1_IBMXIV
Identifiers:
CodeSet        : Binary
Type           : EUI64
IdentifierSize : 8
NextOffset     : 28
Association    : Device
Identifiers    :  0 17 38 0 1 13 0 1C

CodeSet        : Ascii
Type           : VendorId
IdentifierSize : 36
NextOffset     : 56
Association    : Device
Identifiers    : IBM     2810XIV          1300113001C

CodeSet        : Ascii
Type           : VendorSpecific
IdentifierSize : 13
NextOffset     : 32
Association    : Device
Identifiers    : vol=CSV_1

CodeSet        : Ascii
Type           : VendorSpecific
IdentifierSize : 22
NextOffset     : 40
Association    : Device
Identifiers    : host=37-4611K2713K

CodeSet        : Binary
Type           : TargetPortGroup
IdentifierSize : 4
NextOffset     : 24
Association    : Port
Identifiers    :  0 0 0 0

CodeSet        : Binary
Type           : PortRelative
IdentifierSize : 4
NextOffset     : 24
Association    : Port
Identifiers    :  0 0 7 1

CSV ClusterResourceName:  Cluster_CSV2_IBMXIV
Identifiers:
CodeSet        : Binary
Type           : EUI64
IdentifierSize : 8
NextOffset     : 28
Association    : Device
Identifiers    :  0 17 38 0 1 13 0 1D

CodeSet        : Ascii
Type           : VendorId
IdentifierSize : 36
NextOffset     : 56
Association    : Device
Identifiers    : IBM     2810XIV          1300113001D

CodeSet        : Ascii
Type           : VendorSpecific
IdentifierSize : 13
NextOffset     : 32
Association    : Device
Identifiers    : vol=CSV_2

CodeSet        : Ascii
Type           : VendorSpecific
IdentifierSize : 22
NextOffset     : 40
Association    : Device
Identifiers    : host=37-4611K2713F

CodeSet        : Binary
Type           : TargetPortGroup
IdentifierSize : 4
NextOffset     : 24
Association    : Port
Identifiers    :  0 0 0 0

CodeSet        : Binary
Type           : PortRelative
IdentifierSize : 4
NextOffset     : 24
Association    : Port
Identifiers    :  0 0 7 3

Taylor Brown
Hyper-V Enterprise Deployment Team
taylorb@microsoft.com
http://blogs.msdn.com/taylorb

WS08R2-HyperV_v_rgb