Lab Case

I need to expand an Azure Storage Account disk that is attached to a particular drive letter in the Azure VM (Windows Server). Before that can be done I need to know what disk the drive letter maps to in Azure.

Solution

There are steps that can be used to gather this data from the UI in Windows Server utilizing the Disk Management console, but PowerShell is much more efficient in this use case.

The output of the script below will look similar to the following:

AzureVMName    AzureDiskName             DriveLetter LUN PartitionName          PartitionSizeGB DiskIndex SCSILogicalUnit DiskSizeGB DiskStatus
-----------    -------------             ----------- --- -------------          --------------- --------- --------------- ---------- ----------
VM004DB        vm004db-datadisk1         E             0 Disk #2, Partition #0              128         2               0        128 OK
VM004DB        vm004db-datadisk2         F             1 Disk #3, Partition #0             4090         3               1       4090 OK
VM004DB        vm004db-datadisk3         G             2 Disk #4, Partition #0             4090         4               2       4090 OK
<#
    .SYNOPSIS
    Returns a table providing mapping of Azure Storage disk to Azure VM OS volume.

    .DESCRIPTION
    Script is written to be executed from the admin's local device (adjust to execute through Azure Function or Runbook as desired)
    Items/Dependencies:
    - Az CLI (quickest one-liner to get the data)
    - PowerShell (Windows PowerShell 5.1 or PowerShell 7+)
    - Network access to Azure VM via WinRM
    - Azure Account privileges to query VM data
    - Also assumes the VM name is also the hostname for WinRM access

    . EXAMPLES
    .\Get-AzDiskToVmDisk.ps1 -VMName VM004DB -ResourceGroup mystuff -Credential (Get-Secret myAdmin)

    Uses SecretManagement module to retrieve PSCredential for "myAdmin".
    Pulls the list of Storage disk assigned to VM "VM004DB", CIM calls to map those to the Disk and Volume in Windows Server

    .NOTES
    Original source: https://github.com/peterlil/script-and-templates/blob/master/az%20cli/iaas/storage/map-azure-disks-to-vm-disks-windows.ps1
#>
param(
    [string] $VMName,
    [string] $ResourceGroup,
    [PSCredential]$Credential
)

<# Create CIM Session to the provided VM using the credential #>
try {
    $cimSession = New-CimSession -ComputerName $VMName -Credential $Credential
} catch {
    throw "Unable to connect CIM to $VMName $($_)"
}

<# Grab the Disk name and LUN number for the VM in Azure #>
$azDisks = az vm show -n $VMName -g $ResourceGroup --query "storageProfile.dataDisks[].{Name:name, LUN:lun}" --output JSON | ConvertFrom-Json

<# Using the CIM Session retrieve disk, partition and volume details from Windows Server #>
$diskDrives = Get-CimInstance -Class Win32_DiskDrive -CimSession $cimSession | Sort-Object -Property DeviceID
$diskDriveToDiskPartitionMappings = Get-CimInstance -Class Win32_DiskDriveToDiskPartition -CimSession $cimSession
$diskPartitions = Get-CimInstance -Class Win32_DiskPartition -CimSession $cimSession
$logicalDiskToPartitionMappings = Get-CimInstance -Class Win32_LogicalDiskToPartition -CimSession $cimSession
$logicalDisks = Get-CimInstance -Class Win32_LogicalDisk -CimSession $cimSession

foreach ($diskDrive in $diskDrives) {
    if ($diskDrive.InterfaceType -eq 'SCSI') {
        # Get partition mappings for the current drive
        $diskDriveToDiskPartitionMapping = $diskDriveToDiskPartitionMappings | Where-Object { $_.Antecedent.DeviceID -eq $diskDrive.DeviceID }

        $azDiskName = ($azDisks | Where-Object { $_.LUN -eq $diskDrive.SCSILogicalUnit }).Name

        if($diskDriveToDiskPartitionMapping) {
            $diskDriveToDiskPartitionMapping | ForEach-Object {
                $partitionMapping = $_
                $diskPartition = $diskPartitions | Where-Object { $_.Name -eq $partitionMapping.Dependent.DeviceID }

                $logicalDiskToPartitionMapping = $logicalDiskToPartitionMappings | Where-Object { $_.Antecedent.DeviceID -eq $partitionMapping.Dependent.DeviceID }

                if($logicalDiskToPartitionMapping) {
                    $logicalDisk = $logicalDisks | Where-Object { $_.DeviceID -eq $logicalDiskToPartitionMapping.Dependent.DeviceID }
                }

                if($logicalDisk.DeviceID) {
                    $deviceId = $logicalDisk.DeviceID.Trim(':')
                }
                [pscustomobject]@{
                    AzureDiskName   = $azDiskName
                    DriveLetter     = $deviceId
                    LUN             = $DiskDrive.SCSILogicalUnit
                    PartitionName   = $diskPartition.Name
                    PartitionSizeGB = [math]::Round($diskPartition.Size / 1GB, 1)
                    DiskIndex       = $DiskDrive.Index
                    SCSILogicalUnit = $DiskDrive.SCSILogicalUnit
                    DiskSizeGB      = [math]::Round($DiskDrive.Size / 1GB)
                    DiskStatus      = $DiskDrive.Status
                }
            }
        }
        $logicalDisk,$logicalDiskToPartitionMapping,$diskDriveToDiskPartitionMapping,$deviceId,$diskPartition = $null
    }
}