????
Current Path : C:/Windows/SysWOW64/WindowsPowerShell/v1.0/Modules/Storage/ |
Current File : C:/Windows/SysWOW64/WindowsPowerShell/v1.0/Modules/Storage/StorageScripts.psm1 |
############################ # # Copyright (c) Microsoft Corporation # # Abstract: # SMAPI script cmdlets # ############################ using namespace System.Management.Automation.Runspaces using namespace Microsoft.Windows.Storage import-module Storage\StorageHealth.cdxml import-module Storage\StorageSubSystem.cdxml $StorageNamespace = 'root\microsoft\windows\storage' $WmiNamespace = 'root\wmi' $ClusterNamespace = 'root\mscluster' $Global:StorageHistoryCharts = [Hashtable]::Synchronized(@{}) function CreateErrorRecord { Param ( [String] $ErrorId, [String] $ErrorMessage, [System.Management.Automation.ErrorCategory] $ErrorCategory, [Exception] $Exception, [Object] $TargetObject ) if($Exception -eq $null) { $Exception = New-Object System.Management.Automation.RuntimeException $ErrorMessage } $errorRecord = New-Object System.Management.Automation.ErrorRecord @($Exception, $ErrorId, $ErrorCategory, $TargetObject) return $errorRecord } class StorageHistory { [UInt16]$Version [String]$FriendlyName [String]$SerialNumber [String]$AdapterSerialNumber [String]$FirmwareRevision [String]$DeviceGuid [String]$DeviceNumber [String]$BusType [String]$MediaType [DateTime]$EventTime [UInt32]$MaxQueueCount [UInt32]$MaxOutstandingCount [UInt64]$TotalIoCount [UInt64]$SuccessIoCount [UInt64]$FailedIoCount [UInt64]$TotalReadBytes [UInt64]$TotalWriteBytes [UInt64]$TotalIoLatency [UInt64]$AvgIoLatency [UInt64]$MaxIoLatency [UInt64]$MaxFlushLatency [UInt64]$MaxUnmapLatency [UInt64]$TotalQueueIoCount [UInt64]$TotalQueueIoWaitTime [UInt64]$AvgQueueIoWaitTime [UInt64]$MaxQueueIoWaitTime [UInt32]$IoTimeout [UInt64]$QueueIoWaitExceededTimeoutCount [UInt64]$QueueIoBusyCount [UInt64]$QueueIoPausedCount [UInt64]$QueueIoUntaggedCommandOutstandingCount [UInt64]$QueueIoPausedForUntaggedCount [UInt64]$TotalErrors [UInt64]$TotalReadWriteErrors [UInt64]$TotalImpendingDeviceFailureErrors [UInt64]$TotalDeviceFailureErrors [UInt16]$BucketCount [UInt64[]]$BucketIoLatency [UInt64[]]$BucketSuccessIoCount [UInt64[]]$BucketFailedIoCount [UInt64[]]$BucketTotalIoCount [UInt64[]]$BucketTotalIoTime [Single[]]$BucketIoPercent [UInt16]$HighestLatencyBucket [System.Collections.ArrayList]$UnresponsiveTime [System.Collections.ArrayList]$ResponsiveTime } class StorageHistoryAggregate { [UInt16]$Version [String]$FriendlyName [String]$SerialNumber [String]$AdapterSerialNumber [String]$FirmwareRevision [String]$DeviceGuid [String]$DeviceNumber [String]$BusType [String]$MediaType [DateTime]$StartTime [DateTime]$EndTime [UInt32]$EventCount [UInt32]$MaxQueueCount [UInt32]$MaxOutstandingCount [Double]$TotalIoCount [Double]$SuccessIoCount [Double]$FailedIoCount [Double]$TotalReadBytes [Double]$TotalWriteBytes [Double]$TotalIoLatency [UInt64]$AvgIoLatency [UInt64]$MaxIoLatency [UInt64]$MaxFlushLatency [UInt64]$MaxUnmapLatency [Double]$TotalQueueIoCount [Double]$TotalQueueIoWaitTime [UInt64]$AvgQueueIoWaitTime [UInt64]$MaxQueueIoWaitTime [UInt32]$IoTimeout [Double]$QueueIoWaitExceededTimeoutCount [Double]$QueueIoBusyCount [Double]$QueueIoPausedCount [Double]$QueueIoUntaggedCommandOutstandingCount [Double]$QueueIoPausedForUntaggedCount [Double]$TotalErrors [Double]$TotalReadWriteErrors [Double]$TotalImpendingDeviceFailureErrors [Double]$TotalDeviceFailureErrors [UInt16]$BucketCount [UInt64[]]$BucketIoLatency [Double[]]$BucketSuccessIoCount [Double[]]$BucketFailedIoCount [Double[]]$BucketTotalIoCount [Double[]]$BucketTotalIoTime [UInt32[]]$BucketIoPercent [UInt32[]]$BucketHighestLatencyCount } class StorageError { [UInt16]$Version [String]$FriendlyName [String]$SerialNumber [String]$AdapterSerialNumber [String]$FirmwareRevision [String]$DeviceGuid [String]$DeviceNumber [String]$BusType [String]$MediaType [DateTime]$EventTime [UInt32]$ServiceDuration [UInt32]$QueueWaitDuration [Byte]$Command [Byte]$SrbStatus [Byte]$ScsiStatus [Byte]$SenseKey [Byte]$SenseCode [Byte]$SenseCodeQualifier [UInt32]$IoSize [UInt32]$QueueDepth [UInt64]$LBA } class StorageIoDistribution { [UInt16]$Version [DateTime]$EventTime [UInt64]$TotalSuccessCount [String]$IoType [String]$IoSize [UInt64]$IoSizeSuccessCount [UInt64]$LatencySuccessCount1 [UInt64]$LatencySuccessCount2 [UInt64]$LatencySuccessCount3 [UInt64]$LatencySuccessCount4 [UInt64]$LatencySuccessCount5 [UInt64]$LatencySuccessCount6 [UInt64]$LatencySuccessCount7 [UInt64]$LatencySuccessCount8 [UInt64]$LatencySuccessCount9 [UInt64]$LatencySuccessCount10 [UInt64]$LatencySuccessCount11 [UInt64]$LatencySuccessCount12 } $PopulateStorageError = { Param ( [Ref] $Object, [System.Diagnostics.Eventing.Reader.EventRecord] $EventRecord, [System.String] $FriendlyName, [System.String] $SerialNumber, [System.String] $AdapterSerialNumber, [System.String] $FirmwareRevision, [System.String] $DeviceGuid, [System.String] $Number, [System.String] $BusType, [System.String] $MediaType ) $eventXml = [XML]$EventRecord.ToXml() $tempObject = [PsCustomObject]@{} for ($index = 0; $index -lt $eventXml.Event.EventData.Data.Count; $index++) { $tempObject | Add-Member -MemberType NoteProperty ` -Name $eventXml.Event.EventData.Data[$index].Name ` -Value $eventXml.Event.EventData.Data[$index].'#Text' } $Object.Value.PSObject.TypeNames.Insert( 0, "Microsoft.Windows.StorageManagement.StorageError" ) $Object.Value.PSObject.TypeNames.Insert( 1, "Microsoft.Windows.StorageManagement.StorageError_v1" ) $Object.Value.Version = [UInt16]$tempObject.Version if ([String]::IsNullOrEmpty($FriendlyName) -eq $false) { $Object.Value.FriendlyName = $FriendlyName } else { $Object.Value.FriendlyName = $tempObject.ProductId } if ([String]::IsNullOrEmpty($SerialNumber) -eq $false) { $Object.Value.SerialNumber = $SerialNumber } else { $Object.Value.SerialNumber = $tempObject.SerialNumber } if ([String]::IsNullOrEmpty($AdapterSerialNumber) -eq $false) { $Object.Value.AdapterSerialNumber = $AdapterSerialNumber } else { $Object.Value.AdapterSerialNumber = $tempObject.AdapterSerialNumber } if ([String]::IsNullOrEmpty($FirmwareRevision) -eq $false) { $Object.Value.FirmwareRevision = $FirmwareRevision } $Object.Value.DeviceGuid = $DeviceGuid $Object.Value.DeviceNumber = $Number if ([String]::IsNullOrEmpty($BusType) -eq $false) { $Object.Value.BusType = $BusType } else { switch ($tempObject.BusType) { "8" { $Object.Value.BusType = "RAID" } "10" { $Object.Value.BusType = "SAS" } "11" { $Object.Value.BusType = "SATA" } "14" { $Object.Value.BusType = "Virtual" } "17" { $Object.Value.BusType = "NVMe" } "19" { $Object.Value.BusType = "UFS" } default { $Object.Value.BusType = "Unknown" } } } $Object.Value.MediaType = $MediaType $Object.Value.EventTime = $event.TimeCreated $Object.Value.ServiceDuration = $tempObject.RequestDuration_ms $Object.Value.QueueWaitDuration = $tempObject.WaitDuration_ms $Object.Value.Command = $tempObject.Command $Object.Value.SrbStatus = $tempObject.SrbStatus $Object.Value.ScsiStatus = $tempObject.ScsiStatus $Object.Value.SenseKey = $tempObject.SenseKey $Object.Value.SenseCode = $tempObject.AddSense $Object.Value.SenseCodeQualifier = $tempObject.AddSenseQ $Object.Value.IoSize = $tempObject.IoSize $Object.Value.QueueDepth = $tempObject.QueueDepth $Object.Value.LBA = $tempObject.LBA } $PopulateStorageHistory = { Param ( [Ref] $Object, [System.Diagnostics.Eventing.Reader.EventRecord] $EventRecord, [System.String] $FriendlyName, [System.String] $SerialNumber, [System.String] $AdapterSerialNumber, [System.String] $FirmwareRevision, [System.String] $DeviceGuid, [System.String] $Number, [System.String] $BusType, [System.String] $MediaType ) $eventXml = [XML]$EventRecord.ToXml() $tempObject = [PsCustomObject]@{} for ($index = 0; $index -lt $eventXml.Event.EventData.Data.Count; $index++) { $tempObject | Add-Member -MemberType NoteProperty ` -Name $eventXml.Event.EventData.Data[$index].Name ` -Value $eventXml.Event.EventData.Data[$index].'#Text' } $Object.Value.PSObject.TypeNames.Insert( 0, "Microsoft.Windows.StorageManagement.StorageHistory" ) $Object.Value.Version = [UInt16]$tempObject.Version if ([String]::IsNullOrEmpty($FriendlyName) -eq $false) { $Object.Value.FriendlyName = $FriendlyName } else { $Object.Value.FriendlyName = $tempObject.ProductId } if ([String]::IsNullOrEmpty($SerialNumber) -eq $false) { $Object.Value.SerialNumber = $SerialNumber } else { $Object.Value.SerialNumber = $tempObject.SerialNumber } if ([String]::IsNullOrEmpty($AdapterSerialNumber) -eq $false) { $Object.Value.AdapterSerialNumber = $AdapterSerialNumber } else { $Object.Value.AdapterSerialNumber = $tempObject.AdapterSerialNumber } if ([String]::IsNullOrEmpty($FirmwareRevision) -eq $false) { $Object.Value.FirmwareRevision = $FirmwareRevision } else { $Object.Value.FirmwareRevision = $tempObject.FirmwareRevision } $Object.Value.DeviceGuid = $DeviceGuid $Object.Value.DeviceNumber = $Number if ([String]::IsNullOrEmpty($BusType) -eq $false) { $Object.Value.BusType = $BusType } else { switch ($tempObject.BusType) { "8" { $Object.Value.BusType = "RAID" } "10" { $Object.Value.BusType = "SAS" } "11" { $Object.Value.BusType = "SATA" } "14" { $Object.Value.BusType = "Virtual" } "17" { $Object.Value.BusType = "NVMe" } "19" { $Object.Value.BusType = "UFS" } default { $Object.Value.BusType = "Unknown" } } } $Object.Value.MediaType = $MediaType $Object.Value.EventTime = $event.TimeCreated $Object.Value.MaxQueueCount = [UInt32]$tempObject.MaxDeviceQueueCount $Object.Value.MaxOutstandingCount = [UInt32]$tempObject.MaxOutstandingCount if ($Object.Value.Version -eq 5) { $Object.Value.PSObject.TypeNames.Insert( 1, "Microsoft.Windows.StorageManagement.StorageHistory_v5" ) $Object.Value.BucketCount = 5 $Object.Value.BucketIoLatency = New-Object -TypeName System.UInt64[] $Object.Value.BucketCount $Object.Value.BucketSuccessIoCount = New-Object -TypeName System.UInt64[] $Object.Value.BucketCount $Object.Value.BucketFailedIoCount = New-Object -TypeName System.UInt64[] $Object.Value.BucketCount $Object.Value.BucketTotalIoCount = New-Object -TypeName System.UInt64[] $Object.Value.BucketCount $Object.Value.BucketTotalIoTime = New-Object -TypeName System.UInt64[] $Object.Value.BucketCount $Object.Value.BucketIoPercent = New-Object -TypeName System.UInt64[] $Object.Value.BucketCount $Object.Value.BucketIoLatency[0] = 16 * 10000 # 16 ms in 100ns $Object.Value.BucketIoLatency[1] = 64 * 10000 # 64 ms in 100ns $Object.Value.BucketIoLatency[2] = 2048 * 10000 # 2048 ms in 100ns $Object.Value.BucketIoLatency[3] = 5120 * 10000 # 5120 ms in 100ns $Object.Value.BucketIoLatency[4] = 10000 * 10000 # 5120+ ms approx to 10000 ms in 100ns for ($index = 0; $index -lt $Object.Value.BucketCount; $index++) { $field = "BucketIoCount" + [System.String]($index + 1) $Object.Value.BucketTotalIoCount[$index] = $($tempObject.$field) $Object.Value.BucketTotalIoTime[$index] = $Object.Value.BucketTotalIoCount[$index] * $Object.Value.BucketIoLatency[$index] $Object.Value.TotalIoCount += $Object.Value.BucketTotalIoCount[$index] $Object.Value.TotalIoLatency += $Object.Value.BucketTotalIoTime[$index] if ($Object.Value.BucketTotalIoCount[$index] -gt 0) { $Object.Value.MaxIoLatency = $Object.Value.BucketIoLatency[$index] $Object.Value.HighestLatencyBucket = $index } } if ($Object.Value.TotalIoCount -gt 0) { for ($index = 0; $index -lt $Object.Value.BucketCount; $index++) { $Object.Value.BucketIoPercent[$index] = ($Object.Value.BucketTotalIoCount[$index] * 100) / $Object.Value.TotalIoCount } } } elseif (($Object.Value.Version -ge 9) -or ($Object.Value.Version -le 11)) { $Object.Value.PSObject.TypeNames.Insert( 1, "Microsoft.Windows.StorageManagement.StorageHistory_v11" ) $Object.Value.TotalIoCount = $tempObject.TotalIoCount $Object.Value.TotalReadBytes = $tempObject.TotalReadBytes $Object.Value.TotalWriteBytes = $tempObject.TotalWriteBytes $Object.Value.MaxIoLatency = $tempObject.MaxReadWriteLatency_100ns $Object.Value.MaxFlushLatency = $tempObject.MaxFlushLatency_100ns $Object.Value.MaxUnmapLatency = $tempObject.MaxUnmapLatency_100ns if ($Object.Value.Version -eq 11) { $Object.Value.TotalQueueIoCount = $tempObject.TotalDeviceQueueIoCount $Object.Value.TotalQueueIoWaitTime = $tempObject.TotalDeviceQueueIoWaitDuration_100ns $Object.Value.MaxQueueIoWaitTime = $tempObject.MaxDeviceQueueIoWaitDuration_100ns $Object.Value.IoTimeout = $tempObject.IoTimeout_s $Object.Value.QueueIoWaitExceededTimeoutCount = $tempObject.DeviceQueueIoWaitExceededTimeoutCount $Object.Value.QueueIoBusyCount = $tempObject.DeviceQueueIoBusyCount $Object.Value.QueueIoPausedCount = $tempObject.DeviceQueueIoPausedCount $Object.Value.QueueIoUntaggedCommandOutstandingCount = $tempObject.DeviceQueueIoUntaggedCommandOutstandingCount $Object.Value.QueueIoPausedForUntaggedCount = $tempObject.DeviceQueueIoPausedForUntaggedCount } $Object.Value.BucketCount = 12 $Object.Value.BucketIoLatency = New-Object -TypeName System.UInt64[] $Object.Value.BucketCount $Object.Value.BucketSuccessIoCount = New-Object -TypeName System.UInt64[] $Object.Value.BucketCount $Object.Value.BucketFailedIoCount = New-Object -TypeName System.UInt64[] $Object.Value.BucketCount $Object.Value.BucketTotalIoCount = New-Object -TypeName System.UInt64[] $Object.Value.BucketCount $Object.Value.BucketTotalIoTime = New-Object -TypeName System.UInt64[] $Object.Value.BucketCount $Object.Value.BucketIoPercent = New-Object -TypeName System.UInt64[] $Object.Value.BucketCount $Object.Value.BucketIoLatency[0] = 256 * 10 # 256 us in 100ns $Object.Value.BucketIoLatency[1] = 1 * 10000 # 1 ms in 100ns $Object.Value.BucketIoLatency[2] = 4 * 10000 # 4 ms in 100ns $Object.Value.BucketIoLatency[3] = 16 * 10000 # 16 ms in 100ns $Object.Value.BucketIoLatency[4] = 64 * 10000 # 64 ms in 100ns $Object.Value.BucketIoLatency[5] = 128 * 10000 # 128 ms in 100ns $Object.Value.BucketIoLatency[6] = 256 * 10000 # 256 ms in 100ns $Object.Value.BucketIoLatency[7] = 2000 * 10000 # 2000 ms in 100ns $Object.Value.BucketIoLatency[8] = 6000 * 10000 # 6000 ms in 100ns $Object.Value.BucketIoLatency[9] = 10000 * 10000 # 10000 ms in 100ns $Object.Value.BucketIoLatency[10] = 20000 * 10000 # 20000 ms in 100ns $Object.Value.BucketIoLatency[11] = 30000 * 10000 # 20000+ ms approx to 30000 ms in 100ns for ($index = 0; $index -lt $Object.Value.BucketCount; $index++) { $field = "BucketIoSuccess" + [System.String]($index + 1) $Object.Value.BucketSuccessIoCount[$index] = $($tempObject.$field) $field = "BucketIoFailed" + [System.String]($index + 1) $Object.Value.BucketFailedIoCount[$index] = $($tempObject.$field) $field = "BucketIoLatency" + [System.String]($index + 1) + "_100ns" $Object.Value.BucketTotalIoTime[$index] = $($tempObject.$field) $Object.Value.BucketTotalIoCount[$index] = $Object.Value.BucketSuccessIoCount[$index] + $Object.Value.BucketFailedIoCount[$index] $Object.Value.SuccessIoCount += $Object.Value.BucketSuccessIoCount[$index] $Object.Value.FailedIoCount += $Object.Value.BucketFailedIoCount[$index] $Object.Value.TotalIoLatency += $Object.Value.BucketTotalIoTime[$index] if ($Object.Value.TotalIoCount -gt 0) { $Object.Value.BucketIoPercent[$index] = ($Object.Value.BucketTotalIoCount[$index] * 100) / $Object.Value.TotalIoCount } if ($Object.Value.BucketTotalIoCount[$index] -gt 0) { $Object.Value.HighestLatencyBucket = $index } } } if ($Object.Value.TotalIoCount -gt 0) { $Object.Value.AvgIoLatency = $Object.Value.TotalIoLatency / $Object.Value.TotalIoCount } if (($Object.Value.Version -eq 11) -and ($Object.Value.TotalQueueIoCount -gt 0)) { $Object.Value.AvgQueueIoWaitTime = $Object.Value.TotalQueueIoWaitTime / $Object.Value.TotalQueueIoCount } } $AugmentStorageHistory = { Param ( [Ref] $Object, [System.Diagnostics.Eventing.Reader.EventRecord] $EventRecord ) $eventXml = [XML]$EventRecord.ToXml() $tempObject = [PsCustomObject]@{} for ($index = 0; $index -lt $eventXml.Event.EventData.Data.Count; $index++) { $tempObject | Add-Member -MemberType NoteProperty ` -Name $eventXml.Event.EventData.Data[$index].Name ` -Value $eventXml.Event.EventData.Data[$index].'#Text' } if ($EventRecord.Id -eq 502) { if ($Object.Value.UnresponsiveTime -eq $null) { $Object.Value.UnresponsiveTime = New-Object System.Collections.ArrayList } $Object.Value.UnresponsiveTime.Add($EventRecord.TimeCreated) } elseif ($EventRecord.Id -eq 503) { if ($Object.Value.ResponsiveTime -eq $null) { $Object.Value.ResponsiveTime = New-Object System.Collections.ArrayList } $Object.Value.ResponsiveTime.Add($EventRecord.TimeCreated) } elseif ($EventRecord.Id -eq 504) { $Object.Value.TotalErrors = $tempObject.TotalErrors $Object.Value.TotalReadWriteErrors = $tempObject.TotalReadWriteErrors $Object.Value.TotalImpendingDeviceFailureErrors = $tempObject.TotalImpendingDeviceFailureErrors $Object.Value.TotalDeviceFailureErrors = $tempObject.TotalDeviceFailureErrors } } $PopulateStorageHistoryAggregate = { Param ( [Ref] $AggregateObject, [Ref] $Object ) if ($AggregateObject.Value.Version -eq 0) { $AggregateObject.Value.PSObject.TypeNames.Insert( 0, "Microsoft.Windows.StorageManagement.StorageHistoryAggregate" ) if ($Object.Value.Version -eq 5) { $AggregateObject.Value.PSObject.TypeNames.Insert( 1, "Microsoft.Windows.StorageManagement.StorageHistoryAggregate_v5" ) } elseif (($Object.Value.Version -ge 9) -or ($Object.Value.Version -le 11)) { $AggregateObject.Value.PSObject.TypeNames.Insert( 1, "Microsoft.Windows.StorageManagement.StorageHistoryAggregate_v11" ) } $AggregateObject.Value.Version = $Object.Value.Version $AggregateObject.Value.FriendlyName = $Object.Value.FriendlyName $AggregateObject.Value.SerialNumber = $Object.Value.SerialNumber $AggregateObject.Value.AdapterSerialNumber = $Object.Value.AdapterSerialNumber $AggregateObject.Value.FirmwareRevision = $Object.Value.FirmwareRevision $AggregateObject.Value.DeviceGuid = $Object.Value.DeviceGuid $AggregateObject.Value.DeviceNumber = $Object.Value.DeviceNumber $AggregateObject.Value.BusType = $Object.Value.BusType $AggregateObject.Value.MediaType = $Object.Value.MediaType $AggregateObject.Value.StartTime = $Object.Value.EventTime $AggregateObject.Value.EndTime = $Object.Value.EventTime $AggregateObject.Value.EventCount = 1 $AggregateObject.Value.MaxQueueCount = $Object.Value.MaxQueueCount $AggregateObject.Value.MaxOutstandingCount = $Object.Value.MaxOutstandingCount $AggregateObject.Value.TotalIoCount = $Object.Value.TotalIoCount $AggregateObject.Value.SuccessIoCount = $Object.Value.SuccessIoCount $AggregateObject.Value.FailedIoCount = $Object.Value.FailedIoCount $AggregateObject.Value.TotalReadBytes = $Object.Value.TotalReadBytes $AggregateObject.Value.TotalWriteBytes = $Object.Value.TotalWriteBytes $AggregateObject.Value.TotalIoLatency = $Object.Value.TotalIoLatency $AggregateObject.Value.MaxIoLatency = $Object.Value.MaxIoLatency $AggregateObject.Value.MaxFlushLatency = $Object.Value.MaxFlushLatency $AggregateObject.Value.MaxUnmapLatency = $Object.Value.MaxUnmapLatency $AggregateObject.Value.TotalQueueIoCount = $Object.Value.TotalQueueIoCount $AggregateObject.Value.TotalQueueIoWaitTime = $Object.Value.TotalQueueIoWaitTime $AggregateObject.Value.MaxQueueIoWaitTime = $Object.Value.MaxQueueIoWaitTime if ($Object.Value.IoTimeout) { $AggregateObject.Value.IoTimeout = $Object.Value.IoTimeout } $AggregateObject.Value.QueueIoWaitExceededTimeoutCount = $Object.Value.QueueIoWaitExceededTimeoutCount $AggregateObject.Value.QueueIoBusyCount = $Object.Value.QueueIoBusyCount $AggregateObject.Value.QueueIoPausedCount = $Object.Value.QueueIoPausedCount $AggregateObject.Value.QueueIoUntaggedCommandOutstandingCount = $Object.Value.QueueIoUntaggedCommandOutstandingCount $AggregateObject.Value.QueueIoPausedForUntaggedCount = $Object.Value.QueueIoPausedForUntaggedCount $AggregateObject.Value.TotalErrors = $Object.Value.TotalErrors $AggregateObject.Value.TotalReadWriteErrors = $Object.Value.TotalReadWriteErrors $AggregateObject.Value.TotalImpendingDeviceFailureErrors = $Object.Value.TotalImpendingDeviceFailureErrors $AggregateObject.Value.TotalDeviceFailureErrors = $Object.Value.TotalDeviceFailureErrors $AggregateObject.Value.BucketCount = $Object.Value.BucketCount $AggregateObject.Value.BucketIoLatency = New-Object -TypeName System.UInt64[] $AggregateObject.Value.BucketCount $AggregateObject.Value.BucketSuccessIoCount = New-Object -TypeName System.UInt64[] $AggregateObject.Value.BucketCount $AggregateObject.Value.BucketFailedIoCount = New-Object -TypeName System.UInt64[] $AggregateObject.Value.BucketCount $AggregateObject.Value.BucketTotalIoCount = New-Object -TypeName System.UInt64[] $AggregateObject.Value.BucketCount $AggregateObject.Value.BucketTotalIoTime = New-Object -TypeName System.UInt64[] $AggregateObject.Value.BucketCount $AggregateObject.Value.BucketIoPercent = New-Object -TypeName System.UInt64[] $AggregateObject.Value.BucketCount $AggregateObject.Value.BucketHighestLatencyCount = New-Object -TypeName System.UInt32[] $AggregateObject.Value.BucketCount for ($index = 0; $index -lt $AggregateObject.Value.BucketCount; $index++) { $AggregateObject.Value.BucketIoLatency[$index] = $Object.Value.BucketIoLatency[$index] $AggregateObject.Value.BucketSuccessIoCount[$index] = $Object.Value.BucketSuccessIoCount[$index] $AggregateObject.Value.BucketFailedIoCount[$index] = $Object.Value.BucketFailedIoCount[$index] $AggregateObject.Value.BucketTotalIoCount[$index] = $Object.Value.BucketTotalIoCount[$index] $AggregateObject.Value.BucketTotalIoTime[$index] = $Object.Value.BucketTotalIoTime[$index] } $AggregateObject.Value.BucketHighestLatencyCount[$($Object.Value.HighestLatencyBucket)]++ } else { if ($Object.Value.Version -ne $AggregateObject.Value.Version) { return } if ($Object.Value.EventTime -lt $AggregateObject.Value.StartTime) { $AggregateObject.Value.StartTime = $Object.Value.EventTime } if ($Object.Value.EventTime -gt $AggregateObject.Value.EndTime) { $AggregateObject.Value.EndTime = $Object.Value.EventTime } $AggregateObject.Value.EventCount++ if ($Object.Value.MaxQueueCount -gt $AggregateObject.Value.MaxQueueCount) { $AggregateObject.Value.MaxQueueCount = $Object.Value.MaxQueueCount } if ($Object.Value.MaxOutstandingCount -gt $AggregateObject.Value.MaxOutstandingCount) { $AggregateObject.Value.MaxOutstandingCount = $Object.Value.MaxOutstandingCount } $AggregateObject.Value.TotalIoCount += $Object.Value.TotalIoCount $AggregateObject.Value.SuccessIoCount += $Object.Value.SuccessIoCount $AggregateObject.Value.FailedIoCount += $Object.Value.FailedIoCount $AggregateObject.Value.TotalReadBytes += $Object.Value.TotalReadBytes $AggregateObject.Value.TotalWriteBytes += $Object.Value.TotalWriteBytes $AggregateObject.Value.TotalIoLatency += $Object.Value.TotalIoLatency if ($Object.Value.MaxIoLatency -gt $AggregateObject.Value.MaxIoLatency) { $AggregateObject.Value.MaxIoLatency = $Object.Value.MaxIoLatency } if ($Object.Value.MaxFlushLatency -gt $AggregateObject.Value.MaxFlushLatency) { $AggregateObject.Value.MaxFlushLatency = $Object.Value.MaxFlushLatency } if ($Object.Value.MaxUnmapLatency -gt $AggregateObject.Value.MaxUnmapLatency) { $AggregateObject.Value.MaxUnmapLatency = $Object.Value.MaxUnmapLatency } $AggregateObject.Value.TotalQueueIoCount += $Object.Value.TotalQueueIoCount $AggregateObject.Value.TotalQueueIoWaitTime += $Object.Value.TotalQueueIoWaitTime if ($Object.Value.MaxQueueIoWaitTime -gt $AggregateObject.Value.MaxQueueIoWaitTime) { $AggregateObject.Value.MaxQueueIoWaitTime = $Object.Value.MaxQueueIoWaitTime } if ($Object.Value.IoTimeout) { $AggregateObject.Value.IoTimeout = $Object.Value.IoTimeout } $AggregateObject.Value.QueueIoWaitExceededTimeoutCount += $Object.Value.QueueIoWaitExceededTimeoutCount $AggregateObject.Value.QueueIoBusyCount += $Object.Value.QueueIoBusyCount $AggregateObject.Value.QueueIoPausedCount += $Object.Value.QueueIoPausedCount $AggregateObject.Value.QueueIoUntaggedCommandOutstandingCount += $Object.Value.QueueIoUntaggedCommandOutstandingCount $AggregateObject.Value.QueueIoPausedForUntaggedCount += $Object.Value.QueueIoPausedForUntaggedCount $AggregateObject.Value.TotalErrors += $Object.Value.TotalErrors $AggregateObject.Value.TotalReadWriteErrors += $Object.Value.TotalReadWriteErrors $AggregateObject.Value.TotalImpendingDeviceFailureErrors += $Object.Value.TotalImpendingDeviceFailureErrors $AggregateObject.Value.TotalDeviceFailureErrors += $Object.Value.TotalDeviceFailureErrors for ($index = 0; $index -lt $AggregateObject.Value.BucketCount; $index++) { $AggregateObject.Value.BucketSuccessIoCount[$index] += $Object.Value.BucketSuccessIoCount[$index] $AggregateObject.Value.BucketFailedIoCount[$index] += $Object.Value.BucketFailedIoCount[$index] $AggregateObject.Value.BucketTotalIoCount[$index] += $Object.Value.BucketTotalIoCount[$index] $AggregateObject.Value.BucketTotalIoTime[$index] += $Object.Value.BucketTotalIoTime[$index] } $AggregateObject.Value.BucketHighestLatencyCount[$($Object.Value.HighestLatencyBucket)]++ } } function Get-StorageHistory { Param ( #### -------------------- Parameter sets -------------------------------------#### [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_PhysicalDisk")] [Parameter( ParameterSetName = 'ByPhysicalDisk', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $PhysicalDisk, [System.String] [Parameter( ParameterSetName = 'ByDeviceGuid', ValueFromPipeline = $false, Mandatory = $true)] [ValidateNotNullOrEmpty()] [Alias("DeviceId")] $DeviceGuid, [System.String] [Parameter( ParameterSetName = 'ByDeviceNumber', ValueFromPipeline = $false, Mandatory = $true)] [ValidateNotNullOrEmpty()] $DeviceNumber, #### ------------------------- PhysicalDisk parameters -----------------------#### [System.Management.Automation.PSCredential] [Parameter( ParameterSetName = 'ByPhysicalDisk', Mandatory = $false)] $Credential, #### ------------------------- Common method parameters ----------------------#### [System.String] [Parameter( ParameterSetName = 'ByPhysicalDisk', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceGuid', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceNumber', Mandatory = $false)] [ValidateNotNullOrEmpty()] $LogFile, [UInt32] [Parameter( ParameterSetName = 'ByPhysicalDisk', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceGuid', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceNumber', Mandatory = $false)] $NumberOfHours, [Switch] [Parameter( ParameterSetName = 'ByPhysicalDisk', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceGuid', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceNumber', Mandatory = $false)] $Disaggregate, [Switch] [Parameter( ParameterSetName = 'ByPhysicalDisk', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceGuid', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceNumber', Mandatory = $false)] $Errors ) Begin { } Process { # # Get the device id based on the parameter set # switch -regex ($psCmdlet.ParameterSetName) { { @( ` "ByPhysicalDisk", ` "ByDeviceNumber" ` ) -contains $_ ` } { # # If device number is specified, # get the corresponding device # try { if ($DeviceNumber) { $deviceId = $DeviceNumber $PhysicalDisk = Get-PhysicalDisk -DeviceNumber $DeviceNumber } } catch { $errorObject = CreateErrorRecord -ErrorId "ObjectNotFound" ` -ErrorMessage "A physical disk matching the input criteria was not found" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } # # If log file is not specified, # get the node name where the # device is connected # if (-not $LogFile) { $subsystem = $PhysicalDisk | get-storagesubsystem if ($subsystem.Model -eq "Clustered Windows Storage" -and $subsystem.StorageConnectionType -eq "Local Storage") { # If the subsystem is clustered local (storage spaces direct) use # SSU mapping since it works for 'Lost Communication' drives $storageScaleUnit = $PhysicalDisk | Get-StorageScaleUnit if (-not $storageScaleUnit) { $errorObject = CreateErrorRecord -ErrorId "ObjectNotFound" ` -ErrorMessage "A storage scale unit was not found for the physical disk" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } $computerName = $storageScaleUnit.FriendlyName } else { $storageNode = $PhysicalDisk | Get-StorageNode -PhysicallyConnected if (-not $storageNode) { $errorObject = CreateErrorRecord -ErrorId "ObjectNotFound" ` -ErrorMessage "A storage node was not found for the physical disk" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } $computerName = $storageNode.Name } } # Extract the device guid from the object id # It is of the format ' <>:PD:<DeviceGuid>" ' $guid = $PhysicalDisk.ObjectId.Split(":")[2].TrimEnd('"') $friendlyName = $PhysicalDisk.FriendlyName $serialNumber = $PhysicalDisk.SerialNumber $adapterSerialNumber = $PhysicalDisk.AdapterSerialNumber $firmwareRevision = $PhysicalDisk.FirmwareVersion if ($PhysicalDisk.DeviceId -ge 0) { $deviceId = $PhysicalDisk.DeviceId } $busType = $PhysicalDisk.BusType $mediaType = $PhysicalDisk.MediaType } { @( ` "ByDeviceGuid" ` ) -contains $_ ` } { try { [System.Guid]::Parse($DeviceGuid) | Out-Null $guid = $DeviceGuid } catch { $errorObject = CreateErrorRecord -ErrorId "InvalidArgument" ` -ErrorMessage "DeviceGuid (or DeviceId) should be a GUID of the format 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' or '{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}'" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidArgument) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } } } # # Build the query to look for the specific events in storport log # if ($PSBoundParameters.ContainsKey("Errors")) { $eventFilter = "EventID=549" } else { $eventFilter = "EventID=502 or ` EventID=503 or ` EventID=504 or ` EventID=505" } if ($NumberOfHours) { $query = "*[System[($eventFilter) and TimeCreated[timediff(@SystemTime) <= $($NumberOfHours*3600*1000)]]] and *[EventData[Data[@Name='ClassDeviceGuid'] and (Data='$guid')]]" } else { $query = "*[System[($eventFilter)]] and *[EventData[Data[@Name='ClassDeviceGuid'] and (Data='$guid')]]" } # # Retrive the events depending on the parameters # Write-Progress -Activity "Get-StorageHistory" -PercentComplete 0 -CurrentOperation "Gathering events" -Status "0/2" try { if ($computerName) { if ($Credential) { $events = Get-WinEvent -LogName Microsoft-Windows-Storage-Storport/Operational ` -FilterXPath $query ` -ComputerName $computerName ` -Credential $Credential ` -ErrorAction Stop ` -Oldest } else { $events = Get-WinEvent -LogName Microsoft-Windows-Storage-Storport/Operational ` -FilterXPath $query ` -ComputerName $computerName ` -ErrorAction Stop ` -Oldest } } else { if ($LogFile) { $events = Get-WinEvent -Path $LogFile ` -FilterXPath $query ` -ErrorAction Stop ` -Oldest } else { $events = Get-WinEvent -LogName Microsoft-Windows-Storage-Storport/Operational ` -FilterXPath $query ` -ErrorAction Stop ` -Oldest } } } catch { $errorObject = CreateErrorRecord -ErrorId $_.FullyQualifiedErrorId ` -ErrorMessage $null ` -ErrorCategory $_.CategoryInfo.Category ` -Exception $_.Exception ` -TargetObject $_.TargetObject $psCmdlet.WriteError($errorObject) return } # # Construct StorageHistory and StorageHistoryAggregate objects # Write-Progress -Activity "Get-StorageHistory" -PercentComplete 0 -CurrentOperation "Constructing storage history objects" -Status "1/2" if ($PSBoundParameters.ContainsKey("Errors")) { for ($index = 0; $index -lt $events.count;) { $event = $events[$index] $index++ [StorageError]$data = [StorageError]::new() &$PopulateStorageError -Object ([ref]$data) ` -EventRecord $event ` -FriendlyName $friendlyName ` -SerialNumber $serialNumber ` -AdapterSerialNumber $adapterSerialNumber ` -FirmwareRevision $firmwareRevision ` -DeviceGuid $guid ` -Number $deviceId ` -BusType $busType ` -MediaType $mediaType | Out-Null $psCmdlet.WriteObject($data) } } else { $firstEvent = $true for ($index = 0; $index -lt $events.count;) { $event = $events[$index] $index++ # # A StorageHistory object is created for every 505 event. # Following non-505 events are collaped into it till # the next 505 event is encountered. All non-505 events # before the first 505 event are ignored # if ($event.Id -ne 505) { continue } [StorageHistory]$data = [StorageHistory]::new() &$PopulateStorageHistory -Object ([ref]$data) ` -EventRecord $event ` -FriendlyName $friendlyName ` -SerialNumber $serialNumber ` -AdapterSerialNumber $adapterSerialNumber ` -FirmwareRevision $firmwareRevision ` -DeviceGuid $guid ` -Number $deviceId ` -BusType $busType ` -MediaType $mediaType | Out-Null # # If there are no IOs, skip # if ($data.TotalIoCount -eq 0) { continue } # # Check if there are any non-505 events following it # for (; $index -lt $events.count; $index++) { $event = $events[$index] if ($event.Id -eq 505) { break } &$AugmentStorageHistory -Object ([ref]$data) ` -EventRecord $event | Out-Null } if ($PSBoundParameters.ContainsKey("Disaggregate")) { $psCmdlet.WriteObject($data) } else { if ($firstEvent) { [StorageHistoryAggregate]$aggregateData = [StorageHistoryAggregate]::new() $firstEvent = $false } &$PopulateStorageHistoryAggregate -AggregateObject ([ref]$aggregateData) ` -Object ([ref]$data) | Out-Null } } if ($aggregateData) { $aggregateData.AvgIoLatency = $aggregateData.TotalIoLatency / $aggregateData.TotalIoCount if ($aggregateData.TotalQueueIoCount -gt 0) { $aggregateData.AvgQueueIoWaitTime = $aggregateData.TotalQueueIoWaitTime / $aggregateData.TotalQueueIoCount } for ($index = 0; $index -lt $aggregateData.BucketCount; $index++) { $aggregateData.BucketIoPercent[$index] = ($aggregateData.BucketTotalIoCount[$index] * 100) / $aggregateData.TotalIoCount } $psCmdlet.WriteObject($aggregateData) } } Write-Progress -Activity "Get-StorageHistory" -Completed -Status "2/2" } } $DisplayTimeSeriesChart = { Param ( [System.String] $ChartId, [System.String] $Title, [System.Collections.ArrayList] $StorageHistory, [System.Boolean] $ShowDefault, [System.Boolean] $ShowAvgLatency, [System.Boolean] $ShowMaxLatency ) # # Build a device table with the # data to chart. # $deviceTable = New-Object System.Collections.Hashtable ForEach ($data in $StorageHistory) { if ($deviceTable.Contains($data.DeviceGuid) -eq $false) { $deviceData = New-Object -TypeName PsObject $deviceData | Add-Member -NotePropertyName "DeviceNumber" -NotePropertyValue $data.DeviceNumber $deviceData | Add-Member -NotePropertyName "FriendlyName" -NotePropertyValue $data.FriendlyName $deviceData | Add-Member -NotePropertyName "SerialNumber" -NotePropertyValue $data.SerialNumber $dataList = New-Object System.Collections.ArrayList $deviceData | Add-Member -NotePropertyName "DataList" -NotePropertyValue $dataList $deviceTable.Add($data.DeviceGuid, $deviceData) } else { $deviceData = $deviceTable[$data.DeviceGuid] $dataList = $deviceData.DataList } $dataPoint = New-Object -TypeName PsObject $dataPoint | Add-Member -NotePropertyName "DateTime" -NotePropertyValue $data.EventTime $dataPoint | Add-Member -NotePropertyName "AvgIoLatency" -NotePropertyValue ($data.AvgIoLatency/10) $dataPoint | Add-Member -NotePropertyName "MaxIoLatency" -NotePropertyValue ($data.MaxIoLatency/10) $dataPoint | Add-Member -NotePropertyName "MaxQueueDepth" -NotePropertyValue $data.MaxOutstandingCount $dataPoint | Add-Member -NotePropertyName "UnresponsiveTime" -NotePropertyValue $data.UnresponsiveTime $dataPoint | Add-Member -NotePropertyName "ResponsiveTime" -NotePropertyValue $data.ResponsiveTime [void]$dataList.Add($dataPoint) } # # Create a chart # $chart = New-object Windows.Forms.DataVisualization.Charting.Chart $chart.Anchor = [Windows.Forms.AnchorStyles]::Bottom -bor [Windows.Forms.AnchorStyles]::Right -bor [Windows.Forms.AnchorStyles]::Top -bor [Windows.Forms.AnchorStyles]::Left $chart.Width = 1000 $chart.Height = 800 $chart.Left = 40 $chart.Top = 30 $chart.BackColor = [Drawing.Color]::White [void]$chart.Titles.Add($Title) $chart.Titles[0].Font = "segoeuilight,12pt" # # Create a chart area to draw on # $chartArea = New-Object Windows.Forms.DataVisualization.Charting.ChartArea $chartarea.Name = "TimeSeries" $chartarea.AxisX.IntervalType = [Windows.Forms.DataVisualization.Charting.DateTimeIntervalType]::Hours $chartarea.AxisX.IntervalAutoMode = [Windows.Forms.DataVisualization.Charting.IntervalAutoMode]::VariableCount $chartarea.AxisX.MajorGrid.Enabled = $false $chartarea.AxisX.LabelStyle.Format = "yyyy/MM/dd h tt" $chartarea.AxisX.ScaleView.Zoomable = $true $chartarea.AxisX.ScrollBar.IsPositionedInside = $true $chartarea.AxisX.ScrollBar.ButtonStyle = [Windows.Forms.DataVisualization.Charting.ScrollBarButtonStyles]::All $chartarea.CursorX.IsUserEnabled = $true $chartarea.CursorX.IsUserSelectionEnabled = $true $chartarea.CursorX.IntervalType = [Windows.Forms.DataVisualization.Charting.DateTimeIntervalType]::Hours $chartarea.CursorX.AutoScroll = $true $chartArea.AxisY.Title = "Latency (us)" $chartArea.AxisY.TitleFont = "segoeuilight,12pt" $chartarea.AxisY.LabelStyle.Format = "N0" $chartarea.AxisY.MinorGrid.Enabled = $true $chartarea.AxisY.MinorGrid.LineDashStyle = [Windows.Forms.DataVisualization.Charting.ChartDashStyle]::Dot $chart.ChartAreas.Add($chartArea) $legend = New-Object Windows.Forms.DataVisualization.Charting.Legend $legend.Name = "TimeSeries" $legend.Docking = [Windows.Forms.DataVisualization.Charting.Docking]::Right $legend.LegendStyle = [Windows.Forms.DataVisualization.Charting.LegendStyle]::Column $chart.Legends.Add($legend) # # Build the data to chart # if ($ShowDefault -eq $true) { $ShowAvgLatency = $true if ($deviceTable.Count -eq 1) { $ShowMaxLatency = $true } else { $ShowMaxLatency = $false } } ForEach ($deviceGuid in $deviceTable.Keys) { $deviceData = $deviceTable[$deviceGuid] $dataList = $deviceData.DataList if ($deviceTable.Count -eq 1) { [void]$chart.Titles.Add("Latency plot for " + $deviceData.DeviceNumber + " : " + $deviceData.FriendlyName + " : " + $deviceData.SerialNumber) } # # Show max and avg latencies # if ($ShowAvgLatency -eq $true) { if ($deviceTable.Count -eq 1) { $seriesName = "Avg" } else { $seriesName = "$($deviceData.DeviceNumber) : $($deviceData.SerialNumber) : Avg" } [void]$chart.Series.Add($seriesName) $chart.Series[$seriesName].ChartType = [Windows.Forms.DataVisualization.Charting.SeriesChartType]::Line $chart.Series[$seriesName].XValueType = [Windows.Forms.DataVisualization.Charting.ChartValueType]::DateTime $chart.Series[$seriesName].Legend = "TimeSeries" $chart.Series[$seriesName].MarkerStyle = [Windows.Forms.DataVisualization.Charting.MarkerStyle]::Circle if ($deviceTable.Count -eq 1) { $chart.Series[$seriesName].Color = [Drawing.Color]::Green } ForEach ($dataPoint in $dataList) { $point = New-Object Windows.Forms.DataVisualization.Charting.DataPoint $point.SetValueXY($dataPoint.DateTime, $dataPoint.AvgIoLatency) $point.Tooltip = "Value: #VALY{N0} us\n" + "At: #VALX{yyyy/MM/dd h:mm tt}\n" + "Max QD: $($dataPoint.MaxQueueDepth)" $chart.Series[$seriesName].Points.Add($point) } } if ($ShowMaxLatency -eq $true) { if ($deviceTable.Count -eq 1) { $seriesName = "Max" } else { $seriesName = "$($deviceData.DeviceNumber) : $($deviceData.SerialNumber) : Max" } [void]$chart.Series.Add($seriesName) $chart.Series[$seriesName].ChartType = [Windows.Forms.DataVisualization.Charting.SeriesChartType]::Line $chart.Series[$seriesName].XValueType = [Windows.Forms.DataVisualization.Charting.ChartValueType]::DateTime $chart.Series[$seriesName].Legend = "TimeSeries" $chart.Series[$seriesName].MarkerStyle = [Windows.Forms.DataVisualization.Charting.MarkerStyle]::Circle if ($deviceTable.Count -eq 1) { $chart.Series[$seriesName].Color = [Drawing.Color]::Orange } ForEach ($dataPoint in $dataList) { $point = New-Object Windows.Forms.DataVisualization.Charting.DataPoint $point.SetValueXY($dataPoint.DateTime, $dataPoint.MaxIoLatency) $point.Tooltip = "Value: #VALY{N0} us\n" + "At: #VALX{yyyy/MM/dd h:mm tt}\n" + "Max QD: $($dataPoint.MaxQueueDepth)" $chart.Series[$seriesName].Points.Add($point) } } # # Show interesting events when there # is a single device # if ($deviceTable.Count -eq 1) { $seriesName = "Events" [void]$chart.Series.Add($seriesName) $chart.Series[$seriesName].ChartType = [Windows.Forms.DataVisualization.Charting.SeriesChartType]::Point $chart.Series[$seriesName].XValueType = [Windows.Forms.DataVisualization.Charting.ChartValueType]::DateTime $chart.Series[$seriesName].Legend = "TimeSeries" $chart.Series[$seriesName].MarkerStyle = [Windows.Forms.DataVisualization.Charting.MarkerStyle]::Square $chart.Series[$seriesName].MarkerSize = 5 $chart.Series[$seriesName].Color = [Drawing.Color]::Red # # Find the level at which to show the points # $value = 0 ForEach ($dataPoint in $dataList) { if ($ShowMaxLatency -eq $true) { if ($dataPoint.MaxIoLatency -gt $value) { $value = $dataPoint.MaxIoLatency } } else { if ($dataPoint.AvgIoLatency -gt $value) { $value = $dataPoint.AvgIoLatency } } } ForEach ($dataPoint in $dataList) { ForEach ($time in $dataPoint.UnresponsiveTime) { $point = New-Object Windows.Forms.DataVisualization.Charting.DataPoint $point.SetValueXY($time, $value) $point.Tooltip = "Marked unresponsive at #VALX{yyyy/MM/dd h:mm tt}\n" $chart.Series[$seriesName].Points.Add($point) } ForEach ($time in $dataPoint.ResponsiveTime) { $point = New-Object Windows.Forms.DataVisualization.Charting.DataPoint $point.SetValueXY($time, $value) $point.Tooltip = "Marked responsive at #VALX{yyyy/MM/dd h:mm tt}\n" $chart.Series[$seriesName].Points.Add($point) } } } } $chart.ChartAreas[0].AxisX.LabelStyle.Angle = -45 # # Display the chart on a form # $form = New-Object Windows.Forms.Form $form.Text = "Storage Performance Chart" $form.Width = 1100 $form.Height = 900 $form.controls.add($chart) $form.Add_Shown({$form.Activate()}) [void]$form.ShowDialog() $form.Close() $form.Dispose() [void]$StorageHistoryCharts.Host.Runspace.Events.GenerateEvent("StorageHistoryChart.Close", $null, $null, $ChartId) } $DisplaySummaryChart = { Param ( [System.String] $ChartId, [System.String] $Title, [System.Collections.ArrayList] $StorageHistory, [System.Boolean] $ShowDefault, [System.Boolean] $ShowAvgLatency, [System.Boolean] $ShowMaxLatency ) # # Create a chart # $chart = New-object Windows.Forms.DataVisualization.Charting.Chart $chart.Anchor = [Windows.Forms.AnchorStyles]::Bottom -bor [Windows.Forms.AnchorStyles]::Right -bor [Windows.Forms.AnchorStyles]::Top -bor [Windows.Forms.AnchorStyles]::Left $chart.Width = 1000 $chart.Height = 800 $chart.Left = 40 $chart.Top = 30 $chart.BackColor = [Drawing.Color]::White [void]$chart.Titles.Add($Title) $chart.Titles[0].Font = "segoeuilight,12pt" if ($StorageHistory.Count -eq 1) { [void]$chart.Titles.Add("IO Distribution for " + $StorageHistory[0].DeviceNumber + " : " + $StorageHistory[0].FriendlyName + " : " + $StorageHistory[0].SerialNumber) } else { [void]$chart.Titles.Add("Latency comparison") } # # Create a chart area to draw on # $chartArea = New-Object Windows.Forms.DataVisualization.Charting.ChartArea $chartarea.Name = "Summary" $chartarea.AxisX.Interval = 1 $chartarea.AxisX.IntervalAutoMode = [Windows.Forms.DataVisualization.Charting.IntervalAutoMode]::FixedCount $chartarea.AxisX.MajorGrid.Enabled = $false # For a single device show IO distribution. # For multiple devices show avg and max latency. if ($StorageHistory.Count -eq 1) { $chartArea.AxisX.Title = "Latency buckets (us)" $chartArea.AxisX.TitleFont = "segoeuilight,12pt" $chartArea.AxisY.Title = "IO Count" } else { $chartarea.AxisX.ScaleView.Size = [Math]::Min($StorageHistory.Count, 40) $chartarea.AxisX.ScaleView.Zoomable = $true $chartarea.AxisX.ScrollBar.IsPositionedInside = $true $chartarea.AxisX.ScrollBar.ButtonStyle = [Windows.Forms.DataVisualization.Charting.ScrollBarButtonStyles]::All $chartarea.CursorX.IsUserEnabled = $true $chartarea.CursorX.IsUserSelectionEnabled = $true $chartarea.CursorX.AutoScroll = $true $chartArea.AxisY.Title = "Latency (us)" } $chartArea.AxisY.TitleFont = "segoeuilight,12pt" $chartarea.AxisY.LabelStyle.Format = "N0" $chartarea.AxisY.MinorGrid.Enabled = $true $chartarea.AxisY.MinorGrid.LineDashStyle = [Windows.Forms.DataVisualization.Charting.ChartDashStyle]::Dot $chart.ChartAreas.Add($chartArea) $legend = New-Object Windows.Forms.DataVisualization.Charting.Legend $legend.Name = "Summary" $legend.Docking = [Windows.Forms.DataVisualization.Charting.Docking]::Right $legend.LegendStyle = [Windows.Forms.DataVisualization.Charting.LegendStyle]::Column $chart.Legends.Add($legend) # # Build the data to chart # if ($StorageHistory.Count -eq 1) { $data = $StorageHistory[0] $seriesName = "SuccessIoCount" [void]$chart.Series.Add($seriesName) $chart.Series[$seriesName].ChartType = [Windows.Forms.DataVisualization.Charting.SeriesChartType]::StackedColumn $chart.Series[$seriesName].XValueType = [Windows.Forms.DataVisualization.Charting.ChartValueType]::String $chart.Series[$seriesName].Legend = "Summary" $chart.Series[$seriesName].Color = [Drawing.Color]::Green for ($index = 0; $index -lt $data.BucketCount; $index++) { $point = New-Object Windows.Forms.DataVisualization.Charting.DataPoint $point.SetValueXY([String]::Format('{0:N0}', $data.BucketIoLatency[$index] / 10), $data.BucketSuccessIoCount[$index]) $point.Tooltip = [String]::Format('Success: {0:N0}\nTotal: {1:N0}', $data.BucketSuccessIoCount[$index], $data.BucketTotalIoCount[$index]) $chart.Series[$seriesName].Points.Add($point) } $seriesName = "FailedIoCount" [void]$chart.Series.Add($seriesName) $chart.Series[$seriesName].ChartType = [Windows.Forms.DataVisualization.Charting.SeriesChartType]::StackedColumn $chart.Series[$seriesName].XValueType = [Windows.Forms.DataVisualization.Charting.ChartValueType]::String $chart.Series[$seriesName].Legend = "Summary" $chart.Series[$seriesName].Color = [Drawing.Color]::Red for ($index = 0; $index -lt $data.BucketCount; $index++) { $point = New-Object Windows.Forms.DataVisualization.Charting.DataPoint $point.SetValueXY([String]::Format('{0:N0}', $data.BucketIoLatency[$index] / 10), $data.BucketFailedIoCount[$index]) $point.Tooltip = [String]::Format('Failed: {0:N0}\nTotal: {1:N0}', $data.BucketFailedIoCount[$index], $data.BucketTotalIoCount[$index]) $chart.Series[$seriesName].Points.Add($point) } } else { if ($ShowDefault -eq $true) { $ShowAvgLatency = $true $ShowMaxLatency = $true } if ($ShowAvgLatency -eq $true) { $seriesName = "Avg" [void]$chart.Series.Add($seriesName) $chart.Series[$seriesName].ChartType = [Windows.Forms.DataVisualization.Charting.SeriesChartType]::Column $chart.Series[$seriesName].XValueType = [Windows.Forms.DataVisualization.Charting.ChartValueType]::String $chart.Series[$seriesName].Legend = "Summary" $chart.Series[$seriesName].Color = [Drawing.Color]::Green ForEach ($data in $StorageHistory) { $point = New-Object Windows.Forms.DataVisualization.Charting.DataPoint $point.SetValueXY($data.DeviceNumber + " : " + $data.SerialNumber, $data.AvgIoLatency / 10) $point.Tooltip = "Avg: #VALY{N0} us\n" + [String]::Format('Total Io Count: {0:N0}\nSuccess Io Count: {1:N0}\nFailed Io Count: {2:N0}', $data.TotalIoCount, $data.SuccessIoCount, $data.FailedIoCount) $chart.Series[$seriesName].Points.Add($point) } } if ($ShowMaxLatency -eq $true) { $seriesName = "Max" [void]$chart.Series.Add($seriesName) $chart.Series[$seriesName].ChartType = [Windows.Forms.DataVisualization.Charting.SeriesChartType]::Column $chart.Series[$seriesName].XValueType = [Windows.Forms.DataVisualization.Charting.ChartValueType]::String $chart.Series[$seriesName].Legend = "Summary" $chart.Series[$seriesName].Color = [Drawing.Color]::Orange ForEach ($data in $StorageHistory) { $point = New-Object Windows.Forms.DataVisualization.Charting.DataPoint $point.SetValueXY($data.DeviceNumber + " : " + $data.SerialNumber, $data.MaxIoLatency / 10) $point.Tooltip = "Max: #VALY{N0} us\n" + [String]::Format('Total Io Count: {0:N0}\nSuccess Io Count: {1:N0}\nFailed Io Count: {2:N0}', $data.TotalIoCount, $data.SuccessIoCount, $data.FailedIoCount) $chart.Series[$seriesName].Points.Add($point) } } $chart.ChartAreas[0].AxisX.LabelStyle.Angle = -45 } # # Display the chart on a form # $form = New-Object Windows.Forms.Form $form.Text = "Storage Performance Chart" $form.Width = 1100 $form.Height = 900 $form.controls.add($chart) $form.Add_Shown({$form.Activate()}) [void]$form.ShowDialog() $form.Close() $form.Dispose() [void]$StorageHistoryCharts.Host.Runspace.Events.GenerateEvent("StorageHistoryChart.Close", $null, $null, $ChartId) } $StorageHistoryChartClose = { $chartId = $event.MessageData $runspace = $Global:StorageHistoryCharts[$chartId] $runspace.Close() $runspace.Dispose() $Global:StorageHistoryCharts.Remove($chartId) } function Show-StorageHistory { Param ( #### -------------------- Parameter sets -------------------------------------#### [System.Collections.ArrayList] [Parameter( ParameterSetName = 'ByObjects', ValueFromPipeline = $false, Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] $Objects, #### ------------------------- Common method parameters ----------------------#### [System.String] [Parameter( ParameterSetName = 'ByObjects', Mandatory = $false)] [ValidateNotNullOrEmpty()] $Title, [Switch] [Parameter( ParameterSetName = 'ByObjects', Mandatory = $false)] $DisplayAvgLatency, [Switch] [Parameter( ParameterSetName = 'ByObjects', Mandatory = $false)] $DisplayMaxLatency ) Begin { } Process { if ($PSEdition -ne "Desktop") { # # Full PowerShell is required for charting # $errorObject = CreateErrorRecord -ErrorId "NotSupported" ` -ErrorMessage "This cmdlet requires full powershell support" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::NotEnabled) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } # # Global state initialization # Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Windows.Forms.DataVisualization $Global:StorageHistoryCharts.Host = $Host $subscriber = Get-EventSubscriber -SourceIdentifier "StorageHistoryChart.Close" ` -ErrorAction SilentlyContinue if ($subscriber -eq $null) { Register-EngineEvent -SourceIdentifier "StorageHistoryChart.Close" ` -Action $StorageHistoryChartClose | Out-Null } # # Display chart based on the data passed # $type = [String]::Empty $storageHistory = New-Object System.Collections.ArrayList ForEach ($object in $Objects) { if ([String]::IsNullOrEmpty($type)) { $type = $object.PSObject.TypeNames[0] } if ($object.PSObject.TypeNames[0] -eq $type) { [void]$storageHistory.Add($object) } } $chartId = [System.Guid]::newguid().ToString() $runspace = [RunspaceFactory]::CreateRunspace() $runspace.ApartmentState = "STA" $runspace.ThreadOptions = "ReuseThread" $runspace.Open() $runspace.SessionStateProxy.SetVariable("StorageHistoryCharts",$Global:StorageHistoryCharts) $Global:StorageHistoryCharts.Add($chartId, $runspace) $powershell = [PowerShell]::Create() $showDefault = $true $showAvgLatency = $false $showMaxLatency = $false if ($PSBoundParameters.ContainsKey("DisplayAvgLatency")) { $showDefault = $false $showAvgLatency = $true } if ($PSBoundParameters.ContainsKey("DisplayMaxLatency")) { $showDefault = $false $showMaxLatency = $true } if ($type -eq "Microsoft.Windows.StorageManagement.StorageHistory") { [void]$powershell.AddScript($DisplayTimeSeriesChart) } elseif ($type -eq "Microsoft.Windows.StorageManagement.StorageHistoryAggregate") { [void]$powershell.AddScript($DisplaySummaryChart) } else { return } [void]$powershell.AddArgument($chartId) [void]$powershell.AddArgument($Title) [void]$powershell.AddArgument($storageHistory) [void]$powershell.AddArgument($showDefault) [void]$powershell.AddArgument($showAvgLatency) [void]$powershell.AddArgument($showMaxLatency) $powershell.Runspace = $runspace [void]$powershell.BeginInvoke() } } function Enable-StorageDataCollection { Param ( #### -------------------- Parameter sets -------------------------------------#### [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_PhysicalDisk")] [Parameter( ParameterSetName = 'ByPhysicalDisk', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $PhysicalDisk, [System.String] [Parameter( ParameterSetName = 'ByDeviceGuid', ValueFromPipeline = $false, Mandatory = $true)] [ValidateNotNullOrEmpty()] [Alias("DeviceId")] $DeviceGuid, [System.String] [Parameter( ParameterSetName = 'ByDeviceNumber', ValueFromPipeline = $false, Mandatory = $true)] [ValidateNotNullOrEmpty()] $DeviceNumber, #### -------------------- Common method parameters ---------------------------#### [StorageDataCollectionType] [Parameter( ParameterSetName = 'ByPhysicalDisk', Mandatory = $true)] [Parameter( ParameterSetName = 'ByDeviceGuid', Mandatory = $true)] [Parameter( ParameterSetName = 'ByDeviceNumber', Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageDataCollectionType ) Begin { [flagsattribute()] Enum StorageDataCollectionType { StorageIoDistribution = 0 } } Process { # # Get the physical disk based on the parameter set # try { if ($DeviceNumber) { $PhysicalDisk = Get-PhysicalDisk -DeviceNumber $DeviceNumber } elseif ($DeviceGuid) { $PhysicalDisk = Get-PhysicalDisk | ? ObjectId -Match $DeviceGuid } } catch { $errorObject = CreateErrorRecord -ErrorId "ObjectNotFound" ` -ErrorMessage "A physical disk matching the input criteria was not found" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } $subsystem = $PhysicalDisk | get-storagesubsystem if ($subsystem.Model -eq "Clustered Windows Storage" -and $subsystem.StorageConnectionType -eq "Local Storage") { # If the subsystem is clustered local (storage spaces direct) use # SSU mapping to determine the node where the drive is connected $storageScaleUnit = $PhysicalDisk | Get-StorageScaleUnit if (-not $storageScaleUnit) { $errorObject = CreateErrorRecord -ErrorId "ObjectNotFound" ` -ErrorMessage "A storage scale unit was not found for the physical disk" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } $computerName = $storageScaleUnit.FriendlyName $number = $PhysicalDisk.DeviceId % 1000 } else { $storageNode = $PhysicalDisk | Get-StorageNode -PhysicallyConnected if (-not $storageNode) { $errorObject = CreateErrorRecord -ErrorId "ObjectNotFound" ` -ErrorMessage "A storage node was not found for the physical disk" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } $computerName = $storageNode.Name $number = $PhysicalDisk.DeviceId } # Allow only when the physical disk is locally connected if ($computerName -ne [System.Net.Dns]::GetHostByName(($env:computerName)).HostName) { $errorObject = CreateErrorRecord -ErrorId "NotSupported" ` -ErrorMessage "The operation is supported only on the machine where the physical disk is locally connected" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::NotEnabled) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } try { $devicePath = "\\.\PhysicalDrive" + $number if ($StorageDataCollectionType -eq [StorageDataCollectionType]::StorageIoDistribution) { [Core.DeviceMgmt]::EnableStorageIoDistribution($devicePath) | Out-Null } } catch { $errorObject = CreateErrorRecord -ErrorId $_.FullyQualifiedErrorId ` -ErrorMessage $null ` -ErrorCategory $_.CategoryInfo.Category ` -Exception $_.Exception ` -TargetObject $_.TargetObject $psCmdlet.WriteError($errorObject) return } } } function Disable-StorageDataCollection { Param ( #### -------------------- Parameter sets -------------------------------------#### [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_PhysicalDisk")] [Parameter( ParameterSetName = 'ByPhysicalDisk', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $PhysicalDisk, [System.String] [Parameter( ParameterSetName = 'ByDeviceGuid', ValueFromPipeline = $false, Mandatory = $true)] [ValidateNotNullOrEmpty()] [Alias("DeviceId")] $DeviceGuid, [System.String] [Parameter( ParameterSetName = 'ByDeviceNumber', ValueFromPipeline = $false, Mandatory = $true)] [ValidateNotNullOrEmpty()] $DeviceNumber, #### -------------------- Common method parameters ---------------------------#### [StorageDataCollectionType] [Parameter( ParameterSetName = 'ByPhysicalDisk', Mandatory = $true)] [Parameter( ParameterSetName = 'ByDeviceGuid', Mandatory = $true)] [Parameter( ParameterSetName = 'ByDeviceNumber', Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageDataCollectionType ) Begin { [flagsattribute()] Enum StorageDataCollectionType { StorageIoDistribution = 0 } } Process { # # Get the physical disk based on the parameter set # try { if ($DeviceNumber) { $PhysicalDisk = Get-PhysicalDisk -DeviceNumber $DeviceNumber } elseif ($DeviceGuid) { $PhysicalDisk = Get-PhysicalDisk | ? ObjectId -Match $DeviceGuid } } catch { $errorObject = CreateErrorRecord -ErrorId "ObjectNotFound" ` -ErrorMessage "A physical disk matching the input criteria was not found" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } $subsystem = $PhysicalDisk | get-storagesubsystem if ($subsystem.Model -eq "Clustered Windows Storage" -and $subsystem.StorageConnectionType -eq "Local Storage") { # If the subsystem is clustered local (storage spaces direct) use # SSU mapping to determine the node where the drive is connected $storageScaleUnit = $PhysicalDisk | Get-StorageScaleUnit if (-not $storageScaleUnit) { $errorObject = CreateErrorRecord -ErrorId "ObjectNotFound" ` -ErrorMessage "A storage scale unit was not found for the physical disk" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } $computerName = $storageScaleUnit.FriendlyName $number = $PhysicalDisk.DeviceId % 1000 } else { $storageNode = $PhysicalDisk | Get-StorageNode -PhysicallyConnected if (-not $storageNode) { $errorObject = CreateErrorRecord -ErrorId "ObjectNotFound" ` -ErrorMessage "A storage node was not found for the physical disk" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } $computerName = $storageNode.Name $number = $PhysicalDisk.DeviceId } # Allow only when the physical disk is locally connected if ($computerName -ne [System.Net.Dns]::GetHostByName(($env:computerName)).HostName) { $errorObject = CreateErrorRecord -ErrorId "NotSupported" ` -ErrorMessage "The operation is supported only on the machine where the physical disk is locally connected" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::NotEnabled) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } try { $devicePath = "\\.\PhysicalDrive" + $number if ($StorageDataCollectionType -eq [StorageDataCollectionType]::StorageIoDistribution) { [Core.DeviceMgmt]::DisableStorageIoDistribution($devicePath) | Out-Null } } catch { $errorObject = CreateErrorRecord -ErrorId $_.FullyQualifiedErrorId ` -ErrorMessage $null ` -ErrorCategory $_.CategoryInfo.Category ` -Exception $_.Exception ` -TargetObject $_.TargetObject $psCmdlet.WriteError($errorObject) return } } } function Save-StorageDataCollection { Param ( #### -------------------- Parameter sets -------------------------------------#### [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_PhysicalDisk")] [Parameter( ParameterSetName = 'ByPhysicalDisk', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $PhysicalDisk, [System.String] [Parameter( ParameterSetName = 'ByDeviceGuid', ValueFromPipeline = $false, Mandatory = $true)] [ValidateNotNullOrEmpty()] [Alias("DeviceId")] $DeviceGuid, [System.String] [Parameter( ParameterSetName = 'ByDeviceNumber', ValueFromPipeline = $false, Mandatory = $true)] [ValidateNotNullOrEmpty()] $DeviceNumber, #### -------------------- Common method parameters ---------------------------#### [StorageDataCollectionType] [Parameter( ParameterSetName = 'ByPhysicalDisk', Mandatory = $true)] [Parameter( ParameterSetName = 'ByDeviceGuid', Mandatory = $true)] [Parameter( ParameterSetName = 'ByDeviceNumber', Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageDataCollectionType ) Begin { [flagsattribute()] Enum StorageDataCollectionType { StorageIoDistribution = 0 } } Process { # # Get the physical disk based on the parameter set # try { if ($DeviceNumber) { $PhysicalDisk = Get-PhysicalDisk -DeviceNumber $DeviceNumber } elseif ($DeviceGuid) { $PhysicalDisk = Get-PhysicalDisk | ? ObjectId -Match $DeviceGuid } } catch { $errorObject = CreateErrorRecord -ErrorId "ObjectNotFound" ` -ErrorMessage "A physical disk matching the input criteria was not found" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } $subsystem = $PhysicalDisk | get-storagesubsystem if ($subsystem.Model -eq "Clustered Windows Storage" -and $subsystem.StorageConnectionType -eq "Local Storage") { # If the subsystem is clustered local (storage spaces direct) use # SSU mapping to determine the node where the drive is connected $storageScaleUnit = $PhysicalDisk | Get-StorageScaleUnit if (-not $storageScaleUnit) { $errorObject = CreateErrorRecord -ErrorId "ObjectNotFound" ` -ErrorMessage "A storage scale unit was not found for the physical disk" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } $computerName = $storageScaleUnit.FriendlyName $number = $PhysicalDisk.DeviceId % 1000 } else { $storageNode = $PhysicalDisk | Get-StorageNode -PhysicallyConnected if (-not $storageNode) { $errorObject = CreateErrorRecord -ErrorId "ObjectNotFound" ` -ErrorMessage "A storage node was not found for the physical disk" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } $computerName = $storageNode.Name $number = $PhysicalDisk.DeviceId } # Allow only when the physical disk is locally connected if ($computerName -ne [System.Net.Dns]::GetHostByName(($env:computerName)).HostName) { $errorObject = CreateErrorRecord -ErrorId "NotSupported" ` -ErrorMessage "The operation is supported only on the machine where the physical disk is locally connected" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::NotEnabled) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } try { $devicePath = "\\.\PhysicalDrive" + $number if ($StorageDataCollectionType -eq [StorageDataCollectionType]::StorageIoDistribution) { [Core.DeviceMgmt]::LogStorageIoDistribution($devicePath) | Out-Null } } catch { $errorObject = CreateErrorRecord -ErrorId $_.FullyQualifiedErrorId ` -ErrorMessage $null ` -ErrorCategory $_.CategoryInfo.Category ` -Exception $_.Exception ` -TargetObject $_.TargetObject $psCmdlet.WriteError($errorObject) return } } } function Get-StorageDataCollection { Param ( #### -------------------- Parameter sets -------------------------------------#### [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_PhysicalDisk")] [Parameter( ParameterSetName = 'ByPhysicalDisk', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $PhysicalDisk, [System.String] [Parameter( ParameterSetName = 'ByDeviceGuid', ValueFromPipeline = $false, Mandatory = $true)] [ValidateNotNullOrEmpty()] [Alias("DeviceId")] $DeviceGuid, [System.String] [Parameter( ParameterSetName = 'ByDeviceNumber', ValueFromPipeline = $false, Mandatory = $true)] [ValidateNotNullOrEmpty()] $DeviceNumber, #### -------------------- Common method parameters ---------------------------#### [StorageDataCollectionType] [Parameter( ParameterSetName = 'ByPhysicalDisk', Mandatory = $true)] [Parameter( ParameterSetName = 'ByDeviceGuid', Mandatory = $true)] [Parameter( ParameterSetName = 'ByDeviceNumber', Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageDataCollectionType, [UInt32] [Parameter( ParameterSetName = 'ByPhysicalDisk', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceGuid', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceNumber', Mandatory = $false)] [ValidateNotNullOrEmpty()] $Count = 1, [Switch] [Parameter( ParameterSetName = 'ByPhysicalDisk', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceGuid', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceNumber', Mandatory = $false)] [ValidateNotNullOrEmpty()] $Oldest ) Begin { [flagsattribute()] Enum StorageDataCollectionType { StorageIoDistribution = 0 } } Process { # # Get the physical disk based on the parameter set # try { if ($DeviceNumber) { $PhysicalDisk = Get-PhysicalDisk -DeviceNumber $DeviceNumber } elseif ($DeviceGuid) { $PhysicalDisk = Get-PhysicalDisk | ? ObjectId -Match $DeviceGuid } } catch { $errorObject = CreateErrorRecord -ErrorId "ObjectNotFound" ` -ErrorMessage "A physical disk matching the input criteria was not found" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } $subsystem = $PhysicalDisk | get-storagesubsystem if ($subsystem.Model -eq "Clustered Windows Storage" -and $subsystem.StorageConnectionType -eq "Local Storage") { # If the subsystem is clustered local (storage spaces direct) use # SSU mapping to determine the node where the drive is connected $storageScaleUnit = $PhysicalDisk | Get-StorageScaleUnit if (-not $storageScaleUnit) { $errorObject = CreateErrorRecord -ErrorId "ObjectNotFound" ` -ErrorMessage "A storage scale unit was not found for the physical disk" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } $computerName = $storageScaleUnit.FriendlyName $number = $PhysicalDisk.DeviceId % 1000 } else { $storageNode = $PhysicalDisk | Get-StorageNode -PhysicallyConnected if (-not $storageNode) { $errorObject = CreateErrorRecord -ErrorId "ObjectNotFound" ` -ErrorMessage "A storage node was not found for the physical disk" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } $computerName = $storageNode.Name $number = $PhysicalDisk.DeviceId } # Allow only when the physical disk is locally connected if ($computerName -ne [System.Net.Dns]::GetHostByName(($env:computerName)).HostName) { $errorObject = CreateErrorRecord -ErrorId "NotSupported" ` -ErrorMessage "The operation is supported only on the machine where the physical disk is locally connected" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::NotEnabled) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } # Extract the device guid from the object id # It is of the format ' <>:PD:<DeviceGuid>" ' $guid = $physicalDisk.ObjectId.Split(":")[2].TrimEnd('"') $showOldest = $false if ($PSBoundParameters.ContainsKey("Oldest")) { $showOldest = $true } if ($StorageDataCollectionType -eq [StorageDataCollectionType]::StorageIoDistribution) { # # Build the query to look for the latest storage io distribution event in storport log # $query = "*[System[(EventID=563)]] and *[EventData[Data[@Name='ClassDeviceGuid'] and (Data='$guid')]]" if ($showOldest -eq $true) { $events = Get-WinEvent -LogName Microsoft-Windows-Storage-Storport/Operational ` -FilterXPath $query ` -MaxEvents $Count ` -Oldest ` -ErrorAction Stop } else { $events = Get-WinEvent -LogName Microsoft-Windows-Storage-Storport/Operational ` -FilterXPath $query ` -MaxEvents $Count ` -ErrorAction Stop } if ($events.count -eq 0) { return } foreach ($event in $events) { $eventXml = [XML]$event.ToXml() $tempObject = [PsCustomObject]@{} for ($index = 0; $index -lt $eventXml.Event.EventData.Data.Count; $index++) { $tempObject | Add-Member -MemberType NoteProperty ` -Name $eventXml.Event.EventData.Data[$index].Name ` -Value $eventXml.Event.EventData.Data[$index].'#Text' } $ioTypes = @("Read", "Write") $ioSizes = $tempObject.IoSizeBuckets.Split(", ", [System.StringSplitOptions]::RemoveEmptyEntries) $ioLatencies = $tempObject.IoLatencyBuckets.Split(", ", [System.StringSplitOptions]::RemoveEmptyEntries) foreach ($iotype in $ioTypes) { for ($sizeIndex = 0; $sizeIndex -lt $ioSizes.count; $sizeIndex++) { $countsField = $iotype + "IoSizeBucket" + [System.String]($sizeIndex + 1) + "IoSuccess" $counts = $($tempObject.$countsField).Split(", ", [System.StringSplitOptions]::RemoveEmptyEntries) [StorageIoDistribution]$countsData = [StorageIoDistribution]::new() $countsData.PSObject.TypeNames.Insert(0, "Microsoft.Windows.StorageManagement.StorageIoDistribution") $countsData.Version = $tempObject.Version $countsData.EventTime = $event.TimeCreated $countsData.TotalSuccessCount = $tempObject.TotalSuccessIoCount $countsData.IoType = $iotype $countsData.IoSize = $ioSizes[$sizeIndex] $countsData.IoSizeSuccessCount = 0 for ($latencyIndex = 0; $latencyIndex -lt $ioLatencies.count; $latencyIndex++) { $fieldName = "LatencySuccessCount" + [System.String]($latencyIndex + 1) $countsData.$fieldName = [UInt64]$Counts[$latencyIndex] $countsData.IoSizeSuccessCount += [UInt64]$Counts[$latencyIndex] } $psCmdlet.WriteObject($countsData) } } } } } } function Get-PhysicalDisk { [CmdletBinding( DefaultParameterSetName = "ByUniqueId" )] Param ( #### -------------------- Parameter sets -------------------------------------#### [System.String] [Parameter( ParameterSetName = 'ByUniqueId', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $false)] [ValidateNotNullOrEmpty()] [Alias("Id")] $UniqueId, [System.String] [Parameter( ParameterSetName = 'ByObjectId', ValueFromPipeline = $true, Mandatory = $false)] [ValidateNotNullOrEmpty()] [Alias("PhysicalDiskObjectId")] $ObjectId, [System.String] [Parameter( ParameterSetName = 'ByDeviceNumber', ValueFromPipeline = $false, Mandatory = $false)] [ValidateNotNullOrEmpty()] $DeviceNumber, [System.String] [Parameter( ParameterSetName = 'ByName', ValueFromPipeline = $true, Mandatory = $false, Position = 0)] [ValidateNotNullOrEmpty()] $FriendlyName, [System.String] [Parameter( ParameterSetName = 'ByName', ValueFromPipeline = $true, Mandatory = $false, Position = 1)] [ValidateNotNullOrEmpty()] $SerialNumber, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_PhysicalDisk")] [Parameter( ParameterSetName = 'ByInputObject', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $InputObject, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageSubsystem")] [Parameter( ParameterSetName = 'ByStorageSubsystem', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageSubsystem, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageEnclosure")] [Parameter( ParameterSetName = 'ByStorageEnclosure', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageEnclosure, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageScaleUnit")] [Parameter( ParameterSetName = 'ByStorageScaleUnit', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageScaleUnit, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageChassis")] [Parameter( ParameterSetName = 'ByStorageChassis', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageChassis, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageRack")] [Parameter( ParameterSetName = 'ByStorageRack', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageRack, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageSite")] [Parameter( ParameterSetName = 'ByStorageSite', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageSite, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageNode")] [Parameter( ParameterSetName = 'ByStorageNode', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageNode, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StoragePool")] [Parameter( ParameterSetName = 'ByStoragePool', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StoragePool, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_VirtualDisk")] [Parameter( ParameterSetName = 'ByVirtualDisk', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $VirtualDisk, #### ------------------ VirtualDisk association parameters -------------------#### [System.UInt64] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] $VirtualRangeMin, [System.UInt64] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] $VirtualRangeMax, [System.Boolean] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] $HasAllocations, [System.Boolean] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] $SelectedForUse, [Switch] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] $NoRedundancy, #### --------- StoragePool and VirtualDisk association parameters ------------#### [Switch] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] $HasMetadata, #### ------------------ StorageNode association parameters -------------------#### [Switch] [Parameter( ParameterSetName = 'ByStorageNode', Mandatory = $false)] $PhysicallyConnected, #### ------------------------- Common parameters -----------------------------#### [PhysicalDiskUsage] [Parameter( ParameterSetName = 'ByUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByObjectId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceNumber', Mandatory = $false)] [Parameter( ParameterSetName = 'ByName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageEnclosure', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageScaleUnit', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageChassis', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageRack', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSite', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageNode', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] $Usage, [System.String] [Parameter( ParameterSetName = 'ByUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByObjectId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceNumber', Mandatory = $false)] [Parameter( ParameterSetName = 'ByName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageEnclosure', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageScaleUnit', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageChassis', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageRack', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSite', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageNode', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] [ValidateNotNullOrEmpty()] $Description, [System.String] [Parameter( ParameterSetName = 'ByUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByObjectId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceNumber', Mandatory = $false)] [Parameter( ParameterSetName = 'ByName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageEnclosure', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageScaleUnit', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageChassis', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageRack', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSite', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageNode', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] [ValidateNotNullOrEmpty()] $Manufacturer, [System.String] [Parameter( ParameterSetName = 'ByUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByObjectId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceNumber', Mandatory = $false)] [Parameter( ParameterSetName = 'ByName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageEnclosure', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageScaleUnit', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageChassis', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageRack', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSite', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageNode', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] [ValidateNotNullOrEmpty()] $Model, [System.Boolean] [Parameter( ParameterSetName = 'ByUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByObjectId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceNumber', Mandatory = $false)] [Parameter( ParameterSetName = 'ByName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageEnclosure', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageScaleUnit', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageChassis', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageRack', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSite', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageNode', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] $CanPool, [PhysicalDiskHealthStatus] [Parameter( ParameterSetName = 'ByUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByObjectId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDeviceNumber', Mandatory = $false)] [Parameter( ParameterSetName = 'ByName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageEnclosure', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageScaleUnit', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageChassis', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageRack', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSite', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageNode', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] $HealthStatus, [Microsoft.Management.Infrastructure.CimSession] $CimSession ) Begin { [flagsattribute()] Enum PhysicalDiskUsage { Unknown = 0 AutoSelect = 1 ManualSelect = 2 HotSpare = 3 Retired = 4 Journal = 5 } [flagsattribute()] Enum PhysicalDiskHealthStatus { Healthy = 0 Warning = 1 Unhealthy = 2 Unknown = 5 } } Process { if (-not $CimSession) { $CimSession = New-CimSession } ## Gather the instance objects. $options = New-Object Microsoft.Management.Infrastructure.Options.CimOperationOptions switch -regex ($psCmdlet.ParameterSetName) { { @( ` "ByInputObject" ` ) -contains $_ ` } { $cimInstance = New-Object Microsoft.Management.Infrastructure.CimInstance("MSFT_PhysicalDisk") $cimInstance.CimInstanceProperties.Add([Microsoft.Management.Infrastructure.CimProperty]::Create("ObjectId", $InputObject.ObjectId, "String", "Key")) $instance = $CimSession.GetInstance($StorageNamespace, $cimInstance); break; } { @( ` "ByObjectId", ` "ByUniqueId", ` "ByDeviceNumber", ` "ByName" ` ) -contains $_ ` } { $results = $CimSession.EnumerateInstances($StorageNamespace, "MSFT_PhysicalDisk"); break; } { @( ` "ByStorageSubsystem", ` "ByStoragePool", ` "ByStorageEnclosure", ` "ByStorageNode", ` "ByVirtualDisk" ` ) -contains $_ ` } { if ($PSBoundParameters.ContainsKey("StorageSubsystem")) { $subsystem = $StorageSubsystem } elseif ($PSBoundParameters.ContainsKey("StoragePool")) { $subsystem = $StoragePool | get-storagesubsystem -CimSession $CimSession } elseif ($PSBoundParameters.ContainsKey("StorageEnclosure")) { $subsystem = $StorageEnclosure | get-storagesubsystem -CimSession $CimSession } elseif ($PSBoundParameters.ContainsKey("StorageNode")) { $subsystem = $StorageNode | get-storagesubsystem -CimSession $CimSession } elseif ($PSBoundParameters.ContainsKey("VirtualDisk")) { $subsystem = $VirtualDisk | get-storagesubsystem -CimSession $CimSession } # If the subsystem model is "Windows Storage", # perform associations using enumeration if ($PSBoundParameters.ContainsKey("StorageSubsystem")) { $options.SetCustomOption("InputClassName", "MSFT_StorageSubsystem", $false) $options.SetCustomOption("InputObjectId", $StorageSubsystem.ObjectId, $false) if ($subsystem.Model -like "*Windows Storage*") { $results = $CimSession.EnumerateInstances($StorageNamespace, "MSFT_PhysicalDisk", $options) } else { $results = $CimSession.EnumerateAssociatedInstances($StorageNamespace, $StorageSubsystem, "MSFT_StorageSubsystemToPhysicalDisk", "MSFT_PhysicalDisk", "StorageSubsystem", "PhysicalDisk", $options) } } elseif ($PSBoundParameters.ContainsKey("StoragePool")) { $options.SetCustomOption("InputClassName", "MSFT_StoragePool", $false) $options.SetCustomOption("InputObjectId", $StoragePool.ObjectId, $false) if ($PSBoundParameters.ContainsKey("HasMetadata")) { $options.SetCustomOption("HasMetadata", $HasMetadata, $false) } if ($subsystem.Model -like "*Windows Storage*") { $results = $CimSession.EnumerateInstances($StorageNamespace, "MSFT_PhysicalDisk", $options) } else { $results = $CimSession.EnumerateAssociatedInstances($StorageNamespace, $StoragePool, "MSFT_StoragePoolToPhysicalDisk", "MSFT_PhysicalDisk", "StoragePool", "PhysicalDisk", $options) } } elseif ($PSBoundParameters.ContainsKey("StorageEnclosure")) { $options.SetCustomOption("InputClassName", "MSFT_StorageEnclosure", $false) $options.SetCustomOption("InputObjectId", $StorageEnclosure.ObjectId, $false) if ($subsystem.Model -like "*Windows Storage*") { $results = $CimSession.EnumerateInstances($StorageNamespace, "MSFT_PhysicalDisk", $options); } else { $results = $CimSession.EnumerateAssociatedInstances($StorageNamespace, $StorageEnclosure, "MSFT_StorageEnclosureToPhysicalDisk", "MSFT_PhysicalDisk", "StorageEnclosure", "PhysicalDisk", $options); } } elseif ($PSBoundParameters.ContainsKey("StorageNode")) { $options.SetCustomOption("InputClassName", "MSFT_StorageNode", $false) $options.SetCustomOption("InputObjectId", $StorageNode.ObjectId, $false) if ($PSBoundParameters.ContainsKey("PhysicallyConnected")) { $options.SetCustomOption("PhysicallyConnected", $true, $false) } if ($subsystem.Model -like "*Windows Storage*") { $results = $CimSession.EnumerateInstances($StorageNamespace, "MSFT_PhysicalDisk", $options); } else { $results = $CimSession.EnumerateAssociatedInstances($StorageNamespace, $StorageNode, "MSFT_StorageNodeToPhysicalDisk", "MSFT_PhysicalDisk", "StorageNode", "PhysicalDisk", $options); } } elseif ($PSBoundParameters.ContainsKey("VirtualDisk")) { $options.SetCustomOption("InputClassName", "MSFT_VirtualDisk", $false) $options.SetCustomOption("InputObjectId", $VirtualDisk.ObjectId, $false) if ($PSBoundParameters.ContainsKey("VirtualRangeMin")) { $options.SetCustomOption("VirtualRangeMin", $VirtualRangeMin, [Microsoft.Management.Infrastructure.CimType]::UInt64, $false) } if ($PSBoundParameters.ContainsKey("VirtualRangeMax")) { $options.SetCustomOption("VirtualRangeMax", $VirtualRangeMax, [Microsoft.Management.Infrastructure.CimType]::UInt64, $false) } if ($PSBoundParameters.ContainsKey("HasAllocations")) { $options.SetCustomOption("HasAllocations", $HasAllocations, $false) } if ($PSBoundParameters.ContainsKey("SelectedForUse")) { $options.SetCustomOption("SelectedForUse", $SelectedForUse, $false) } if ($PSBoundParameters.ContainsKey("HasMetadata")) { $options.SetCustomOption("HasMetadata", $HasMetadata, $false) } if ($PSBoundParameters.ContainsKey("NoRedundancy")) { $options.SetCustomOption("NoRedundancy", $true, $false) } if ($subsystem.Model -like "*Windows Storage*") { $results = $CimSession.EnumerateInstances($StorageNamespace, "MSFT_PhysicalDisk", $options); } else { $results = $CimSession.EnumerateAssociatedInstances($StorageNamespace, $VirtualDisk, "MSFT_VirtualDiskToPhysicalDisk", "MSFT_PhysicalDisk", "VirtualDisk", "PhysicalDisk", $options); } } break; } { @( ` "ByStorageScaleUnit", ` "ByStorageChassis", ` "ByStorageRack", ` "ByStorageSite" ` ) -contains $_ ` } { if ($PSBoundParameters.ContainsKey("StorageScaleUnit")) { $inputObject = $StorageScaleUnit } elseif ($PSBoundParameters.ContainsKey("StorageChassis")) { $inputObject = $StorageChassis } elseif ($PSBoundParameters.ContainsKey("StorageRack")) { $inputObject = $StorageRack } elseif ($PSBoundParameters.ContainsKey("StorageSite")) { $inputObject = $StorageSite } # If the subsystem model is "Windows Storage", # perform associations using enumeration $subsystem = $inputObject | get-storagesubsystem -CimSession $CimSession if ($subsystem.Model -like "*Windows Storage*") { $options.SetCustomOption("InputClassName", $inputObject.ClassName, $false) $options.SetCustomOption("InputObjectId", $inputObject.ObjectId, $false) $results = $CimSession.EnumerateInstances($StorageNamespace, "MSFT_PhysicalDisk", $options); } else { $options.SetCustomOption("ResultObject", "MSFT_PhysicalDisk", $false) $results = $CimSession.EnumerateAssociatedInstances($StorageNamespace, $inputObject, "MSFT_StorageFaultDomainToStorageFaultDomain", "MSFT_PhysicalDisk", "SourceStorageFaultDomain", "TargetStorageFaultDomain", $options); } break; } } $instances = @() if ($psCmdlet.ParameterSetName -eq "ByInputObject") { $instances += $instance } else { $enumerator = $results.GetEnumerator() while ($enumerator.MoveNext()) { $instance = $enumerator.Current ## Filter by optional input parameters if (($PSBoundParameters.ContainsKey("ObjectId")) -and ($instance.ObjectId -notlike $ObjectId)) { continue } if (($PSBoundParameters.ContainsKey("UniqueId")) -and ($instance.UniqueId -notlike $UniqueId)) { continue } if (($PSBoundParameters.ContainsKey("DeviceNumber")) -and ($instance.DeviceId -ne $DeviceNumber)) { continue } if (($PSBoundParameters.ContainsKey("FriendlyName")) -and ($instance.FriendlyName -notlike $FriendlyName)) { continue } if (($PSBoundParameters.ContainsKey("SerialNumber")) -and ($instance.SerialNumber -notlike $SerialNumber)) { continue } if ($PSBoundParameters.ContainsKey("Usage")) { $matchFound = $true switch -regex ($Usage) { { @( ` "Unknown", ` "Retired", ` "Journal" ` ) -contains $_ ` } { if ( $instance.Usage -ne $Usage ) { $matchFound = $false break } } { @( ` "AutoSelect" ` ) -contains $_ ` } { if ( $instance.Usage -ne "Auto-Select" ) { $matchFound = $false break } } { @( ` "ManualSelect" ` ) -contains $_ ` } { if ( $instance.Usage -ne "Manual-Select" ) { $matchFound = $false break } } { @( ` "HotSpare" ` ) -contains $_ ` } { if ( $instance.Usage -ne "Hot Spare" ) { $matchFound = $false break } } } if ( $matchFound -eq $false ) { continue } } if (($PSBoundParameters.ContainsKey("Description")) -and ($instance.Description -notlike $Description)) { continue } if (($PSBoundParameters.ContainsKey("Manufacturer")) -and ($instance.Manufacturer -ne $Manufacturer)) { continue } if (($PSBoundParameters.ContainsKey("Model")) -and ($instance.Model -ne $Model)) { continue } if (($PSBoundParameters.ContainsKey("CanPool")) -and ($instance.CanPool -ne $CanPool)) { continue } if (($PSBoundParameters.ContainsKey("HealthStatus")) -and ($instance.HealthStatus -ne $HealthStatus)) { continue } $instances += $instance } } $instances } } function Get-PhysicalExtent { Param ( #### -------------------- Parameter sets -------------------------------------#### [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_VirtualDisk")] [Parameter( ParameterSetName = 'ByVirtualDisk', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $VirtualDisk, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageTier")] [Parameter( ParameterSetName = 'ByStorageTier', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageTier, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_PhysicalDisk")] [Parameter( ParameterSetName = 'ByPhysicalDisk', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $PhysicalDisk, #### -------------------- Powershell parameters ------------------------------#### [Microsoft.Management.Infrastructure.CimSession] $CimSession ) Process { if (-not $CimSession) { $CimSession = New-CimSession } ## Gather the instance objects switch ($psCmdlet.ParameterSetName) { "ByVirtualDisk" { $inputObject = $VirtualDisk; break; } "ByStorageTier" { $inputObject = $StorageTier; break; } "ByPhysicalDisk" { $inputObject = $PhysicalDisk; break; } } $instances = @() try { $output = Invoke-CimMethod -CimSession $CimSession -InputObject $inputObject -MethodName "GetPhysicalExtent" -ErrorAction Stop $enumerator = $output.PhysicalExtents.GetEnumerator() while ($enumerator.MoveNext()) { $instance = $enumerator.Current $instance.PSObject.TypeNames.Insert(0, "Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_PhysicalExtent") $instances += $instance } } catch { $errorObject = CreateErrorRecord -ErrorId $_.FullyQualifiedErrorId ` -ErrorMessage $null ` -ErrorCategory $_.CategoryInfo.Category ` -Exception $_.Exception ` -TargetObject $_.TargetObject $psCmdlet.WriteError($errorObject) } $instances } } function Get-PhysicalExtentAssociation { Param ( #### -------------------- Parameter sets -------------------------------------#### [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_PhysicalExtent")] [Parameter( ParameterSetName = 'ByInputObject', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $InputObject, #### -------------------- Powershell parameters ------------------------------#### [Microsoft.Management.Infrastructure.CimSession] $CimSession ) Process { if (-not $CimSession) { $CimSession = New-CimSession } $virtualDisk = Get-VirtualDisk -CimSession $CimSession -UniqueId $InputObject.VirtualDiskUniqueId -ErrorAction Stop if ($InputObject.StorageTierUniqueId -ne $null) { $storageTier = Get-StorageTier -CimSession $CimSession -UniqueId $InputObject.StorageTierUniqueId -ErrorAction Stop } $physicalDisk = Get-PhysicalDisk -CimSession $CimSession -UniqueId $InputObject.PhysicalDiskUniqueId -ErrorAction Stop $associations = [pscustomobject]@{ VirtualDisk = $virtualDisk; StorageTier = $storageTier; PhysicalDisk = $physicalDisk; } $associations } } function Enable-PhysicalDiskIdentification { [CmdletBinding( DefaultParameterSetName = "ByName", ConfirmImpact="Low" )] [Alias("Enable-PhysicalDiskIndication")] Param ( #### -------------------- Parameter sets -------------------------------------#### [System.String] [Parameter( ParameterSetName = 'ByUniqueId', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] [Alias("Id")] $UniqueId, [System.String] [Parameter( ParameterSetName = 'ByName', ValueFromPipeline = $true, Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] $FriendlyName, [Microsoft.Management.Infrastructure.CimInstance[]] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_PhysicalDisk")] [Parameter( ParameterSetName = 'ByInputObject', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $InputObject, #### ------------------------- Common method parameters ----------------------#### [Microsoft.Management.Infrastructure.CimSession] $CimSession, # Provided for compatibility with CDXML cmdlets, not actually used. [Int32] $ThrottleLimit, [Switch] $AsJob ) Begin { $SourceCaller = "Microsoft.PowerShell" } Process { if (-not $CimSession) { $CimSession = New-CimSession } if (-not $AsJob) { Write-Progress -Activity "Enable-PhysicalDiskIdentification" -PercentComplete 0 -CurrentOperation "Gathering objects" -Status "0/2" } ## Gather the instance objects switch ($psCmdlet.ParameterSetName) { "ByInputObject" { $Instances = $InputObject; break; } "ByUniqueId" { $Instances = Get-PhysicalDisk -UniqueId $UniqueId -CimSession $CimSession -ErrorAction Stop; break; } "ByName" { $Instances = Get-PhysicalDisk -FriendlyName $FriendlyName -CimSession $CimSession -ErrorAction Stop; break; } } # Would use a closure here, but jobs are run in their own session state. $methodblock = { param($session, $asjob, $instances) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } $arguments = @{ "EnableIndication" = $true } $errorObject = $null if (-not $asjob) { Write-Progress -Activity "Enable-PhysicalDiskIdentification" -PercentComplete 30 -CurrentOperation "Executing method" -Status "1/2" } ForEach ( $instance in $instances ) { try { $progressPreference = "silentlyContinue" $output = Invoke-CimMethod -CimSession $session -InputObject $instance -MethodName Maintenance -Arguments $arguments -ErrorAction Stop $progressPreference = "Continue" } catch { $progressPreference = "Continue" $errorObject = CreateErrorRecord -ErrorId $_.FullyQualifiedErrorId ` -ErrorMessage $null ` -ErrorCategory $_.CategoryInfo.Category ` -Exception $_.Exception ` -TargetObject $_.TargetObject $psCmdlet.WriteError($errorObject) } } if (-not $asjob) { Write-Progress -Activity "Enable-PhysicalDiskIdentification" -Completed -Status "2/2" } } if ($asjob) { $p = $true Start-Job -Name EnablePhysicalDiskIdentification -ScriptBlock $methodblock -ArgumentList @($CimSession, $p, $Instances) } else { &$methodblock $CimSession $p $Instances } } } function Disable-PhysicalDiskIdentification { [CmdletBinding( DefaultParameterSetName = "ByName", ConfirmImpact="Low" )] [Alias("Disable-PhysicalDiskIndication")] Param ( #### -------------------- Parameter sets -------------------------------------#### [System.String] [Parameter( ParameterSetName = 'ByUniqueId', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] [Alias("Id")] $UniqueId, [System.String] [Parameter( ParameterSetName = 'ByName', ValueFromPipeline = $true, Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] $FriendlyName, [Microsoft.Management.Infrastructure.CimInstance[]] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_PhysicalDisk")] [Parameter( ParameterSetName = 'ByInputObject', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $InputObject, #### ------------------------- Common method parameters ----------------------#### [Microsoft.Management.Infrastructure.CimSession] $CimSession, # Provided for compatibility with CDXML cmdlets, not actually used. [Int32] $ThrottleLimit, [Switch] $AsJob ) Begin { $SourceCaller = "Microsoft.PowerShell" } Process { if (-not $CimSession) { $CimSession = New-CimSession } if (-not $AsJob) { Write-Progress -Activity "Disable-PhysicalDiskIdentification" -PercentComplete 0 -CurrentOperation "Gathering objects" -Status "0/2" } ## Gather the instance objects switch ($psCmdlet.ParameterSetName) { "ByInputObject" { $Instances = $InputObject; break; } "ByUniqueId" { $Instances = Get-PhysicalDisk -UniqueId $UniqueId -CimSession $CimSession -ErrorAction Stop; break; } "ByName" { $Instances = Get-PhysicalDisk -FriendlyName $FriendlyName -CimSession $CimSession -ErrorAction Stop; break; } } # Would use a closure here, but jobs are run in their own session state. $methodblock = { param($session, $asjob, $instances) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } $arguments = @{ "EnableIndication" = $false } $errorObject = $null if (-not $asjob) { Write-Progress -Activity "Disable-PhysicalDiskIdentification" -PercentComplete 30 -CurrentOperation "Executing method" -Status "1/2" } ForEach ( $instance in $instances ) { try { $progressPreference = "silentlyContinue" $output = Invoke-CimMethod -CimSession $session -InputObject $instance -MethodName Maintenance -Arguments $arguments -ErrorAction Stop $progressPreference = "Continue" } catch { $progressPreference = "Continue" $errorObject = CreateErrorRecord -ErrorId $_.FullyQualifiedErrorId ` -ErrorMessage $null ` -ErrorCategory $_.CategoryInfo.Category ` -Exception $_.Exception ` -TargetObject $_.TargetObject $psCmdlet.WriteError($errorObject) } } if (-not $asjob) { Write-Progress -Activity "Disable-PhysicalDiskIdentification" -Completed -Status "2/2" } } if ($asjob) { $p = $true Start-Job -Name DisablePhysicalDiskIdentification -ScriptBlock $methodblock -ArgumentList @($CimSession, $p, $Instances) } else { &$methodblock $CimSession $p $Instances } } } function Reset-PhysicalDisk { [CmdletBinding( DefaultParameterSetName = "ByName", ConfirmImpact="Medium" )] Param ( #### -------------------- Parameter sets -------------------------------------#### [System.String] [Parameter( ParameterSetName = 'ByUniqueId', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] [Alias("Id")] $UniqueId, [System.String] [Parameter( ParameterSetName = 'ByName', ValueFromPipeline = $true, Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] $FriendlyName, [Microsoft.Management.Infrastructure.CimInstance[]] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_PhysicalDisk")] [Parameter( ParameterSetName = 'ByInputObject', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $InputObject, #### ------------------------- Common method parameters ----------------------#### [Microsoft.Management.Infrastructure.CimSession] $CimSession, # Provided for compatibility with CDXML cmdlets, not actually used. [Int32] $ThrottleLimit, [Switch] $AsJob ) Begin { $SourceCaller = "Microsoft.PowerShell" } Process { if (-not $CimSession) { $CimSession = New-CimSession } if (-not $AsJob) { Write-Progress -Activity "Reset-PhysicalDisk" -PercentComplete 0 -CurrentOperation "Gathering objects" -Status "0/2" } ## Gather the instance objects switch ($psCmdlet.ParameterSetName) { "ByInputObject" { $Instances = $InputObject; break; } "ByUniqueId" { $Instances = Get-PhysicalDisk -UniqueId $UniqueId -CimSession $CimSession -ErrorAction Stop; break; } "ByName" { $Instances = Get-PhysicalDisk -FriendlyName $FriendlyName -CimSession $CimSession -ErrorAction Stop; break; } } # Would use a closure here, but jobs are run in their own session state. $methodblock = { param($session, $asjob, $instances) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } $errorObject = $null if (-not $asjob) { Write-Progress -Activity "Reset-PhysicalDisk" -PercentComplete 30 -CurrentOperation "Executing method" -Status "1/2" } ForEach ( $instance in $instances ) { try { $progressPreference = "silentlyContinue" $output = Invoke-CimMethod -CimSession $session -InputObject $instance -MethodName Reset -ErrorAction Stop $progressPreference = "Continue" } catch { $progressPreference = "Continue" $errorObject = CreateErrorRecord -ErrorId $_.FullyQualifiedErrorId ` -ErrorMessage $null ` -ErrorCategory $_.CategoryInfo.Category ` -Exception $_.Exception ` -TargetObject $_.TargetObject $psCmdlet.WriteError($errorObject) } } if (-not $asjob) { Write-Progress -Activity "Reset-PhysicalDisk" -Completed -Status "2/2" } } if ($asjob) { $p = $true Start-Job -Name ResetPhysicalDisk -ScriptBlock $methodblock -ArgumentList @($CimSession, $p, $Instances) } else { &$methodblock $CimSession $p $Instances } } } function Get-StorageFirmwareInformation { [CmdletBinding( DefaultParameterSetName = "ByPhysicalDiskFriendlyName", ConfirmImpact="None" )] Param ( #### -------------------- Parameter sets -------------------------------------#### [System.String] [Parameter( ParameterSetName = 'ByPhysicalDiskUniqueId', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] [Alias("Id")] [Alias("PhysicalDiskUniqueId")] $UniqueId, [System.String] [Parameter( ParameterSetName = 'ByPhysicalDiskFriendlyName', ValueFromPipeline = $true, Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] [Alias("PhysicalDiskFriendlyName")] $FriendlyName, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_PhysicalDisk")] [Parameter( ParameterSetName = 'ByPhysicalDisk', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] [Alias("PhysicalDisk")] $InputObject, [System.String] [Parameter( ParameterSetName = 'ByStorageEnclosureUniqueId', Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageEnclosureUniqueId, [System.String] [Parameter( ParameterSetName = 'ByStorageEnclosureFriendlyName', Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] $StorageEnclosureFriendlyName, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageEnclosure")] [Parameter( ParameterSetName = 'ByStorageEnclosure', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageEnclosure, #### ------------------------- Common method parameters ----------------------#### #### -------------------- Powershell parameters ------------------------------#### [Microsoft.Management.Infrastructure.CimSession] $CimSession, # Provided for compatibility with CDXML cmdlets, not actually used. [Int32] $ThrottleLimit, [Switch] $AsJob ) Begin { $SourceCaller = "Microsoft.PowerShell" } Process { $info = $resources.info $p = $null if (-not $CimSession) { $CimSession = New-CimSession } ## Gather the instance object switch ($psCmdlet.ParameterSetName) { "ByPhysicalDisk" { $PhysicalDiskInstance = $InputObject; break; } "ByPhysicalDiskUniqueId" { $PhysicalDiskInstance = Get-PhysicalDisk -UniqueId $UniqueId -CimSession $CimSession -ErrorAction Stop; break; } "ByPhysicalDiskFriendlyName" { $PhysicalDiskInstance = Get-PhysicalDisk -FriendlyName $FriendlyName -CimSession $CimSession -ErrorAction Stop; break; } "ByStorageEnclosure" { $StorageEnclosureInstance = $StorageEnclosure; break; } "ByStorageEnclosureUniqueId" { $StorageEnclosureInstance = Get-StorageEnclosure -UniqueId $StorageEnclosureUniqueId -CimSession $CimSession -ErrorAction Stop; break; } "ByStorageEnclosureFriendlyName" { $StorageEnclosureInstance = Get-StorageEnclosure -FriendlyName $StorageEnclosureFriendlyName -CimSession $CimSession -ErrorAction Stop; break; } } if (-not $AsJob) { Write-Progress -Activity "Get-StorageFirmwareInformation" -PercentComplete 0 -CurrentOperation "Validating parameters" -Status "0/2" } # Would use a closure here, but jobs are run in their own session state. $methodblock = { param($session, $asjob, $inputobject) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } $errorObject = $null if (-not $asjob) { Write-Progress -Activity "Get-StorageFirmwareInformation" -PercentComplete 30 -CurrentOperation "Getting storage firmware information" -Status "1/2" } try { $progressPreference = "silentlyContinue" $output = Invoke-CimMethod -CimSession $session -InputObject $inputobject -MethodName GetFirmwareInformation -ErrorAction Stop $progressPreference = "Continue" $firmwareInformation = [pscustomobject]@{ Object = $inputobject; SupportsUpdate = $output.SupportsUpdate; NumberOfSlots = $output.NumberOfSlots; ActiveSlotNumber = $output.ActiveSlotNumber; SlotNumber = $output.SlotNumber; IsSlotWritable = $output.IsSlotWritable; FirmwareVersionInSlot = $output.FirmwareVersionInSlot; } $psCmdlet.WriteObject($firmwareInformation); } catch { $progressPreference = "Continue" $errorObject = CreateErrorRecord -ErrorId $_.FullyQualifiedErrorId ` -ErrorMessage $null ` -ErrorCategory $_.CategoryInfo.Category ` -Exception $_.Exception ` -TargetObject $_.TargetObject $psCmdlet.WriteError($errorObject) } if (-not $asjob) { Write-Progress -Activity "Get-StorageFirmwareInformation" -Completed -Status "2/2" } } if ($asjob) { $p = $true if ($PhysicalDiskInstance) { Start-Job -Name GetStorageFirmwareInformation -ScriptBlock $methodblock -ArgumentList @($CimSession, $p, $PhysicalDiskInstance) } elseif ($StorageEnclosureInstance) { Start-Job -Name GetStorageFirmwareInformation -ScriptBlock $methodblock -ArgumentList @($CimSession, $p, $StorageEnclosureInstance) } } else { if ($PhysicalDiskInstance) { &$methodblock $CimSession $p $PhysicalDiskInstance } elseif ($StorageEnclosureInstance) { &$methodblock $CimSession $p $StorageEnclosureInstance } } } } function Update-StorageFirmware { [CmdletBinding( DefaultParameterSetName = "ByPhysicalDiskFriendlyName", ConfirmImpact="Medium" )] Param ( #### -------------------- Parameter sets -------------------------------------#### [System.String] [Parameter( ParameterSetName = 'ByPhysicalDiskUniqueId', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] [Alias("Id")] [Alias("PhysicalDiskUniqueId")] $UniqueId, [System.String] [Parameter( ParameterSetName = 'ByPhysicalDiskFriendlyName', ValueFromPipeline = $true, Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] [Alias("PhysicalDiskFriendlyName")] $FriendlyName, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_PhysicalDisk")] [Parameter( ParameterSetName = 'ByPhysicalDisk', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] [Alias("PhysicalDisk")] $InputObject, [System.String] [Parameter( ParameterSetName = 'ByStorageEnclosureUniqueId', Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageEnclosureUniqueId, [System.String] [Parameter( ParameterSetName = 'ByStorageEnclosureFriendlyName', Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] $StorageEnclosureFriendlyName, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageEnclosure")] [Parameter( ParameterSetName = 'ByStorageEnclosure', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageEnclosure, #### ------------------------- Common method parameters ----------------------#### [System.String] [Parameter( ParameterSetName = 'ByPhysicalDiskUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByPhysicalDiskFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByPhysicalDisk', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageEnclosureUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageEnclosureFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageEnclosure', Mandatory = $false)] $ImagePath, [System.UInt16] [Parameter( ParameterSetName = 'ByPhysicalDiskUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByPhysicalDiskFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByPhysicalDisk', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageEnclosureUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageEnclosureFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageEnclosure', Mandatory = $false)] $SlotNumber, #### -------------------- Powershell parameters ------------------------------#### [Microsoft.Management.Infrastructure.CimSession] $CimSession, # Provided for compatibility with CDXML cmdlets, not actually used. [Int32] $ThrottleLimit, [Switch] $AsJob ) Begin { $SourceCaller = "Microsoft.PowerShell" } Process { $info = $resources.info $p = $null if (-not $CimSession) { $CimSession = New-CimSession } ## Gather the instance object switch ($psCmdlet.ParameterSetName) { "ByPhysicalDisk" { $PhysicalDiskInstance = $InputObject; break; } "ByPhysicalDiskUniqueId" { $PhysicalDiskInstance = Get-PhysicalDisk -UniqueId $UniqueId -CimSession $CimSession -ErrorAction Stop; break; } "ByPhysicalDiskFriendlyName" { $PhysicalDiskInstance = Get-PhysicalDisk -FriendlyName $FriendlyName -CimSession $CimSession -ErrorAction Stop; break; } "ByStorageEnclosure" { $StorageEnclosureInstance = $StorageEnclosure; break; } "ByStorageEnclosureUniqueId" { $StorageEnclosureInstance = Get-StorageEnclosure -UniqueId $StorageEnclosureUniqueId -CimSession $CimSession -ErrorAction Stop; break; } "ByStorageEnclosureFriendlyName" { $StorageEnclosureInstance = Get-StorageEnclosure -FriendlyName $StorageEnclosureFriendlyName -CimSession $CimSession -ErrorAction Stop; break; } } if (-not $AsJob) { Write-Progress -Activity "Update-StorageFirmware" -PercentComplete 0 -CurrentOperation "Validating parameters" -Status "0/2" } ## Populate arguments list $arguments = @{} if ($PSBoundParameters.ContainsKey("ImagePath")) { $arguments.Add("ImagePath", $ImagePath) } if ($PSBoundParameters.ContainsKey("SlotNumber")) { $arguments.Add("SlotNumber", $SlotNumber) } # Would use a closure here, but jobs are run in their own session state. $methodblock = { param($session, $asjob, $inputobject, $arguments) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } $errorObject = $null if (-not $asjob) { Write-Progress -Activity "Update-StorageFirmware" -PercentComplete 30 -CurrentOperation "Updating storage firmware" -Status "1/2" } try { $progressPreference = "silentlyContinue" $output = Invoke-CimMethod -CimSession $session -InputObject $inputobject -MethodName UpdateFirmware -Arguments $arguments -ErrorAction Stop $progressPreference = "Continue" } catch { $progressPreference = "Continue" $errorObject = CreateErrorRecord -ErrorId $_.FullyQualifiedErrorId ` -ErrorMessage $null ` -ErrorCategory $_.CategoryInfo.Category ` -Exception $_.Exception ` -TargetObject $_.TargetObject $psCmdlet.WriteError($errorObject) } if (-not $asjob) { Write-Progress -Activity "Update-StorageFirmware" -Completed -Status "2/2" } } if ($asjob) { $p = $true if ($PhysicalDiskInstance) { Start-Job -Name UpdateStorageFirmware -ScriptBlock $methodblock -ArgumentList @($CimSession, $p, $PhysicalDiskInstance, $arguments) } elseif ($StorageEnclosureInstance) { Start-Job -Name UpdateStorageFirmware -ScriptBlock $methodblock -ArgumentList @($CimSession, $p, $StorageEnclosureInstance, $arguments) } } else { if ($PhysicalDiskInstance) { &$methodblock $CimSession $p $PhysicalDiskInstance $arguments } elseif ($StorageEnclosureInstance) { &$methodblock $CimSession $p $StorageEnclosureInstance $arguments } } } } function Get-PhysicalDiskStorageNodeView { [CmdletBinding()] Param ( [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageNode")] [Parameter( ValueFromPipeline = $true)] $StorageNode, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_PhysicalDisk")] [Parameter( ValueFromPipeline = $true)] $PhysicalDisk, [Microsoft.Management.Infrastructure.CimSession] $CimSession ) Process { if (-not $CimSession) { $CimSession = New-CimSession } ## Gather the association objects $options = New-Object Microsoft.Management.Infrastructure.Options.CimOperationOptions if ($PSBoundParameters.ContainsKey("StorageNode")) { $options.SetCustomOption("InputClassName", "MSFT_StorageNode", $false) $options.SetCustomOption("InputObjectId", $StorageNode.ObjectId, $false) } elseif ($PSBoundParameters.ContainsKey("PhysicalDisk")) { $options.SetCustomOption("InputClassName", "MSFT_PhysicalDisk", $false) $options.SetCustomOption("InputObjectId", $PhysicalDisk.ObjectId, $false) } $results = $CimSession.EnumerateInstances($StorageNamespace, "MSFT_StorageNodeToPhysicalDisk", $options ) $enumerator = $results.GetEnumerator() $instances = @() while ($enumerator.MoveNext()) { $instance = $enumerator.Current if ($PSBoundParameters.ContainsKey("StorageNode")) { if ($instance.StorageNodeObjectId -ne $StorageNode.ObjectId) { continue } } elseif ($PSBoundParameters.ContainsKey("PhysicalDisk")) { if ($instance.PhysicalDiskObjectId -ne $PhysicalDisk.ObjectId) { continue } } $instances += $instance } $instances } } function Get-DiskStorageNodeView { [CmdletBinding()] Param ( [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageNode")] [Parameter( ValueFromPipeline = $true)] $StorageNode, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_Disk")] [Parameter( ValueFromPipeline = $true)] $Disk, [Microsoft.Management.Infrastructure.CimSession] $CimSession ) Process { if (-not $CimSession) { $CimSession = New-CimSession } ## Gather the association objects $options = New-Object Microsoft.Management.Infrastructure.Options.CimOperationOptions if ($PSBoundParameters.ContainsKey("StorageNode")) { $options.SetCustomOption("InputClassName", "MSFT_StorageNode", $false) $options.SetCustomOption("InputObjectId", $StorageNode.ObjectId, $false) } elseif ($PSBoundParameters.ContainsKey("Disk")) { $options.SetCustomOption("InputClassName", "MSFT_Disk", $false) $options.SetCustomOption("InputObjectId", $Disk.ObjectId, $false) } $results = $CimSession.EnumerateInstances($StorageNamespace, "MSFT_StorageNodeToDisk", $options ) $enumerator = $results.GetEnumerator() $instances = @() while ($enumerator.MoveNext()) { $instance = $enumerator.Current if ($PSBoundParameters.ContainsKey("StorageNode")) { if ($instance.StorageNodeObjectId -ne $StorageNode.ObjectId) { continue } } elseif ($PSBoundParameters.ContainsKey("Disk")) { if ($instance.DiskObjectId -ne $Disk.ObjectId) { continue } } $instances += $instance } $instances } } function Get-StorageEnclosureStorageNodeView { [CmdletBinding()] Param ( [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageNode")] [Parameter( ValueFromPipeline = $true)] $StorageNode, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageEnclosure")] [Parameter( ValueFromPipeline = $true)] $StorageEnclosure, [Microsoft.Management.Infrastructure.CimSession] $CimSession ) Process { if (-not $CimSession) { $CimSession = New-CimSession } ## Gather the association objects $options = New-Object Microsoft.Management.Infrastructure.Options.CimOperationOptions if ($PSBoundParameters.ContainsKey("StorageNode")) { $options.SetCustomOption("InputClassName", "MSFT_StorageNode", $false) $options.SetCustomOption("InputObjectId", $StorageNode.ObjectId, $false) } elseif ($PSBoundParameters.ContainsKey("StorageEnclosure")) { $options.SetCustomOption("InputClassName", "MSFT_StorageEnclosure", $false) $options.SetCustomOption("InputObjectId", $StorageEnclosure.ObjectId, $false) } $results = $CimSession.EnumerateInstances($StorageNamespace, "MSFT_StorageNodeToStorageEnclosure", $options ) $enumerator = $results.GetEnumerator() $instances = @() while ($enumerator.MoveNext()) { $instance = $enumerator.Current if ($PSBoundParameters.ContainsKey("StorageNode")) { if ($instance.StorageNodeObjectId -ne $StorageNode.ObjectId) { continue } } elseif ($PSBoundParameters.ContainsKey("StorageEnclosure")) { if ($instance.StorageEnclosureObjectId -ne $StorageEnclosure.ObjectId) { continue } } $instances += $instance } $instances } } function Get-StorageHealthAction { [CmdletBinding()] Param ( [string[]] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "ByUniqueId")] $UniqueId, [CimInstance] [Parameter( Mandatory = $false, ValueFromPipeline = $true, ParameterSetName = "InputObject")] $InputObject, [Microsoft.Management.Infrastructure.CimSession] $CimSession, # Provided for compatibility with CDXML cmdlets, not actually used. [Int32] $ThrottleLimit, [Switch] $AsJob ) Begin{} Process { $info = $resources.info $p = $null $methodName = "GetActions" if (-not $CimSession) { $CimSession = New-CimSession } if ($UniqueId) { # Would use a closure here, but jobs are run in their own session state. $block = { param($session, $asjob, $ns, $id) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } Get-CimInstance -CimSession $session -Query "Select * From MSFT_HealthAction Where UniqueId='$id'" -Namespace $ns } if ($asjob) { $p = $true Start-Job -ScriptBlock $block -ArgumentList @($CimSession, $p, $StorageNamespace, $UniqueId) } else { if ($pscmdlet.ShouldProcess($info, $resources.warning, $info)) { &$block $CimSession $p $StorageNamespace $UniqueId } } } elseif ($InputObject.CimClass.CimClassName -eq "MSFT_HealthAction") { # Would use a closure here, but jobs are run in their own session state. $block = { param($session, $asjob, $io) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } $io | Get-CimInstance -CimSession $session } if ($asjob) { $p = $true Start-Job -ScriptBlock $block -ArgumentList @($CimSession, $p, $InputObject) } else { if ($pscmdlet.ShouldProcess($info, $resources.warning, $info)) { &$block $CimSession $p $InputObject } } } elseif (-not $InputObject -or ( $InputObject.CimClass.CimClassName -eq "MSFT_StorageHealth" )) { $className = "MSFT_HealthAction" $block = { param($session, $ns, $asjob, $cn) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } Get-CimInstance -CimSession $session -Namespace $ns -ClassName $cn } if ($asjob) { $p = $true Start-Job -ScriptBlock $block -ArgumentList @($CimSession, $StorageNamespace, $p, $className) } else { if ($pscmdlet.ShouldProcess($info, $resources.warning, $info)) { &$block $CimSession $StorageNamespace $p $className } } } else { # Would use a closure here, but jobs are run in their own session state. $block = { param($session, $ns, $asjob, $mn, $io) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } $r = Invoke-CimMethod -CimSession $session -InputObject $io -MethodName $mn -Confirm:$false for($i = 0; $i -lt $r.Count - 1; $i++) { $r[$i].ItemValue } } if ($asjob) { $p = $true Start-Job -ScriptBlock $block -ArgumentList @($CimSession, $StorageNamespace, $p, $methodName, $InputObject) } else { if ($pscmdlet.ShouldProcess($info, $resources.warning, $info)) { &$block $CimSession $StorageNamespace $p $methodName $InputObject } } } } } function Get-StorageFaultDomain { [CmdletBinding( DefaultParameterSetName = "EnumerateFaultDomains" )] Param ( #### -------------------- Parameter sets -------------------------------------#### [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageSubsystem")] [Parameter( ParameterSetName = 'ByStorageSubsystem', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageSubsystem, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageFaultDomain")] [Parameter( ParameterSetName = 'ByStorageFaultDomain', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageFaultDomain, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_VirtualDisk")] [Parameter( ParameterSetName = 'ByVirtualDisk', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $VirtualDisk, #### ------------------------- Common parameters -----------------------------#### [System.String] [Parameter( ParameterSetName = 'EnumerateFaultDomains', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageFaultDomain', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] $FriendlyName, [System.String] [Parameter( ParameterSetName = 'EnumerateFaultDomains', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageFaultDomain', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] $SerialNumber, [StorageFaultDomainType] [Parameter( ParameterSetName = 'EnumerateFaultDomains', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageFaultDomain', Mandatory = $false)] $Type, [System.String] [Parameter( ParameterSetName = 'EnumerateFaultDomains', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageFaultDomain', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] [ValidateNotNullOrEmpty()] $PhysicalLocation, [Microsoft.Management.Infrastructure.CimSession] $CimSession ) Begin { [flagsattribute()] Enum StorageFaultDomainType { PhysicalDisk = 1 StorageEnclosure = 2 StorageScaleUnit = 3 StorageChassis = 4 StorageRack = 5 StorageSite = 6 } $className = "MSFT_StorageFaultDomain" switch ($Type) { PhysicalDisk { $className = "MSFT_PhysicalDisk" } StorageEnclosure { $className = "MSFT_StorageEnclosure" } StorageScaleUnit { $className = "MSFT_StorageScaleUnit" } StorageChassis { $className = "MSFT_StorageChassis" } StorageRack { $className = "MSFT_StorageRack" } StorageSite { $className = "MSFT_StorageSite" } } } Process { if (-not $CimSession) { $CimSession = New-CimSession } ## Gather the instance objects. $options = New-Object Microsoft.Management.Infrastructure.Options.CimOperationOptions if ($PSBoundParameters.ContainsKey("Type")) { $options.SetCustomOption("ResultObject", $className, $false) } switch ($psCmdlet.ParameterSetName) { "ByStorageSubsystem" { if (($className -eq "MSFT_PhysicalDisk") -and ($StorageSubsystem.Model -like "*Windows Storage*")) { $options.SetCustomOption("InputClassName", "MSFT_StorageSubsystem", $false) $options.SetCustomOption("InputObjectId", $StorageSubsystem.ObjectId, $false) $results = $CimSession.EnumerateInstances($StorageNamespace, "MSFT_PhysicalDisk", $options) } else { $results = $CimSession.EnumerateAssociatedInstances($StorageNamespace, $StorageSubsystem, "MSFT_StorageSubsystemToStorageFaultDomain", $className, "StorageSubSystem", "StorageFaultDomain", $options) } break; } "ByStorageFaultDomain" { $subsystem = $StorageFaultDomain | get-storagesubsystem -CimSession $CimSession if (($className -eq "MSFT_PhysicalDisk") -and ($subsystem.Model -like "*Windows Storage*")) { $options.SetCustomOption("InputClassName", $StorageFaultDomain.ClassName, $false) $options.SetCustomOption("InputObjectId", $StorageFaultDomain.ObjectId, $false) $results = $CimSession.EnumerateInstances($StorageNamespace, "MSFT_PhysicalDisk", $options) } else { $results = $CimSession.EnumerateAssociatedInstances($StorageNamespace, $StorageFaultDomain, "MSFT_StorageFaultDomainToStorageFaultDomain", $className, "SourceStorageFaultDomain", "TargetStorageFaultDomain", $options) } break; } "ByVirtualDisk" { $results = $CimSession.EnumerateAssociatedInstances($StorageNamespace, $VirtualDisk, "MSFT_VirtualDiskToStorageFaultDomain", "MSFT_StorageFaultDomain", "VirtualDisk", "StorageFaultDomain") } "EnumerateFaultDomains" { $results = $CimSession.EnumerateInstances($StorageNamespace, $className) break; } } $enumerator = $results.GetEnumerator() $instances = @() while ($enumerator.MoveNext()) { $instance = $enumerator.Current if (($PSBoundParameters.ContainsKey("FriendlyName")) -and ($instance.FriendlyName -notlike $FriendlyName)) { continue } if (($PSBoundParameters.ContainsKey("SerialNumber")) -and ($instance.SerialNumber -notlike $SerialNumber)) { continue } ## Filter by physical location if required and ensure ## the formatting selected is for MSFT_StorageFaultDomain if (($PSBoundParameters.ContainsKey("PhysicalLocation")) -and ($instance.PhysicalLocation -notlike $PhysicalLocation)) { continue } $typename = $instance.PSObject.TypeNames.GetEnumerator() $index = 0; while ($typename.MoveNext()) { if ($typename.Current.Equals("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageObject", [System.StringComparison]::OrdinalIgnoreCase) -eq $true) { break } $index++ } $instance.PSObject.TypeNames.Insert($index + 1, "Microsoft.Management.Infrastructure.CimInstance#MSFT_StorageFaultDomain") $instances += $instance } $instances } } function Get-StorageScaleUnit { [CmdletBinding( DefaultParameterSetName = "EnumerateFaultDomains" )] Param ( #### -------------------- Parameter sets -------------------------------------#### [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageSubsystem")] [Parameter( ParameterSetName = 'ByStorageSubsystem', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageSubsystem, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageFaultDomain")] [Parameter( ParameterSetName = 'ByStorageFaultDomain', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageFaultDomain, #### ------------------------- Common parameters -----------------------------#### [System.String] [Parameter( ParameterSetName = 'EnumerateFaultDomains', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageFaultDomain', Mandatory = $false)] $FriendlyName, [System.String] [Parameter( ParameterSetName = 'EnumerateFaultDomains', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageFaultDomain', Mandatory = $false)] $SerialNumber, [System.String] [Parameter( ParameterSetName = 'EnumerateFaultDomains', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageFaultDomain', Mandatory = $false)] [ValidateNotNullOrEmpty()] $PhysicalLocation, [Microsoft.Management.Infrastructure.CimSession] $CimSession ) Begin { } Process { $arguments = @{} $arguments.Add("Type", 'StorageScaleUnit') if ($PSBoundParameters.ContainsKey("FriendlyName")) { $arguments.Add("FriendlyName", $FriendlyName) } if ($PSBoundParameters.ContainsKey("SerialNumber")) { $arguments.Add("SerialNumber", $SerialNumber) } if ($PSBoundParameters.ContainsKey("PhysicalLocation")) { $arguments.Add("PhysicalLocation", $PhysicalLocation) } if ($PSBoundParameters.ContainsKey("StorageSubsystem")) { $arguments.Add("StorageSubsystem", $StorageSubsystem) } if ($PSBoundParameters.ContainsKey("StorageFaultDomain")) { $arguments.Add("StorageFaultDomain", $StorageFaultDomain) } if ($PSBoundParameters.ContainsKey("CimSession")) { $arguments.Add("CimSession", $CimSession) } Get-StorageFaultDomain @arguments } } function Get-StorageChassis { [CmdletBinding( DefaultParameterSetName = "EnumerateFaultDomains" )] Param ( #### -------------------- Parameter sets -------------------------------------#### [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageSubsystem")] [Parameter( ParameterSetName = 'ByStorageSubsystem', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageSubsystem, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageFaultDomain")] [Parameter( ParameterSetName = 'ByStorageFaultDomain', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageFaultDomain, #### ------------------------- Common parameters -----------------------------#### [System.String] [Parameter( ParameterSetName = 'EnumerateFaultDomains', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageFaultDomain', Mandatory = $false)] $FriendlyName, [System.String] [Parameter( ParameterSetName = 'EnumerateFaultDomains', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageFaultDomain', Mandatory = $false)] $SerialNumber, [System.String] [Parameter( ParameterSetName = 'EnumerateFaultDomains', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageFaultDomain', Mandatory = $false)] [ValidateNotNullOrEmpty()] $PhysicalLocation, [Microsoft.Management.Infrastructure.CimSession] $CimSession ) Begin { } Process { $arguments = @{} $arguments.Add("Type", 'StorageChassis') if ($PSBoundParameters.ContainsKey("FriendlyName")) { $arguments.Add("FriendlyName", $FriendlyName) } if ($PSBoundParameters.ContainsKey("SerialNumber")) { $arguments.Add("SerialNumber", $SerialNumber) } if ($PSBoundParameters.ContainsKey("PhysicalLocation")) { $arguments.Add("PhysicalLocation", $PhysicalLocation) } if ($PSBoundParameters.ContainsKey("StorageSubsystem")) { $arguments.Add("StorageSubsystem", $StorageSubsystem) } if ($PSBoundParameters.ContainsKey("StorageFaultDomain")) { $arguments.Add("StorageFaultDomain", $StorageFaultDomain) } if ($PSBoundParameters.ContainsKey("CimSession")) { $arguments.Add("CimSession", $CimSession) } Get-StorageFaultDomain @arguments } } function Get-StorageRack { [CmdletBinding( DefaultParameterSetName = "EnumerateFaultDomains" )] Param ( #### -------------------- Parameter sets -------------------------------------#### [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageSubsystem")] [Parameter( ParameterSetName = 'ByStorageSubsystem', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageSubsystem, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageFaultDomain")] [Parameter( ParameterSetName = 'ByStorageFaultDomain', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageFaultDomain, #### ------------------------- Common parameters -----------------------------#### [System.String] [Parameter( ParameterSetName = 'EnumerateFaultDomains', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageFaultDomain', Mandatory = $false)] $FriendlyName, [System.String] [Parameter( ParameterSetName = 'EnumerateFaultDomains', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageFaultDomain', Mandatory = $false)] $SerialNumber, [System.String] [Parameter( ParameterSetName = 'EnumerateFaultDomains', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageFaultDomain', Mandatory = $false)] [ValidateNotNullOrEmpty()] $PhysicalLocation, [Microsoft.Management.Infrastructure.CimSession] $CimSession ) Begin { } Process { $arguments = @{} $arguments.Add("Type", 'StorageRack') if ($PSBoundParameters.ContainsKey("FriendlyName")) { $arguments.Add("FriendlyName", $FriendlyName) } if ($PSBoundParameters.ContainsKey("SerialNumber")) { $arguments.Add("SerialNumber", $SerialNumber) } if ($PSBoundParameters.ContainsKey("PhysicalLocation")) { $arguments.Add("PhysicalLocation", $PhysicalLocation) } if ($PSBoundParameters.ContainsKey("StorageSubsystem")) { $arguments.Add("StorageSubsystem", $StorageSubsystem) } if ($PSBoundParameters.ContainsKey("StorageFaultDomain")) { $arguments.Add("StorageFaultDomain", $StorageFaultDomain) } if ($PSBoundParameters.ContainsKey("CimSession")) { $arguments.Add("CimSession", $CimSession) } Get-StorageFaultDomain @arguments } } function Get-StorageSite { [CmdletBinding( DefaultParameterSetName = "EnumerateFaultDomains" )] Param ( #### -------------------- Parameter sets -------------------------------------#### [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageSubsystem")] [Parameter( ParameterSetName = 'ByStorageSubsystem', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageSubsystem, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageFaultDomain")] [Parameter( ParameterSetName = 'ByStorageFaultDomain', ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageFaultDomain, #### ------------------------- Common parameters -----------------------------#### [System.String] [Parameter( ParameterSetName = 'EnumerateFaultDomains', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageFaultDomain', Mandatory = $false)] $FriendlyName, [System.String] [Parameter( ParameterSetName = 'EnumerateFaultDomains', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageFaultDomain', Mandatory = $false)] $SerialNumber, [System.String] [Parameter( ParameterSetName = 'EnumerateFaultDomains', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubsystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageFaultDomain', Mandatory = $false)] [ValidateNotNullOrEmpty()] $PhysicalLocation, [Microsoft.Management.Infrastructure.CimSession] $CimSession ) Begin { } Process { $arguments = @{} $arguments.Add("Type", 'StorageSite') if ($PSBoundParameters.ContainsKey("FriendlyName")) { $arguments.Add("FriendlyName", $FriendlyName) } if ($PSBoundParameters.ContainsKey("SerialNumber")) { $arguments.Add("SerialNumber", $SerialNumber) } if ($PSBoundParameters.ContainsKey("PhysicalLocation")) { $arguments.Add("PhysicalLocation", $PhysicalLocation) } if ($PSBoundParameters.ContainsKey("StorageSubsystem")) { $arguments.Add("StorageSubsystem", $StorageSubsystem) } if ($PSBoundParameters.ContainsKey("StorageFaultDomain")) { $arguments.Add("StorageFaultDomain", $StorageFaultDomain) } if ($PSBoundParameters.ContainsKey("CimSession")) { $arguments.Add("CimSession", $CimSession) } Get-StorageFaultDomain @arguments } } function ValidateFaultDomainsExist { Param ( [String[]] $StorageFaultDomainFriendlyNames, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageSubsystem")] $StorageSubsystem, [Microsoft.Management.Infrastructure.CimSession] $CimSession ) [Microsoft.Management.Infrastructure.CimInstance[]] $faultdomains = @() $subsystemFDs = Get-CimAssociatedInstance -InputObject $StorageSubsystem ` -Association MSFT_StorageSubsystemToStorageFaultDomain ` -ResultClassName MSFT_StorageFaultDomain ` -CimSession $CimSession ` -ErrorAction Stop for ($i = 0; $i -lt $StorageFaultDomainFriendlyNames.Count; $i++) { $found = $false foreach ($fd in $subsystemFDs) { if ($fd.FriendlyName -eq $StorageFaultDomainFriendlyNames[$i]) { $faultdomains += $fd $found = $true break } } if ($found -eq $false) { $errorObject = CreateErrorRecord -ErrorId "InvalidArgument" ` -ErrorMessage "Could not find storage fault domain with the friendly name '$($StorageFaultDomainFriendlyNames[$i])' in the storage subsystem" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidArgument) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) $faultdomains = @() break } } return $faultdomains } function Add-StorageFaultDomain { [CmdletBinding()] Param ( #### -------------------- Parameter sets -------------------------------------#### [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_VirtualDisk")] [Parameter( ParameterSetName = "ByVirtualDisk", ValueFromPipeline = $true, Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] $VirtualDisk, [String] [Parameter( ParameterSetName = "ByVirtualDiskFriendlyName", Mandatory = $true)] [ValidateNotNullOrEmpty()] $VirtualDiskFriendlyName, [String] [Parameter( ParameterSetName = "ByVirtualDiskName", Mandatory = $true)] [ValidateNotNullOrEmpty()] $VirtualDiskName, [String] [Parameter( ParameterSetName = "ByVirtualDiskUniqueId", Mandatory = $true)] [ValidateNotNullOrEmpty()] $VirtualDiskUniqueId, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageTier")] [Parameter( ParameterSetName = "ByStorageTier", ValueFromPipeline = $true, Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] $StorageTier, [String] [Parameter( ParameterSetName = "ByStorageTierFriendlyName", Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageTierFriendlyName, [String] [Parameter( ParameterSetName = "ByStorageTierUniqueId", Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageTierUniqueId, #### -------------------- Common method parameters ---------------------------#### [Microsoft.Management.Infrastructure.CimInstance[]] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageFaultDomain")] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDiskFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDiskName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDiskUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageTier', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageTierFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageTierUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $StorageFaultDomains, [String[]] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDiskFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDiskName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDiskUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageTier', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageTierFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageTierUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $StorageFaultDomainFriendlyNames, #### -------------------- Powershell parameters ------------------------------#### [Microsoft.Management.Infrastructure.CimSession] $CimSession, # Provided for compatibility with CDXML cmdlets, not actually used. [Int32] $ThrottleLimit, [Switch] $AsJob ) Begin { $SourceCaller = "Microsoft.PowerShell" } Process { $info = $resources.info $p = $null if (-not $CimSession) { $CimSession = New-CimSession } ## Gather the instance object switch ($psCmdlet.ParameterSetName) { "ByVirtualDisk" { $VirtualDiskInstance = $VirtualDisk; break; } "ByVirtualDiskFriendlyName" { $VirtualDiskInstance = Get-VirtualDisk -FriendlyName $VirtualDiskFriendlyName -CimSession $CimSession -ErrorAction Stop; break; } "ByVirtualDiskName" { $VirtualDiskInstance = Get-VirtualDisk -Name $VirtualDiskName -CimSession $CimSession -ErrorAction Stop; break; } "ByVirtualDiskUniqueId" { $VirtualDiskInstance = Get-VirtualDisk -UniqueId $VirtualDiskUniqueId -CimSession $CimSession -ErrorAction Stop; break; } "ByStorageTier" { $StorageTierInstance = $StorageTier; break; } "ByStorageTierFriendlyName" { $StorageTierInstance = Get-StorageTier -FriendlyName $StorageTierFriendlyName -CimSession $CimSession -ErrorAction Stop; break; } "ByStorageTierUniqueId" { $StorageTierInstance = Get-StorageTier -UniqueId $StorageTierUniqueId -CimSession $CimSession -ErrorAction Stop; break; } } if (-not $AsJob) { Write-Progress -Activity "Add-StorageFaultDomain" -PercentComplete 0 -CurrentOperation "Validating parameters" -Status "0/2" } if ($VirtualDiskInstance) { $subsystem = $VirtualDiskInstance | get-storagesubsystem -CimSession $CimSession } elseif ($StorageTierInstance) { $subsystem = $StorageTierInstance | get-storagesubsystem -CimSession $CimSession } ## Populate arguments list $arguments = @{} if ($PSBoundParameters.ContainsKey("StorageFaultDomains") -and $PSBoundParameters.ContainsKey("StorageFaultDomainFriendlyNames")) { $errorObject = CreateErrorRecord -ErrorId "InvalidArgument" ` -ErrorMessage "Use either 'StorageFaultDomains' or 'StorageFaultDomainFriendlyNames' parameter" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidArgument) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } if ($PSBoundParameters.ContainsKey("StorageFaultDomains")) { $arguments.Add("StorageFaultDomains", $StorageFaultDomains) } elseif ($PSBoundParameters.ContainsKey("StorageFaultDomainFriendlyNames")) { [Microsoft.Management.Infrastructure.CimInstance[]] $faultdomains = @() $faultdomains = ValidateFaultDomainsExist -StorageFaultDomainFriendlyNames $StorageFaultDomainFriendlyNames ` -StorageSubsystem $subsystem ` -CimSession $CimSession if ($faultdomains.Count -eq 0) { return } $arguments.Add("StorageFaultDomains", $faultdomains) } # Would use a closure here, but jobs are run in their own session state. $addfdblock = { param($session, $asjob, $inputobject, $arguments) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } $errorObject = $null $output = $null try { if (-not $asjob) { Write-Progress -Activity "Add-StorageFaultDomain" -PercentComplete 10 -CurrentOperation "Adding storage fault domains" -Status "1/2" } $progressPreference = "silentlyContinue" $output = Invoke-CimMethod -CimSession $session -InputObject $inputobject -MethodName AddStorageFaultDomain -Arguments $arguments -ErrorAction Stop $progressPreference = "Continue" } catch { $progressPreference = "Continue" $errorObject = CreateErrorRecord -ErrorId $_.FullyQualifiedErrorId ` -ErrorMessage $null ` -ErrorCategory $_.CategoryInfo.Category ` -Exception $_.Exception ` -TargetObject $_.TargetObject } finally { if (-not $asjob) { Write-Progress -Activity "Add-StorageFaultDomain" -Completed -Status "2/2" } } if ($errorObject) { $psCmdlet.WriteError($errorObject) } else { $null } } if ($asjob) { $p = $true if ($VirtualDiskInstance) { Start-Job -Name AddStorageFaultDomain -ScriptBlock $addfdblock -ArgumentList @($CimSession, $p, $VirtualDiskInstance, $arguments) } elseif ($StorageTierInstance) { Start-Job -Name AddStorageFaultDomain -ScriptBlock $addfdblock -ArgumentList @($CimSession, $p, $StorageTierInstance, $arguments) } } else { if ($pscmdlet.ShouldProcess($info, $resources.warning, $info)) { if ($VirtualDiskInstance) { &$addfdblock $CimSession $p $VirtualDiskInstance $arguments } elseif ($StorageTierInstance) { &$addfdblock $CimSession $p $StorageTierInstance $arguments } } } } } function Remove-StorageFaultDomain { [CmdletBinding()] Param ( #### -------------------- Parameter sets -------------------------------------#### [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_VirtualDisk")] [Parameter( ParameterSetName = "ByVirtualDisk", ValueFromPipeline = $true, Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] $VirtualDisk, [String] [Parameter( ParameterSetName = "ByVirtualDiskFriendlyName", Mandatory = $true)] [ValidateNotNullOrEmpty()] $VirtualDiskFriendlyName, [String] [Parameter( ParameterSetName = "ByVirtualDiskName", Mandatory = $true)] [ValidateNotNullOrEmpty()] $VirtualDiskName, [String] [Parameter( ParameterSetName = "ByVirtualDiskUniqueId", Mandatory = $true)] [ValidateNotNullOrEmpty()] $VirtualDiskUniqueId, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageTier")] [Parameter( ParameterSetName = "ByStorageTier", ValueFromPipeline = $true, Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] $StorageTier, [String] [Parameter( ParameterSetName = "ByStorageTierFriendlyName", Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageTierFriendlyName, [String] [Parameter( ParameterSetName = "ByStorageTierUniqueId", Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageTierUniqueId, #### -------------------- Common method parameters ---------------------------#### [Microsoft.Management.Infrastructure.CimInstance[]] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageFaultDomain")] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDiskFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDiskName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDiskUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageTier', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageTierFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageTierUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $StorageFaultDomains, [String[]] [Parameter( ParameterSetName = 'ByVirtualDisk', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDiskFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDiskName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByVirtualDiskUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageTier', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageTierFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageTierUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $StorageFaultDomainFriendlyNames, #### -------------------- Powershell parameters ------------------------------#### [Microsoft.Management.Infrastructure.CimSession] $CimSession, # Provided for compatibility with CDXML cmdlets, not actually used. [Int32] $ThrottleLimit, [Switch] $AsJob ) Begin { $SourceCaller = "Microsoft.PowerShell" } Process { $info = $resources.info $p = $null if (-not $CimSession) { $CimSession = New-CimSession } ## Gather the instance object switch ($psCmdlet.ParameterSetName) { "ByVirtualDisk" { $VirtualDiskInstance = $VirtualDisk; break; } "ByVirtualDiskFriendlyName" { $VirtualDiskInstance = Get-VirtualDisk -FriendlyName $VirtualDiskFriendlyName -CimSession $CimSession -ErrorAction Stop; break; } "ByVirtualDiskName" { $VirtualDiskInstance = Get-VirtualDisk -Name $VirtualDiskName -CimSession $CimSession -ErrorAction Stop; break; } "ByVirtualDiskUniqueId" { $VirtualDiskInstance = Get-VirtualDisk -UniqueId $VirtualDiskUniqueId -CimSession $CimSession -ErrorAction Stop; break; } "ByStorageTier" { $StorageTierInstance = $StorageTier; break; } "ByStorageTierFriendlyName" { $StorageTierInstance = Get-StorageTier -FriendlyName $StorageTierFriendlyName -CimSession $CimSession -ErrorAction Stop; break; } "ByStorageTierUniqueId" { $StorageTierInstance = Get-StorageTier -UniqueId $StorageTierUniqueId -CimSession $CimSession -ErrorAction Stop; break; } } if (-not $AsJob) { Write-Progress -Activity "Remove-StorageFaultDomain" -PercentComplete 0 -CurrentOperation "Validating parameters" -Status "0/2" } if ($VirtualDiskInstance) { $subsystem = $VirtualDiskInstance | get-storagesubsystem -CimSession $CimSession } elseif ($StorageTierInstance) { $subsystem = $StorageTierInstance | get-storagesubsystem -CimSession $CimSession } ## Populate arguments list $arguments = @{} if ($PSBoundParameters.ContainsKey("StorageFaultDomains") -and $PSBoundParameters.ContainsKey("StorageFaultDomainFriendlyNames")) { $errorObject = CreateErrorRecord -ErrorId "InvalidArgument" ` -ErrorMessage "Use either 'StorageFaultDomains' or 'StorageFaultDomainFriendlyNames' parameter" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidArgument) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } if ($PSBoundParameters.ContainsKey("StorageFaultDomains")) { $arguments.Add("StorageFaultDomains", $StorageFaultDomains) } elseif ($PSBoundParameters.ContainsKey("StorageFaultDomainFriendlyNames")) { [Microsoft.Management.Infrastructure.CimInstance[]] $faultdomains = @() $faultdomains = ValidateFaultDomainsExist -StorageFaultDomainFriendlyNames $StorageFaultDomainFriendlyNames ` -StorageSubsystem $subsystem ` -CimSession $CimSession if ($faultdomains.Count -eq 0) { return } $arguments.Add("StorageFaultDomains", $faultdomains) } # Would use a closure here, but jobs are run in their own session state. $removefdblock = { param($session, $asjob, $inputobject, $arguments) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } $errorObject = $null $output = $null try { if (-not $asjob) { Write-Progress -Activity "Remove-StorageFaultDomain" -PercentComplete 10 -CurrentOperation "Removing storage fault domains" -Status "1/2" } $progressPreference = "silentlyContinue" $output = Invoke-CimMethod -CimSession $session -InputObject $inputobject -MethodName RemoveStorageFaultDomain -Arguments $arguments -ErrorAction Stop $progressPreference = "Continue" } catch { $progressPreference = "Continue" $errorObject = CreateErrorRecord -ErrorId $_.FullyQualifiedErrorId ` -ErrorMessage $null ` -ErrorCategory $_.CategoryInfo.Category ` -Exception $_.Exception ` -TargetObject $_.TargetObject } finally { if (-not $asjob) { Write-Progress -Activity "Remove-StorageFaultDomain" -Completed -Status "2/2" } } if ($errorObject) { $psCmdlet.WriteError($errorObject) } else { $null } } if ($asjob) { $p = $true if ($VirtualDiskInstance) { Start-Job -Name RemoveStorageFaultDomain -ScriptBlock $removefdblock -ArgumentList @($CimSession, $p, $VirtualDiskInstance, $arguments) } elseif ($StorageTierInstance) { Start-Job -Name RemoveStorageFaultDomain -ScriptBlock $removefdblock -ArgumentList @($CimSession, $p, $StorageTierInstance, $arguments) } } else { if ($pscmdlet.ShouldProcess($info, $resources.warning, $info)) { if ($VirtualDiskInstance) { &$removefdblock $CimSession $p $VirtualDiskInstance $arguments } elseif ($StorageTierInstance) { &$removefdblock $CimSession $p $StorageTierInstance $arguments } } } } } function New-Volume { [CmdletBinding( DefaultParameterSetName = "ByStoragePool" )] Param ( #### -------------------- Parameter sets -------------------------------------#### [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StoragePool")] [Parameter( ParameterSetName = "ByStoragePool", ValueFromPipeline = $true, Mandatory = $false, Position = 0)] [ValidateNotNullOrEmpty()] $StoragePool, [String] [Parameter( ParameterSetName = "ByStoragePoolFriendlyName", Mandatory = $true)] [ValidateNotNullOrEmpty()] $StoragePoolFriendlyName, [String] [Parameter( ParameterSetName = "ByStoragePoolName", Mandatory = $true)] [ValidateNotNullOrEmpty()] $StoragePoolName, [String] [Parameter( ParameterSetName = "ByStoragePoolUniqueId", Mandatory = $true)] [ValidateNotNullOrEmpty()] $StoragePoolUniqueId, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_Disk")] [Parameter( ParameterSetName = "ByDisk", ValueFromPipeline = $true, Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] $Disk, [UInt32] [Parameter( ParameterSetName = "ByDiskNumber", Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] $DiskNumber, [String] [Parameter( ParameterSetName = "ByDiskPath", Mandatory = $true)] [ValidateNotNullOrEmpty()] $DiskPath, [String] [Parameter( ParameterSetName = "ByDiskUniqueId", Mandatory = $true)] [ValidateNotNullOrEmpty()] $DiskUniqueId, #### -------------------- Common method parameters ---------------------------#### [String] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $true)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $true)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $true)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $true)] [Parameter( ParameterSetName = 'ByDisk', Mandatory = $true)] [Parameter( ParameterSetName = 'ByDiskNumber', Mandatory = $true)] [Parameter( ParameterSetName = 'ByDiskPath', Mandatory = $true)] [Parameter( ParameterSetName = 'ByDiskUniqueId', Mandatory = $true)] [ValidateNotNullOrEmpty()] $FriendlyName, [FileSystemType] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDisk', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDiskNumber', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDiskPath', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDiskUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $FileSystem, [String] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDisk', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDiskNumber', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDiskPath', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDiskUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $AccessPath, [Char] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDisk', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDiskNumber', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDiskPath', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDiskUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $DriveLetter, [UInt32] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDisk', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDiskNumber', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDiskPath', Mandatory = $false)] [Parameter( ParameterSetName = 'ByDiskUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $AllocationUnitSize, #### -------------------- Storage pool parameters ----------------------------#### [UInt64] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $Size, [String] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $ResiliencySettingName, [ProvisioningType] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $ProvisioningType, [MediaType] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $MediaType, [UInt16] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] [Alias("FaultDomainRedundancy")] $PhysicalDiskRedundancy, [UInt16] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $NumberOfDataCopies, [UInt16] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $NumberOfColumns, [UInt16] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $NumberOfGroups, [Microsoft.Management.Infrastructure.CimInstance[]] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageFaultDomain")] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $StorageFaultDomainsToUse, [String[]] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $StorageFaultDomainsToUseFriendlyNames, [Microsoft.Management.Infrastructure.CimInstance[]] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageTier")] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $StorageTiers, [String[]] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $StorageTierFriendlyNames, [UInt64[]] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $StorageTierSizes, [UInt64] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $WriteCacheSize, [UInt64] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] [ValidateNotNullOrEmpty()] $ReadCacheSize, [Switch] [Parameter( ParameterSetName = 'ByStoragePool', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStoragePoolUniqueId', Mandatory = $false)] $UseMaximumSize = $false, #### -------------------- Disk parameters ------------------------------------#### #### -------------------- Powershell parameters ------------------------------#### [Microsoft.Management.Infrastructure.CimSession] $CimSession, # Provided for compatibility with CDXML cmdlets, not actually used. [Int32] $ThrottleLimit, [Switch] $AsJob ) Begin { $SourceCaller = "Microsoft.PowerShell" [flagsattribute()] Enum FileSystemType { NTFS = 14 ReFS = 15 CSVFS_NTFS = 32768 CSVFS_ReFS = 32769 } [flagsattribute()] Enum ProvisioningType { Unknown = 0 Thin = 1 Fixed = 2 } [flagsattribute()] Enum MediaType { HDD = 3 SSD = 4 SCM = 5 } } Process { $info = $resources.info $p = $null if (-not $CimSession) { $CimSession = New-CimSession } ## Gather the instance object switch ($psCmdlet.ParameterSetName) { "ByStoragePool" { if ($StoragePool -ne $null) { $PoolInstance = $StoragePool } else { # Also accept the existence of a single non-primordial pool as unambiguous $PoolInstance = Get-StoragePool |? IsPrimordial -eq $false if ($PoolInstance -eq $null -or $PoolInstance.Count -gt 1) { $errorObject = CreateErrorRecord -ErrorId "InvalidArgument" ` -ErrorMessage "Storage pool must be specified by one of the 'StoragePool' parameters" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidArgument) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } } break } "ByStoragePoolFriendlyName" { $PoolInstance = Get-StoragePool -FriendlyName $StoragePoolFriendlyName -CimSession $CimSession -ErrorAction Stop; break; } "ByStoragePoolName" { $PoolInstance = Get-StoragePool -Name $StoragePoolName -CimSession $CimSession -ErrorAction Stop; break; } "ByStoragePoolUniqueId" { $PoolInstance = Get-StoragePool -UniqueId $StoragePoolUniqueId -CimSession $CimSession -ErrorAction Stop; break; } "ByDisk" { $DiskInstance = $Disk; break; } "ByDiskNumber" { $DiskInstance = Get-Disk -Number $DiskNumber -CimSession $CimSession -ErrorAction Stop; break; } "ByDiskPath" { $DiskInstance = Get-Disk -Path $DiskPath -CimSession $CimSession -ErrorAction Stop; break; } "ByDiskUniqueId" { $DiskInstance = Get-Disk -UniqueId $DiskUniqueId -CimSession $CimSession -ErrorAction Stop; break; } } if (-not $AsJob) { if ($PoolInstance) { Write-Progress -Activity "New-Volume" -PercentComplete 0 -CurrentOperation "Validating parameters" -Status "0/3" } elseif ($DiskInstance) { Write-Progress -Activity "New-Volume" -PercentComplete 0 -CurrentOperation "Validating parameters" -Status "0/2" } } ## Populate arguments list $arguments = @{} if ($PSBoundParameters.ContainsKey("FriendlyName")) { $arguments.Add("FriendlyName", $FriendlyName) } if ($PSBoundParameters.ContainsKey("FileSystem")) { $arguments.Add("FileSystem", [System.UInt16]$FileSystem) } if ($PSBoundParameters.ContainsKey("AccessPath") -and $PSBoundParameters.ContainsKey("DriveLetter")) { $errorObject = CreateErrorRecord -ErrorId "InvalidArgument" ` -ErrorMessage "Use either 'AccessPath' or 'DriveLetter' parameter" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidArgument) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } if ($PSBoundParameters.ContainsKey("AccessPath")) { $arguments.Add("AccessPath", $AccessPath) } elseif ($PSBoundParameters.ContainsKey("DriveLetter")) { $arguments.Add("AccessPath", $DriveLetter + ":") } if ($PSBoundParameters.ContainsKey("AllocationUnitSize")) { $arguments.Add("AllocationUnitSize", $AllocationUnitSize) } if ($PoolInstance) { if ($PSBoundParameters.ContainsKey("Size")) { $arguments.Add("Size", $Size) } if ($PSBoundParameters.ContainsKey("ResiliencySettingName")) { $arguments.Add("ResiliencySettingName", $ResiliencySettingName) } if ($PSBoundParameters.ContainsKey("ProvisioningType")) { $arguments.Add("ProvisioningType", [System.UInt16]$ProvisioningType) } if ($PSBoundParameters.ContainsKey("MediaType")) { $arguments.Add("MediaType", [System.UInt16]$MediaType) } if ($PSBoundParameters.ContainsKey("PhysicalDiskRedundancy")) { $arguments.Add("PhysicalDiskRedundancy", $PhysicalDiskRedundancy) } if ($PSBoundParameters.ContainsKey("NumberOfDataCopies")) { $arguments.Add("NumberOfDataCopies", $NumberOfDataCopies) } if ($PSBoundParameters.ContainsKey("NumberOfColumns")) { $arguments.Add("NumberOfColumns", $NumberOfColumns) } if ($PSBoundParameters.ContainsKey("NumberOfGroups")) { $arguments.Add("NumberOfGroups", $NumberOfGroups) } if ($PSBoundParameters.ContainsKey("StorageFaultDomainsToUse") -and $PSBoundParameters.ContainsKey("StorageFaultDomainsToUseFriendlyNames")) { $errorObject = CreateErrorRecord -ErrorId "InvalidArgument" ` -ErrorMessage "Use either 'StorageFaultDomainsToUse' or 'StorageFaultDomainsToUseFriendlyNames' parameter" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidArgument) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } if ($PSBoundParameters.ContainsKey("StorageFaultDomainsToUse")) { $arguments.Add("StorageFaultDomainsToUse", $StorageFaultDomainsToUse) } elseif ($PSBoundParameters.ContainsKey("StorageFaultDomainsToUseFriendlyNames")) { [Microsoft.Management.Infrastructure.CimInstance[]] $faultdomains = @() $subsystem = $PoolInstance | get-storagesubsystem -CimSession $CimSession $faultdomains = ValidateFaultDomainsExist -StorageFaultDomainFriendlyNames $StorageFaultDomainsToUseFriendlyNames ` -StorageSubsystem $subsystem ` -CimSession $CimSession if ($faultdomains.Count -eq 0) { return } $arguments.Add("StorageFaultDomainsToUse", $faultdomains) } if ($PSBoundParameters.ContainsKey("StorageTiers") -and $PSBoundParameters.ContainsKey("StorageTierFriendlyNames")) { $errorObject = CreateErrorRecord -ErrorId "InvalidArgument" ` -ErrorMessage "Use either 'StorageTiers' or 'StorageTierFriendlyNames' parameter" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidArgument) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } if ($PSBoundParameters.ContainsKey("StorageTiers")) { $arguments.Add("StorageTiers", $StorageTiers) } elseif ($PSBoundParameters.ContainsKey("StorageTierFriendlyNames")) { [Microsoft.Management.Infrastructure.CimInstance[]] $tiers = @() $poolTiers = Get-CimAssociatedInstance -InputObject $PoolInstance ` -Association MSFT_StoragePoolToStorageTier ` -ResultClassName MSFT_StorageTier ` -CimSession $CimSession ` -ErrorAction Stop for ($i = 0; $i -lt $StorageTierFriendlyNames.Count; $i++) { $found = $false foreach ($tier in $poolTiers) { if ($tier.FriendlyName -eq $StorageTierFriendlyNames[$i]) { $tiers += $tier $found = $true break } } if ($found -eq $false) { $errorObject = CreateErrorRecord -ErrorId "InvalidArgument" ` -ErrorMessage "Could not find storage tier with the friendly name '$($StorageTierFriendlyNames[$i])' in the storage pool" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidArgument) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } } $arguments.Add("StorageTiers", $tiers) } if ($PSBoundParameters.ContainsKey("StorageTierSizes")) { $arguments.Add("StorageTierSizes", $StorageTierSizes) } if ($PSBoundParameters.ContainsKey("WriteCacheSize")) { $arguments.Add("WriteCacheSize", $WriteCacheSize) } if ($PSBoundParameters.ContainsKey("ReadCacheSize")) { $arguments.Add("ReadCacheSize", $ReadCacheSize) } if ($PSBoundParameters.ContainsKey("UseMaximumSize")) { $arguments.Add("UseMaximumSize", $UseMaximumSize.ToBool()) } } # Would use a closure here, but jobs are run in their own session state. $poolblock = { param($session, $asjob, $pool, $arguments) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } $errorObject = $null $output = $null $retry = 0 $time = 1 $virtualdisk = $null $volume = $null $subsystem = $pool | get-storagesubsystem -CimSession $session # If the subsystem model is "Windows Storage", # perform the individual steps if ($subsystem.Model -like "*Windows Storage*") { $volCreateParams = @{} if ($arguments.ContainsKey("FriendlyName")) { $volCreateParams.Add("FriendlyName", $arguments["FriendlyName"]) } if ($arguments.ContainsKey("FileSystem")) { $volCreateParams.Add("FileSystem", $arguments["FileSystem"]) $arguments.Remove("FileSystem") } elseif ($subsystem.Model -eq "Clustered Windows Storage" -and $subsystem.StorageConnectionType -eq "Local Storage" -and $subsystem.SupportedFileSystems -contains [FilesystemType]::CSVFS_ReFS) { # If the subsystem is clustered local (storage spaces direct) and supports it, # default to CSVFS + ReFS. $volCreateParams.Add("FileSystem", [System.Uint16][FilesystemType]::CSVFS_ReFS) } if ($arguments.ContainsKey("AccessPath")) { $volCreateParams.Add("AccessPath", $arguments["AccessPath"]) $arguments.Remove("AccessPath") } if ($arguments.ContainsKey("AllocationUnitSize")) { $volCreateParams.Add("AllocationUnitSize", $arguments["AllocationUnitSize"]) $arguments.Remove("AllocationUnitSize") } try { if (-not $asjob) { Write-Progress -Activity "New-Volume" -PercentComplete 10 -CurrentOperation "Creating virtual disk" -Status "1/3" } $progressPreference = "silentlyContinue" $output = Invoke-CimMethod -CimSession $session -InputObject $pool -MethodName CreateVirtualDisk -Arguments $arguments -ErrorAction Stop $progressPreference = "Continue" $virtualdisk = $output.CreatedVirtualDisk do { $disk = $output.CreatedVirtualDisk | get-disk -CimSession $session if ($disk -or ($retry -ge 3)) { break } Start-Sleep -Seconds $time $retry++ $time = $time * 2 } while ($disk -eq $null) if ($disk -eq $null) { $errorObject = CreateErrorRecord -ErrorId "ObjectNotFound" ` -ErrorMessage "The disk associated with the virtual disk created could not be found." ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } if (-not $asjob) { Write-Progress -Activity "New-Volume" -PercentComplete 70 -CurrentOperation "Creating volume" -Status "2/3" } $progressPreference = "silentlyContinue" $output = Invoke-CimMethod -CimSession $session -InputObject $disk -MethodName CreateVolume -Arguments $volCreateParams -ErrorAction Stop $progressPreference = "Continue" $volume = $output.CreatedVolume } catch { $progressPreference = "Continue" if (-not $asjob) { Write-Progress -Activity "New-Volume" -PercentComplete 90 -CurrentOperation "Error encountered. Checking if cleanup is required." -Status "2/3" } $errorObject = CreateErrorRecord -ErrorId $_.FullyQualifiedErrorId ` -ErrorMessage $null ` -ErrorCategory $_.CategoryInfo.Category ` -Exception $_.Exception ` -TargetObject $_.TargetObject # If virtual disk was created and volume was formatted, but # adding to cluster failed or the cluster resource did not # come online after successful addition, do not revert. if (($virtualdisk) -and ($_.FullyQualifiedErrorId -notmatch "46008") -and ($_.FullyQualifiedErrorId -notmatch "46011")) { $progressPreference = "silentlyContinue" $virtualdisk | Remove-VirtualDisk -CimSession $session -Confirm:$false $progressPreference = "Continue" } } finally { if (-not $asjob) { Write-Progress -Activity "New-Volume" -Completed -Status "3/3" } } } # Else fallback to the API on the storage pool else { try { if (-not $asjob) { Write-Progress -Activity "New-Volume" -PercentComplete 10 -CurrentOperation "Creating volume" -Status "1/2" } $progressPreference = "silentlyContinue" $output = Invoke-CimMethod -CimSession $session -InputObject $pool -MethodName CreateVolume -Arguments $arguments -ErrorAction Stop $progressPreference = "Continue" $volume = $output.CreatedVolume } catch { $progressPreference = "Continue" $errorObject = CreateErrorRecord -ErrorId $_.FullyQualifiedErrorId ` -ErrorMessage $null ` -ErrorCategory $_.CategoryInfo.Category ` -Exception $_.Exception ` -TargetObject $_.TargetObject } finally { if (-not $asjob) { Write-Progress -Activity "New-Volume" -Completed -Status "2/2" } } } if ($errorObject) { $psCmdlet.WriteError($errorObject) } else { $volume } } # Would use a closure here, but jobs are run in their own session state. $diskblock = { param($session, $asjob, $disk, $arguments) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } $errorObject = $null $output = $null $volume = $null try { if (-not $asjob) { Write-Progress -Activity "New-Volume" -PercentComplete 10 -CurrentOperation "Creating volume" -Status "1/2" } $progressPreference = "silentlyContinue" $output = Invoke-CimMethod -CimSession $session -InputObject $disk -MethodName CreateVolume -Arguments $arguments -ErrorAction Stop $progressPreference = "Continue" $volume = $output.CreatedVolume } catch { $progressPreference = "Continue" $errorObject = CreateErrorRecord -ErrorId $_.FullyQualifiedErrorId ` -ErrorMessage $null ` -ErrorCategory $_.CategoryInfo.Category ` -Exception $_.Exception ` -TargetObject $_.TargetObject } finally { if (-not $asjob) { Write-Progress -Activity "New-Volume" -Completed -Status "3/3" } } if ($errorObject) { $psCmdlet.WriteError($errorObject) } else { $volume } } if ($asjob) { $p = $true if ($PoolInstance) { Start-Job -Name CreateVolume -ScriptBlock $poolblock -ArgumentList @($CimSession, $p, $PoolInstance, $arguments) } elseif ($DiskInstance) { Start-Job -Name CreateVolume -ScriptBlock $diskblock -ArgumentList @($CimSession, $p, $DiskInstance, $arguments) } } else { if ($pscmdlet.ShouldProcess($info, $resources.warning, $info)) { if ($PoolInstance) { &$poolblock $CimSession $p $PoolInstance $arguments } elseif ($DiskInstance) { &$diskblock $CimSession $p $DiskInstance $arguments } } } } } function Get-StorageExtendedStatus { [CmdletBinding()] Param ( #### -------------------- Parameter sets -------------------------------------#### [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageJob")] [Parameter( ParameterSetName = 'ByStorageJob', Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullOrEmpty()] $StorageJob, #### -------------------- Powershell parameters ------------------------------#### [Microsoft.Management.Infrastructure.CimSession] $CimSession ) Process { if (-not $CimSession) { $CimSession = New-CimSession } ## Gather the instance objects switch ($psCmdlet.ParameterSetName) { "ByStorageJob" { $inputObject = $StorageJob; break; } } $extendedStatus = $null try { $output = Invoke-CimMethod -MethodName "GetExtendedStatus" -InputObject $inputObject -CimSession $CimSession -ErrorAction Stop $extendedStatus = $output.ExtendedStatus } catch { $errorObject = CreateErrorRecord -ErrorId $_.FullyQualifiedErrorId ` -ErrorMessage $null ` -ErrorCategory $_.CategoryInfo.Category ` -Exception $_.Exception ` -TargetObject $_.TargetObject $psCmdlet.WriteError($errorObject) } $extendedStatus } } function Get-StorageAdvancedProperty { [CmdletBinding()] Param ( [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_PhysicalDisk")] [Parameter( ParameterSetName = 'ByPhysicalDisk', Mandatory = $true, ValueFromPipeline = $true)] $PhysicalDisk ) Process { if ( $PhysicalDisk ) { $isDeviceCacheEnabled = $null $isPowerProtected = $null $deviceCacheOutput = Invoke-CimMethod -MethodName IsDeviceCacheEnabled -InputObject $PhysicalDisk -ErrorAction SilentlyContinue $powerProtectedOutput = Invoke-CimMethod -MethodName IsPowerProtected -InputObject $PhysicalDisk -ErrorAction SilentlyContinue # DeviceCache error handling if ( $deviceCacheOutput -and $deviceCacheOutput.ReturnValue -ne 0 ) { Write-Warning "Retrieving IsDeviceCacheEnabled failed with ErrorCode $( $deviceCacheOutput.ReturnValue )." } else { $isDeviceCacheEnabled = $deviceCacheOutput.IsDeviceCacheEnabled } # PowerProtected error handling if ( $powerProtectedOutput -and $powerProtectedOutput.ReturnValue -ne 0 ) { Write-Warning "Retrieving IsPowerProtected failed with ErrorCode $( $powerProtectedOutput.ReturnValue )." } else { $isPowerProtected = $powerProtectedOutput.IsPowerProtected } $customDrive = [pscustomobject]@{ PhysicalDisk = $PhysicalDisk; IsPowerProtected = $isPowerProtected; IsDeviceCacheEnabled = $isDeviceCacheEnabled; } $customDrive.PSObject.TypeNames.Insert( 0, "Microsoft.Windows.StorageManagement.PhysicalDiskAdvancedProperties" ) $customDrive } } } function Get-StorageHealthReport { [CmdletBinding()] Param ( [CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageObject")] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "InputObject")] $InputObject, [Int32] $Count = 1, [Microsoft.Management.Infrastructure.CimSession] $CimSession, # Provided for compatibility with CDXML cmdlets, not actually used. [Int32] $ThrottleLimit, [Switch] $AsJob ) Begin{ Write-Warning "This cmdlet is deprecated and may not be available in the future. Use Get-ClusterPerformanceHistory instead." } Process { $info = $resources.info $p = $null $methodName = "GetReport" if (-not $CimSession) { $CimSession = New-CimSession } if ($InputObject.CimClass.CimClassName -ne "MSFT_StoragesubSystem" -and $InputObject.CimClass.CimClassName -ne "MSFT_StorageNode" -and $InputObject.CimClass.CimClassName -ne "MSFT_Volume" -and $InputObject.CimClass.CimClassName -ne "MSFT_FileShare") { $errorObject = CreateErrorRecord -ErrorId "InvalidArgument" ` -ErrorMessage "The input object must be a StorageSubSystem, StorageNode, Volume or FileShare" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidArgument) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } if($InputObject.CimSystemProperties.ClassName.Equals("MSFT_FileShare")) { $InputObject = $InputObject | Get-Volume -CimSession $CimSession -ErrorAction Stop } $storageSubSystem = $InputObject | Get-StorageSubSystem -CimSession $CimSession -ErrorAction Stop $StorageHealth = Get-CimAssociatedInstance -ResultClassName MSFT_StorageHealth -InputObject $storageSubSystem -CimSession $CimSession -ErrorAction Stop # Would use a closure here, but jobs are run in their own session state. $block = { param($session, $ns, $asjob, $mn, $sh, $io, $co) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } Invoke-CimMethod -CimSession $session -InputObject $sh -MethodName $mn -Arguments @{TargetObject=$io;Count=[uint32]$co} -Confirm:$false } if ($asjob) { $p = $true Start-Job -ScriptBlock $block -ArgumentList @($CimSession, $StorageNamespace, $p, $methodName, $StorageHealth, $InputObject, $Count) } else { if ($pscmdlet.ShouldProcess($info, $resources.warning, $info)) { &$block $CimSession $StorageNamespace $p $methodName $StorageHealth $InputObject $Count } } } } function Get-StorageHealthSetting { [CmdletBinding()] Param ( [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageSubSystem")] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "InputObject")] $InputObject, [String] $Name, [Microsoft.Management.Infrastructure.CimSession] $CimSession, # Provided for compatibility with CDXML cmdlets, not actually used. [Int32] $ThrottleLimit, [Switch] $AsJob ) Begin{} Process { $info = $resources.info $p = $null if (-not $CimSession) { $CimSession = New-CimSession } $StorageHealth = $InputObject | Get-StorageHealth -CimSession $CimSession -ErrorAction Stop # Would use a closure here, but jobs are run in their own session state. $block = { param($session, $asjob, $sh, $na) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { # We need to load the module or it fails with CMDLET Not Found import-module Storage\StorageHealth.cdxml $session = $session | New-CimSession } $sh | Get-StorageHealthSettingInternal -Name $na -CimSession $session -ErrorAction $ErrorActionPreference } if ($asjob) { $p = $true Start-Job -ScriptBlock $block -ArgumentList @($CimSession, $p, $StorageHealth, $Name) } else { if ($pscmdlet.ShouldProcess($info, $resources.warning, $info)) { &$block $CimSession $p $StorageHealth $Name } } } } function Set-StorageHealthSetting { [CmdletBinding()] Param ( [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageSubSystem")] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "InputObject")] $InputObject, [String] [Parameter( Mandatory = $true)] $Name, [String] [Parameter( Mandatory = $true)] $Value, [Microsoft.Management.Infrastructure.CimSession] $CimSession, # Provided for compatibility with CDXML cmdlets, not actually used. [Int32] $ThrottleLimit, [Switch] $AsJob ) Begin{} Process { $info = $resources.info $p = $null if (-not $CimSession) { $CimSession = New-CimSession } $StorageHealth = $InputObject | Get-StorageHealth -CimSession $CimSession -ErrorAction Stop # Would use a closure here, but jobs are run in their own session state. $block = { param($session, $asjob, $sh, $na, $va) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { # We need to load the module or it fails with CMDLET Not Found import-module Storage\StorageHealth.cdxml $session = $session | New-CimSession } $sh | Set-StorageHealthSettingInternal -Name $na -Value $va -CimSession $session -ErrorAction $ErrorActionPreference } if ($asjob) { $p = $true Start-Job -ScriptBlock $block -ArgumentList @($CimSession, $p, $StorageHealth, $Name, $Value) } else { if ($pscmdlet.ShouldProcess($info, $resources.warning, $info)) { &$block $CimSession $p $StorageHealth $Name $Value } } } } function Remove-StorageHealthSetting { [CmdletBinding()] Param ( [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageSubSystem")] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "InputObject")] $InputObject, [String] [Parameter( Mandatory = $true)] $Name, [Microsoft.Management.Infrastructure.CimSession] $CimSession, # Provided for compatibility with CDXML cmdlets, not actually used. [Int32] $ThrottleLimit, [Switch] $AsJob ) Begin{} Process { $info = $resources.info $p = $null if (-not $CimSession) { $CimSession = New-CimSession } $StorageHealth = $InputObject | Get-StorageHealth -CimSession $CimSession -ErrorAction Stop # Would use a closure here, but jobs are run in their own session state. $block = { param($session, $asjob, $sh, $na) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { import-module Storage\StorageHealth.cdxml $session = $session | New-CimSession } $sh | Remove-StorageHealthSettingInternal -Name $na -CimSession $session -ErrorAction $ErrorActionPreference } if ($asjob) { $p = $true Start-Job -ScriptBlock $block -ArgumentList @($CimSession, $p, $StorageHealth, $Name) } else { if ($pscmdlet.ShouldProcess($info, $resources.warning, $info)) { &$block $CimSession $p $StorageHealth $Name } } } } function Debug-StorageSubSystem { [CmdletBinding( DefaultParameterSetName="ByFriendlyName" )] Param ( [string[]] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "ByFriendlyName", Position = 0)] $StorageSubSystemFriendlyName, [string[]] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "ByUniqueId")] $StorageSubSystemUniqueId, [string[]] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "ByName")] $StorageSubSystemName, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageSubSystem")] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "InputObject")] $InputObject, [Microsoft.Management.Infrastructure.CimSession] $CimSession, # Provided for compatibility with CDXML cmdlets, not actually used. [Int32] $ThrottleLimit, [Switch] $AsJob ) Begin{ Write-Warning "This cmdlet is deprecated and may not be available in the future. Use Get-HealthFault instead." } Process { $info = $resources.info $p = $null if (-not $CimSession) { $CimSession = New-CimSession } switch ($PsCmdlet.ParameterSetName) { "ByName" { $InputObject = Get-StorageSubSystem -Name $StorageSubSystemName -CimSession $CimSession; break; } "ByUniqueId" { $InputObject = Get-StorageSubSystem -UniqueId $StorageSubSystemUniqueId -CimSession $CimSession; break; } "ByFriendlyName" { $InputObject = Get-StorageSubSystem -FriendlyName $StorageSubSystemFriendlyName -CimSession $CimSession; break; } } # Would use a closure here, but jobs are run in their own session state. $block = { param($session, $asjob, $io) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } $result = @() $output = Invoke-CimMethod -MethodName Diagnose -InputObject $io -CimSession $session foreach($i in $output){$result += $i.ItemValue} $result | Sort-Object -Property PerceivedSeverity } if ($asjob) { $p = $true Start-Job -ScriptBlock $block -ArgumentList @($CimSession, $p, $InputObject) } else { if ($pscmdlet.ShouldProcess($info, $resources.warning, $info)) { &$block $CimSession $p $InputObject } } } } function Debug-FileShare { [CmdletBinding( DefaultParameterSetName="ByName" )] Param ( [string[]] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "ByName", Position = 0 )] $Name, [string[]] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "ByUniqueId")] $UniqueId, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_FileShare")] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "InputObject")] $InputObject, [Microsoft.Management.Infrastructure.CimSession] $CimSession, # Provided for compatibility with CDXML cmdlets, not actually used. [Int32] $ThrottleLimit, [Switch] $AsJob ) Begin{ Write-Warning "This cmdlet is deprecated and may not be available in the future. Use Get-HealthFault instead." } Process { $info = $resources.info $p = $null if (-not $CimSession) { $CimSession = New-CimSession } switch ($PsCmdlet.ParameterSetName) { "ByName" { $InputObject = Get-FileShare -Name $Name -CimSession $CimSession -ErrorAction stop; break; } "ByUniqueId" { $InputObject = Get-FileShare -UniqueId $UniqueId -CimSession $CimSession -ErrorAction stop; break; } } # Would use a closure here, but jobs are run in their own session state. $block = { param($session, $asjob, $io) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } $result = @() $output = Invoke-CimMethod -MethodName Diagnose -InputObject $io -CimSession $session foreach($i in $output){$result += $i.ItemValue} $result | Sort-Object -Property PerceivedSeverity } if ($asjob) { $p = $true Start-Job -ScriptBlock $block -ArgumentList @($CimSession, $p, $InputObject) } else { if ($pscmdlet.ShouldProcess($info, $resources.warning, $info)) { &$block $CimSession $p $InputObject } } } } function Debug-Volume { [CmdletBinding( DefaultParameterSetName="ByDriveLetter" )] Param ( [char[]] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "ByDriveLetter", Position = 0)] $DriveLetter, [string[]] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "ById")] $ObjectId, [string[]] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "ByPaths")] $Path, [string[]] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "ByLabel")] [Alias("FriendlyName")] $FileSystemLabel, [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_Volume")] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "InputObject")] $InputObject, [Microsoft.Management.Infrastructure.CimSession] $CimSession, # Provided for compatibility with CDXML cmdlets, not actually used. [Int32] $ThrottleLimit, [Switch] $AsJob ) Begin{ Write-Warning "This cmdlet is deprecated and may not be available in the future. Use Get-HealthFault instead." } Process { $info = $resources.info $p = $null if (-not $CimSession) { $CimSession = New-CimSession } switch ($PsCmdlet.ParameterSetName) { "ByDriveLetter" { $io = Get-Volume -CimSession $CimSession -DriveLetter $DriveLetter -ErrorAction stop; break;} "ById" { $io = Get-Volume -CimSession $CimSession -ObjectId $ObjectId -ErrorAction stop; break; } "ByPaths" { $io = Get-Volume -CimSession $CimSession -Path $Path -ErrorAction stop; break;} "ByLabel" { $io = Get-Volume -CimSession $CimSession -FileSystemLabel $FileSystemLabel -ErrorAction stop; break;} } # Would use a closure here, but jobs are run in their own session state. $block = { param($session, $asjob, $io) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } $result = @() $output = Invoke-CimMethod -MethodName Diagnose -InputObject $io -CimSession $session foreach($i in $output){$result += $i.ItemValue} $result | Sort-Object -Property PerceivedSeverity } if ($asjob) { $p = $true Start-Job -ScriptBlock $block -ArgumentList @($CimSession, $p, $InputObject) } else { if ($pscmdlet.ShouldProcess($info, $resources.warning, $info)) { &$block $CimSession $p $InputObject } } } } function Enable-StorageMaintenanceMode { [CmdletBinding()] Param ( [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageFaultDomain")] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "InputObject")] $InputObject, [Switch] $IgnoreDetachedVirtualDisks, [System.Nullable[Bool]] $ValidateVirtualDisksHealthy, [String] $Model, [String] $Manufacturer, [Microsoft.Management.Infrastructure.CimSession] $CimSession, [System.Nullable[Bool]] $ValidateMaintenanceMode, [Switch] $AsJob ) Begin { [flagsattribute()] Enum ValidationFlags { None = 0 VirtualDisksHealthy = 1 } } Process { $info = $resources.info $p = $null if (-not $CimSession) { $CimSession = New-CimSession } if ($InputObject.CimClass.CimClassName -ne "MSFT_PhysicalDisk" -and $InputObject.CimClass.CimClassName -ne "MSFT_StorageEnclosure" -and $InputObject.CimClass.CimClassName -ne "MSFT_StorageScaleUnit") { $errorObject = CreateErrorRecord -ErrorId "InvalidArgument" ` -ErrorMessage "The input object must be a PhysicalDisk, StorageEnclosure or StorageScaleUnit" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidArgument) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } $arguments = @{} $arguments.Add("EnableMaintenanceMode", $true) $arguments.Add("IgnoreDetachedVirtualDisks", $IgnoreDetachedVirtualDisks.IsPresent) if ($InputObject.CimClass.CimClassName -ne "MSFT_PhysicalDisk") { if ($PSBoundParameters.ContainsKey("Model")) { $arguments.Add("Model", $Model) } if ($PSBoundParameters.ContainsKey("Manufacturer")) { $arguments.Add("Manufacturer", $Manufacturer) } } $includeValidationFlags = $false if ($ValidateVirtualDisksHealthy -ne $null) { $includeValidationFlags = $true if($ValidateVirtualDisksHealthy) { $validationFlags += [ValidationFlags]::VirtualDisksHealthy } else { $validationFlags += [ValidationFlags]::None } } if ($includeValidationFlags) { $arguments.Add("ValidationFlags", $validationFlags) } if ($ValidateMaintenanceMode -ne $null) { $arguments.Add("ValidateMaintenanceMode", $ValidateMaintenanceMode) } $storageSubSystem = $InputObject | Get-StorageSubSystem -CimSession $CimSession if ($storageSubSystem.Model -eq "Clustered Windows Storage") { $storageHealth = Get-CimAssociatedInstance -ResultClassName MSFT_StorageHealth -InputObject $storageSubSystem -CimSession $CimSession $arguments.Add("TargetObject", $InputObject) } else { if ($arguments["ValidationFlags"] -ne $null) { $errorObject = CreateErrorRecord -ErrorId "InvalidArgument" ` -ErrorMessage "The parameter 'ValidateVirtualDisksHealthy' is not supported in this subsystem. Invoke again without this parameter." ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidArgument) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } } # Would use a closure here, but jobs are run in their own session state. $block = { param($session, $asjob, $io, $arg, $ss, $sh) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } if ($ss.Model -eq "Clustered Windows Storage") { Invoke-CimMethod -MethodName "Maintenance" -Arguments $arg -InputObject $sh -CimSession $session | Out-Null } else { Invoke-CimMethod -MethodName "Maintenance" -Arguments $arg -InputObject $io -CimSession $session | Out-Null } } if ($asjob) { $p = $true Start-Job -ScriptBlock $block -ArgumentList @($CimSession, $p, $InputObject, $arguments, $storageSubSystem, $storageHealth) } else { if ($pscmdlet.ShouldProcess($info, $resources.warning, $info)) { &$block $CimSession $p $InputObject $arguments $storageSubSystem $storageHealth } } } } function Disable-StorageMaintenanceMode { [CmdletBinding()] Param ( [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageFaultDomain")] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "InputObject")] $InputObject, [string] $Model, [string] $Manufacturer, [Microsoft.Management.Infrastructure.CimSession] $CimSession, [Switch] $AsJob ) Begin{} Process { $info = $resources.info $p = $null if (-not $CimSession) { $CimSession = New-CimSession } if ($InputObject.CimClass.CimClassName -ne "MSFT_PhysicalDisk" -and $InputObject.CimClass.CimClassName -ne "MSFT_StorageEnclosure" -and $InputObject.CimClass.CimClassName -ne "MSFT_StorageScaleUnit") { $errorObject = CreateErrorRecord -ErrorId "InvalidArgument" ` -ErrorMessage "The input object must be a PhysicalDisk, StorageEnclosure or StorageScaleUnit" ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidArgument) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } $arguments = @{} $arguments.Add("EnableMaintenanceMode", $false) if($InputObject.CimClass.CimClassName -ne "MSFT_PhysicalDisk") { if ($PSBoundParameters.ContainsKey("Model")) { $arguments.Add("Model", $Model) } if ($PSBoundParameters.ContainsKey("Manufacturer")) { $arguments.Add("Manufacturer", $Manufacturer) } } $storageSubSystem = $InputObject | Get-StorageSubSystem -CimSession $CimSession if ($storageSubSystem.Model -eq "Clustered Windows Storage") { $arguments.Add("TargetObject", $InputObject) $storageHealth = Get-CimAssociatedInstance -ResultClassName MSFT_StorageHealth -InputObject $storageSubSystem -CimSession $CimSession } # Would use a closure here, but jobs are run in their own session state. $block = { param($session, $asjob, $io, $arg, $ss, $sh) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession } if ($ss.Model -eq "Clustered Windows Storage") { Invoke-CimMethod -MethodName "Maintenance" -Arguments $arg -InputObject $sh -CimSession $session | Out-Null } else { Invoke-CimMethod -MethodName "Maintenance" -Arguments $arg -InputObject $io -CimSession $session | Out-Null } } if ($asjob) { $p = $true Start-Job -ScriptBlock $block -ArgumentList @($CimSession, $p, $InputObject, $arguments, $storageSubSystem, $storageHealth) } else { if ($pscmdlet.ShouldProcess($info, $resources.warning, $info)) { &$block $CimSession $p $InputObject $arguments $storageSubSystem $storageHealth } } } } function Get-StorageDiagnosticInfo { [CmdletBinding()] Param ( #### -------------------- Parameter sets -------------------------------------#### [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageSubSystem")] [Parameter( ParameterSetName = "ByStorageSubSystem", ValueFromPipeline = $true, Mandatory = $true)] [ValidateNotNullOrEmpty()] $InputObject, [String] [Parameter( ParameterSetName = "ByStorageSubSystemFriendlyName", Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] $StorageSubSystemFriendlyName, [String] [Parameter( ParameterSetName = "ByStorageSubSystemName", Mandatory = $true)] [ValidateNotNullOrEmpty()] $StorageSubSystemName, [String] [Parameter( ParameterSetName = "ByStorageSubSystemUniqueId", Mandatory = $true)] [ValidateNotNullOrEmpty()] [Alias("StorageSubSystemId")] $StorageSubSystemUniqueId, #### -------------------- Method parameters ----------------------------------#### [String] [Parameter( ParameterSetName = 'ByStorageSubSystem', Mandatory = $true)] [Parameter( ParameterSetName = 'ByStorageSubSystemFriendlyName', Mandatory = $true)] [Parameter( ParameterSetName = 'ByStorageSubSystemName', Mandatory = $true)] [Parameter( ParameterSetName = 'ByStorageSubSystemUniqueId', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Alias("Path")] $DestinationPath, [UInt32] [Parameter( ParameterSetName = 'ByStorageSubSystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubSystemFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubSystemName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubSystemUniqueId', Mandatory = $false)] $TimeSpan, [String] [Parameter( ParameterSetName = 'ByStorageSubSystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubSystemFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubSystemName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubSystemUniqueId', Mandatory = $false)] $ActivityId, [System.Management.Automation.SwitchParameter] [Parameter( ParameterSetName = 'ByStorageSubSystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubSystemFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubSystemName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubSystemUniqueId', Mandatory = $false)] $ExcludeOperationalLog = $false, [System.Management.Automation.SwitchParameter] [Parameter( ParameterSetName = 'ByStorageSubSystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubSystemFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubSystemName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubSystemUniqueId', Mandatory = $false)] $ExcludeDiagnosticLog = $false, [System.Management.Automation.SwitchParameter] [Parameter( ParameterSetName = 'ByStorageSubSystem', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubSystemFriendlyName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubSystemName', Mandatory = $false)] [Parameter( ParameterSetName = 'ByStorageSubSystemUniqueId', Mandatory = $false)] $IncludeLiveDump = $false, #### -------------------- Powershell parameters ------------------------------#### [Microsoft.Management.Infrastructure.CimSession] $CimSession, # Provided for compatibility with CDXML cmdlets, not actually used. [Int32] $ThrottleLimit, [Switch] $AsJob ) Process { $info = $resources.info $p = $null if (-not $CimSession) { $CimSession = New-CimSession } if (-not $AsJob) { Write-Progress -Activity "Get-StorageDiagnosticInfo" -Id 0 -PercentComplete 0 -CurrentOperation "Validating parameters" -Status "0/2" } ## Gather the instance object switch ($psCmdlet.ParameterSetName) { "ByStorageSubSystem" { $SubsystemInstance = $InputObject; break; } "ByStorageSubSystemFriendlyName" { $SubsystemInstance = Get-StorageSubsystem -FriendlyName $StorageSubSystemFriendlyName -CimSession $CimSession -ErrorAction Stop; break; } "ByStorageSubSystemName" { $SubsystemInstance = Get-StorageSubsystem -Name $StorageSubSystemName -CimSession $CimSession -ErrorAction Stop; break; } "ByStorageSubSystemUniqueId" { $SubsystemInstance = Get-StorageSubsystem -UniqueId $StorageSubSystemUniqueId -CimSession $CimSession -ErrorAction Stop; break; } } # Would use a closure here, but jobs are run in their own session state. $methodblock = { param($session, $asjob, $uniqueId, $arguments) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { # We need to load the module or it fails with CMDLET Not Found import-module Storage\StorageSubSystem.cdxml $session = $session | New-CimSession } $parameters = @{} $parameters.Add("CimSession", $session ) $parameters.Add("StorageSubSystemUniqueId", $uniqueId ) $parameters.Add("DestinationPath", $arguments["DestinationPath"] ) if ($arguments.ContainsKey("TimeSpan")) { $parameters.Add("TimeSpan", $arguments["TimeSpan"]) } if ($arguments.ContainsKey("ActivityId")) { $parameters.Add("ActivityId", $arguments["ActivityId"]) } if ($arguments.ContainsKey("ExcludeOperationalLog")) { $parameters.Add("ExcludeOperationalLog", $arguments["ExcludeOperationalLog"]) } if ($arguments.ContainsKey("ExcludeDiagnosticLog")) { $parameters.Add("ExcludeDiagnosticLog", $arguments["ExcludeDiagnosticLog"]) } if ($arguments.ContainsKey("IncludeLiveDump")) { $parameters.Add("IncludeLiveDump", $arguments["IncludeLiveDump"]) } if (-not $asjob) { Write-Progress -Activity "Get-StorageDiagnosticInfo" -Id 0 -PercentComplete 10 -CurrentOperation "Gathering storage diagnostic logs" -Status "1/2" } Get-StorageDiagnosticInfoInternal @parameters if (-not $asjob) { Write-Progress -Activity "Get-StorageDiagnosticInfo" -Completed -Status "2/2" } } if ($asjob) { $p = $true Start-Job -Name GetDiagnosticInfo ` -ScriptBlock $methodblock ` -ArgumentList @($CimSession, $p, $SubsystemInstance.UniqueId, $PSBoundParameters) } else { &$methodblock $CimSession $p $SubsystemInstance.UniqueId $PSBoundParameters } } } function Remove-StorageHealthIntent { [CmdletBinding( SupportsShouldProcess=$true, ConfirmImpact="High" )] Param ( [Microsoft.Management.Infrastructure.CimInstance] [PSTypeName("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_StorageObject")] [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "InputObject")] $InputObject, [Microsoft.Management.Infrastructure.CimSession] $CimSession, [Switch] $AsJob ) Process { $info = $resources.info if (-not $CimSession) { $CimSession = New-CimSession -Verbose:$false } $arguments = @{} $storageSubSystem = $InputObject | Get-StorageSubSystem -CimSession $CimSession if ($storageSubSystem.Model -eq "Clustered Windows Storage") { $storageHealth = Get-CimAssociatedInstance -ResultClassName MSFT_StorageHealth -InputObject $storageSubSystem -CimSession $CimSession $arguments.Add("TargetObject", $InputObject) } else { $errorObject = CreateErrorRecord -ErrorId "InvalidSubsystem" ` -ErrorMessage "This method is not supported for this subsystem." ` -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidArgument) ` -Exception $null ` -TargetObject $null $psCmdlet.WriteError($errorObject) return } # Would use a closure here, but jobs are run in their own session state. $block = { param($session, $asjob, $io, $arg, $sh) # Start-Job serializes/deserializes the CimSession, # which means it shows up here as having type Deserialized.CimSession. # Must recreate or cast the object in order to pass it to Get-CimInstance. if ($asjob) { $session = $session | New-CimSession -Verbose:$false } Invoke-CimMethod -MethodName "RemoveIntent" -Arguments $arg -InputObject $sh -CimSession $session | Out-Null } if ($asjob) { Start-Job -ScriptBlock $block -ArgumentList @($CimSession, $true, $InputObject, $arguments, $storageHealth) } else { if ($pscmdlet.ShouldProcess($info, $resources.warning, $info)) { &$block $CimSession $false $InputObject $arguments $storageHealth } } } } New-Alias Get-PhysicalDiskSNV Get-PhysicalDiskStorageNodeView New-Alias Get-DiskSNV Get-DiskStorageNodeView New-Alias Get-StorageEnclosureSNV Get-StorageEnclosureStorageNodeView Export-ModuleMember -Alias * -Function *