StoragePre2K12.psm1

function Add-ObjectDetail {
    [CmdletBinding()]
    param(
        [Parameter( Mandatory = $true,
            Position = 0,
            ValueFromPipeline = $true )]
        [ValidateNotNullOrEmpty()]
        [psobject[]]$InputObject,

        [Parameter( Mandatory = $false,
            Position = 1)]
        [string]$TypeName,

        [Parameter( Mandatory = $false,
            Position = 2)]
        [System.Collections.Hashtable]$PropertyToAdd,

        [Parameter( Mandatory = $false,
            Position = 3)]
        [ValidateNotNullOrEmpty()]
        [Alias('dp')]
        [System.String[]]$DefaultProperties,

        [boolean]$Passthru = $True
    )

    Begin {
        if ($PSBoundParameters.ContainsKey('DefaultProperties')) {
            # define a subset of properties
            $ddps = New-Object System.Management.Automation.PSPropertySet DefaultDisplayPropertySet, $DefaultProperties
            $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]$ddps
        }
    }
    Process {
        foreach ($Object in $InputObject) {
            switch ($PSBoundParameters.Keys) {
                'PropertyToAdd' {
                    foreach ($Key in $PropertyToAdd.Keys) {
                        #Add some noteproperties. Slightly faster than Add-Member.
                        $Object.PSObject.Properties.Add( ( New-Object System.Management.Automation.PSNoteProperty($Key, $PropertyToAdd[$Key]) ) )
                    }
                }
                'TypeName' {
                    #Add specified type
                    [void]$Object.PSObject.TypeNames.Insert(0, $TypeName)
                }
                'DefaultProperties' {
                    # Attach default display property set
                    Add-Member -InputObject $Object -MemberType MemberSet -Name PSStandardMembers -Value $PSStandardMembers
                }
            }
            if ($Passthru) {
                $Object
            }
        }
    }
}
function ConvertFrom-SizeString {
    param(
        [parameter(Mandatory, ValueFromPipeline)]
        [string]
        $String
    )

    process {
        switch -Regex ($String) {
            '^\d+ ?TB$' {
                [int64]$String.Replace('TB', '') * 1TB
            }

            '^\d+ ?GB$' {
                [int64]$String.Replace('GB', '') * 1GB
            }

            '^\d+ ?MB$' {
                [int64]$String.Replace('MB', '') * 1MB
            }

            '^\d+ ?KB$' {
                [int64]$String.Replace('KB', '') * 1KB
            }


            '^\d+ ?B$' {
                [int64]$String.Replace('B', '') * 1
            }
        }
    }
}
function Get-DiskPartDisk {
    [OutputType('System.Management.Automation.PSObject')]
    [CmdletBinding()]
    param (
    )

    # read disks from disk part
    $Output = "list disk" | diskpart

    ForEach ($Line in $Output -match ' Disk \d+') {
        try {
            # use regex to retrieve data from table
            if ($Line -Match " Disk (?<disknum>...) +(?<sts>.............) +(?<sz>.......) +(?<fr>.......) +(?<dyn>...) +(?<gpt>...)") {

                # create new disk object
                $Disk = [pscustomobject]@{
                    ComputerName         = $env:COMPUTERNAME
                    DiskNumber           = $Matches['disknum'].Trim()
                    Status               = $Matches['sts'].Trim()
                    Size                 = $Matches['sz'].Trim() | ConvertFrom-SizeString
                    Free                 = $Matches['fr'].Trim() | ConvertFrom-SizeString
                    Dyn                  = $Matches['dyn'].Trim()
                    PartitionStyle       = ""
                    DiskID               = ""
                    DetailType           = ""
                    DetailStatus         = ""
                    Path                 = ""
                    Target               = ""
                    LUNID                = ""
                    LocationPath         = ""
                    CurrentReadOnlyState = ""
                    ReadOnly             = ""
                    BootDisk             = ""
                    PagefileDisk         = ""
                    HibernationFileDisk  = ""
                    CrashdumpDisk        = ""
                    ClusteredDisk        = ""
                }

                # retrieve detailed disk data and loop data
                Foreach ($line in ("select disk $($Disk.DiskNumber)`ndetail disk" | diskpart)) {
                    If ($Line -cmatch "Disk ID" -and $Line -match ":") {
                        $Disk.DiskID = $Line.Split(":")[1].Trim()
                    } ElseIf ($Line.StartsWith("Type") -and $Line -match ":") {
                        $Disk.DetailType = $Line.Split(":")[1].Trim()
                    } ElseIf ($Line.StartsWith("Status") -and $Line -match ":") {
                        $Disk.DetailStatus = $Line.Split(":")[1].Trim()
                    } ElseIf ($Line.StartsWith("Path") -and $Line -match ":") {
                        $Disk.Path = $Line.Split(":")[1].Trim()
                    } ElseIf ($Line.StartsWith("Target") -and $Line -match ":") {
                        $Disk.Target = $Line.Split(":")[1].Trim()
                    } ElseIf ($Line.StartsWith("LUN ID") -and $Line -match ":") {
                        $Disk.LUNID = $Line.Split(":")[1].Trim()
                    } ElseIf ($Line.StartsWith("Location Path") -and $Line -match ":") {
                        $Disk.LocationPath = $Line.Split(":")[1].Trim()
                    } ElseIf ($Line.StartsWith("Current Read-only State") -and $Line -match ":") {
                        $Disk.CurrentReadOnlyState = $Line.Split(":")[1].Trim()
                    } ElseIf ($Line.StartsWith("Read-only") -and $Line -match ":") {
                        $Disk.ReadOnly = $Line.Split(":")[1].Trim()
                    } ElseIf ($Line.StartsWith("Boot Disk") -and $Line -match ":") {
                        $Disk.BootDisk = $Line.Split(":")[1].Trim()
                    } ElseIf ($Line.StartsWith("Pagefile Disk") -and $Line -match ":") {
                        $Disk.PagefileDisk = $Line.Split(":")[1].Trim()
                    } ElseIf ($Line.StartsWith("Hibernation File Disk") -and $Line -match ":") {
                        $Disk.HibernationFileDisk = $Line.Split(":")[1].Trim()
                    } ElseIf ($Line.StartsWith("Crashdump Disk") -and $Line -match ":") {
                        $Disk.CrashdumpDisk = $Line.Split(":")[1].Trim()
                    } ElseIf ($Line.StartsWith("Clustered Disk") -and $Line -match ":") {
                        $Disk.ClusteredDisk = $Line.Split(":")[1].Trim()
                    }
                }

                # retrieve detailed disk data and loop data
                Foreach ($line in ("select disk $($Disk.DiskNumber)`nUniqueID Disk" | diskpart)) {
                    If ($Line -cmatch "Disk ID" -and $Line -match ":") {
                        $DiskID = $Line.replace("Disk ID: ", "").Trim()

                        # if DiskID is a guid then partitionstyle is GPT
                        # otherwises its RAW, MBR or something else
                        if ([guid]::TryParse($DiskID, ([ref]$DiskID))) {
                            $Disk.PartitionStyle = "GPT"
                        } else {
                            if ($DiskID -eq "00000000") {
                                $Disk.PartitionStyle = "RAW"
                            } elseif ($DiskID -match "^[\d\w]{8}$") {
                                $Disk.PartitionStyle = "MBR"
                            } else {
                                $Disk.PartitionStyle = "Unknown"
                            }
                        }
                    }
                }

                # return disk
                $Disk
            }
        } Catch {
            Write-Error $_
            Continue
        }
    }
}
function Get-DiskPartPartition {
    [OutputType('System.Management.Automation.PSObject')]
    [CmdletBinding()]
    param (
    )

    Try {
        $Output = "list disk`n" | diskpart
    } Catch {
        Write-Error $_
        Continue
    }

    $Disks = ForEach ($Line in $Output) {
        If ($Line -match "^. Disk \d") {
            $Line
        }
    }
    $DiskCount = $Disks.Count

    For ($i = 0; $i -le ($DiskCount - 1); $i++) {
        $DiskNumber = $i
        Try {
            $Output = "Select disk $i`nlist partition`n" | diskpart
        } Catch {
            Write-Error $_
            Continue
        }

        $Partitions = @()
        ForEach ($Line in $Output) {
            If ($Line -match "^. Partition \d") {
                $Partitions += $Line
            }
        }

        $PartCount = $Partitions.Count

        For ($p = 0; $p -le ($Partcount - 1); $p++) {
            $Line = $Partitions[$p]
            If ($Line.StartsWith(" Partition")) {
                $Line -Match ". Partition (?<partnum>...) +(?<type>................) +(?<size>\d+ [MBG][ B]) +(?<offset>.......)" | Out-Null
                $PartObj = @{
                    "ComputerName"    = $Computer
                    "PartitionNumber" = $Matches['partnum'].Trim()
                    "DiskNumber"      = $DiskNumber
                    "Size"            = $Matches['size'].Trim()
                    "Offset"          = $Matches['offset'].Trim()
                }

                foreach ($part in $PartObj) {
                    [pscustomobject]@{
                        ComputerName    = $part.ComputerName
                        PartitionNumber = $part.PartitionNumber
                        DiskNumber      = $part.DiskNumber
                        Size            = $part.Size
                        Offset          = $part.Offset
                    }
                }
            }
        }
    }
}
function Get-DiskPartVolume {
    [OutputType('System.Management.Automation.PSObject')]
    [CmdletBinding()]
    param (
    )

    Try {
        $Output = "list disk`n" | diskpart
    } Catch {
        Write-Error $_
        Continue
    }

    $Disks = ForEach ($Line in $Output) {
        If ($Line -match "^. Disk \d") {
            $Line
        }
    }
    $DiskCount = $Disks.Count

    For ($i = 0; $i -le ($DiskCount - 1); $i++) {
        $DiskNumber = $i
        Try {
            $Output = "Select disk $i`nlist partition`n" | diskpart
        } Catch {
            Write-Error $_
            Continue
        }

        $Partitions = @()
        ForEach ($Line in $Output) {
            If ($Line -match "^. Partition \d") {
                $Partitions += $Line
            }
        }

        $PartCount = $Partitions.Count

        For ($p = 1; $p -le ($Partcount); $p++) {
            $PartNumber = $p
            Try {
                $Output = "Select disk $i`nSelect partition $p`nlist Volume`n" | diskpart
            } Catch {
                Write-Error $_
                Continue
            }

            $Vol = $null; $Mounts = @()
            ForEach ($Line in $Output) {
                If ($Line -match "^[*] Volume \d") {
                    $Vol = $Line
                    $Mounts = @()
                } elseif ($Line -match '\s{4}(?<path>[a-zA-Z]:\\\S+)' -and $Line -notmatch '\s{4}[a-zA-Z]:\\\$Recycle' -and $Vol) {
                    $Mounts += $Matches["path"]
                } elseif ($Line -match "^\s{2}Volume" -and $Vol) {
                    break
                }
            }
            if ($Vol -Match "[*] Volume (?<volnum>...) +(?<drltr>...) +(?<lbl>...........) +(?<fs>.....) +(?<typ>..........) +(?<sz>.......) +(?<sts>.........) +(?<nfo>........)" -eq $true) {
                $VolObj = @{
                    "ComputerName"    = $Computer
                    "VolumeNumber"    = $Matches['volnum'].Trim()
                    "DiskNumber"      = $DiskNumber
                    "PartitionNumber" = $PartNumber
                    "DriveLetter"     = $Matches['drltr'].Trim()
                    "FileSystemLabel" = $Matches['lbl'].Trim()
                    "FileSystem"      = $Matches['fs'].Trim()
                    "DriveType"       = $Matches['typ'].Trim()
                    "Size"            = $Matches['sz'].Trim()
                    "HealthStatus"    = $Matches['sts'].Trim()
                    "Type"            = $Matches['nfo'].trim()
                    "Mountpoint"      = $Mounts
                }

                foreach ($part in $VolObj) {

                    [pscustomobject]@{
                        ComputerName    = $part.ComputerName
                        VolumeNumber    = $part.VolumeNumber
                        DiskNumber      = $part.DiskNumber
                        PartitionNumber = $part.PartitionNumber
                        DriveLetter     = $part.DriveLetter
                        FileSystemLabel = $part.FileSystemLabel
                        FileSystem      = $part.FileSystem
                        DriveType       = $part.DriveType
                        Size            = $part.Size
                        HealthStatus    = $part.HealthStatus
                        Type            = $part.Type
                        MountPoint      = $part.Mountpoint
                    }
                }
            }
        }
    }
}
function Format-Volume {
    [cmdletbinding(SupportsShouldProcess = $true)]
    param(
        [ValidateSet("NTFS")]
        [string]
        $FileSystem,

        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        $Partition
    )

    if ($PSCmdlet.ShouldProcess($Partition.PartitionNumber)) {
        Try {
            $Output = "select disk $($Partition.DiskNumber)`nselect partition $($Partition.PartitionNumber)`nFORMAT FS=NTFS QUICK" | diskpart
        } Catch {
            Write-Error $_
        }
    }
}
function Get-Disk {
    [cmdletbinding()]
    param(
        [parameter()]
        [string]
        $FriendlyName,

        [parameter()]
        [string]
        $SerialNumber,

        [parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias("DiskNumber")]
        [int32]
        $Number
    )

    $Disks = Get-WmiObject Win32_DiskDrive
    $DiskPart = Get-DiskPartDisk

    foreach ($Disk in $Disks) {
        if ($PSBoundParameters.ContainsKey("Number") -and $Number -ne $Disk.Index) {
            continue
        }

        if ($PSBoundParameters.ContainsKey("SerialNumber") -and $SerialNumber -ne $Disk.SerialNumber) {
            continue
        }

        if ($PSBoundParameters.ContainsKey("FriendlyName") -and $FriendlyName -ne $Disk.Caption) {
            continue
        }

        $loc = ($DiskPart | where {$_.DiskNumber -eq $Disk.Index}).LocationPath
        if ($loc -imatch '^pciroot\((?<adapter>\d+)\).+p(?<port>\d+)t(?<target>\d+)L(?<lun>\d+)' ) {
            $Location = "PCI Slot : Adapter $([int]$matches["adapter"]) : Port $([int]$matches["port"]) : Target $([int]$matches["target"]) : LUN $([int]$matches["lun"])"
        }

        $OutPut = New-Object PSObject
        $OutPut | Add-Member -MemberType NoteProperty -Name "PartitionStyle" -Value ($DiskPart | where {$_.DiskNumber -eq $Disk.Index}).PartitionStyle
        $OutPut | Add-Member -MemberType NoteProperty -Name "OperationalStatus" -Value ($DiskPart | where {$_.DiskNumber -eq $Disk.Index}).DetailStatus
        $OutPut | Add-Member -MemberType NoteProperty -Name "BusType" -Value ($DiskPart | where {$_.DiskNumber -eq $Disk.Index}).DetailType
        $OutPut | Add-Member -MemberType NoteProperty -Name "BootFromDisk" -Value $(if (($DiskPart | where {$_.DiskNumber -eq $Disk.Index}).BootDisk -eq "Yes") {
                $true
            } else {
                $false
            })
        $OutPut | Add-Member -MemberType NoteProperty -Name "FirmwareVersion" -Value $Disk.FirmwareVersion
        $OutPut | Add-Member -MemberType NoteProperty -Name "FriendlyName" -Value $Disk.Caption
        $OutPut | Add-Member -MemberType NoteProperty -Name "Guid" -Value ($DiskPart | where {$_.DiskNumber -eq $Disk.Index}).DiskID
        $OutPut | Add-Member -MemberType NoteProperty -Name "IsBoot" -Value $(if (($DiskPart | where {$_.DiskNumber -eq $Disk.Index}).BootDisk -eq "Yes") {
                $true
            } else {
                $false
            })
        $OutPut | Add-Member -MemberType NoteProperty -Name "IsClustered" -Value $(if (($DiskPart | where {$_.DiskNumber -eq $Disk.Index}).ClusteredDisk -eq "Yes") {
                $true
            } else {
                $false
            })
        $OutPut | Add-Member -MemberType NoteProperty -Name "IsOffline" -Value $(if (($DiskPart | where {$_.DiskNumber -eq $Disk.Index}).Status -eq "Online") {
                $false
            } else {
                $true
            })
        $OutPut | Add-Member -MemberType NoteProperty -Name "IsReadOnly" -Value $(if (($DiskPart | where {$_.DiskNumber -eq $Disk.Index}).ReadOnly -eq "Yes") {
                $true
            } else {
                $false
            })
        $OutPut | Add-Member -MemberType NoteProperty -Name "Location" -Value $Location
        $OutPut | Add-Member -MemberType NoteProperty -Name "LogicalSectorSize" -Value $Disk.BytesPerSector
        $OutPut | Add-Member -MemberType NoteProperty -Name "FreeSpace" -Value ($DiskPart | where {$_.DiskNumber -eq $Disk.Index}).Free
        $OutPut | Add-Member -MemberType NoteProperty -Name "Manufacturer" -Value $Disk.Manufacturer
        $OutPut | Add-Member -MemberType NoteProperty -Name "Model" -Value $Disk.Model
        $OutPut | Add-Member -MemberType NoteProperty -Name "Number" -Value $Disk.Index
        $OutPut | Add-Member -MemberType NoteProperty -Name "NumberOfPartitions" -Value (Get-Partition -DiskNumber $Disk.Index | Measure).count
        $OutPut | Add-Member -MemberType NoteProperty -Name "SerialNumber" -Value $Disk.SerialNumber
        $OutPut | Add-Member -MemberType NoteProperty -Name "Free" -Value ($DiskPart | where {$_.DiskNumber -eq $Disk.Index}).Free
        $OutPut | Add-Member -MemberType NoteProperty -Name "Size" -Value $Disk.Size

        $OutPut
    }
}
function Get-Partition {
    [cmdletbinding()]
    param(
        [parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias("Number")]
        [int32]
        $DiskNumber,

        [parameter(ValueFromPipelineByPropertyName = $true)]
        [int32]
        $PartitionNumber,

        [parameter()]
        [char]
        $DriveLetter
    )

    $Partitions = Get-DiskPartPartition
    $Vols = Get-Volume

    if ($PSBoundParameters.ContainsKey("DiskNumber")) {
        $Partitions = $Partitions | where { $_.DiskNumber -eq $DiskNumber }
    }
    if ($PSBoundParameters.ContainsKey("PartitionNumber")) {
        $Partitions = $Partitions | where { $_.PartitionNumber -eq $PartitionNumber }
    }

    foreach ($Partition in $Partitions) {
        $FoundDriveLetter = $($p = $Partition; ($Vols | where { $_.DiskNumber -eq $p.DiskNumber -and $_.PartitionNumber -eq $p.PartitionNumber }).Driveletter)
        if ($PSBoundParameters.ContainsKey("DriveLetter") -and $DriveLetter -ne $FoundDriveLetter) {
            continue
        }

        $OutPut = New-Object PSObject
        $OutPut | Add-Member -MemberType NoteProperty -Name "DiskNumber" -Value $Partition.DiskNumber
        $OutPut | Add-Member -MemberType NoteProperty -Name "PartitionNumber" -Value $Partition.PartitionNumber
        $OutPut | Add-Member -MemberType NoteProperty -Name "DriveLetter" -Value $FoundDriveLetter
        $OutPut | Add-Member -MemberType NoteProperty -Name "Type" -Value $($p = $Partition_; $Vol = ($Vols | where {$_.DiskNumber -eq $p.DiskNumber -and $_.PartitionNumber -eq $p.PartitionNumber }); if ($vol.Type -eq "System" -or !$vol) {
                "Reserved"
            } else {
                "Basic"
            })
        $OutPut | Add-Member -MemberType NoteProperty -Name "IsSystem" -Value $($p = $Partition; $Vol = ($Vols | where {$_.DiskNumber -eq $p.DiskNumber -and $_.PartitionNumber -eq $p.PartitionNumber }); if ($vol.Type -eq "System" -or !$vol) {
                $true
            } else {
                $false
            })
        $OutPut | Add-Member -MemberType NoteProperty -Name "Size" -Value $(if ($Partition.Size) {
                $($sizeinfo = $Partition.size.split(" "); switch ($sizeinfo[1]) {
                        "GB" {
                            ([int]$SizeInfo[0] * 1073741824)
                        }"MB" {
                            ([int]$SizeInfo[0] * 1048576)
                        }default {
                            $SizeInfo[0]
                        }
                    })
            })
        $OutPut
    }
}
function Get-PartitionSupportedSize {
    [cmdletbinding()]
    param(
        [parameter(ValueFromPipelineByPropertyName = $true)]
        [int32]
        $DiskNumber,

        [parameter(ValueFromPipelineByPropertyName = $true)]
        [int32]
        $PartitionNumber
    )

    $Disks = Get-WmiObject Win32_DiskDrive
    $Parts = Get-WmiObject Win32_DiskPartition

    if ($PSBoundParameters.ContainsKey("DiskNumber")) {
        $Disks = $Disks | where {$_.Index -eq $DiskNumber }
        $Parts = $Parts | where { $_.DiskIndex -eq $DiskNumber }
    }

    if ($PSBoundParameters.ContainsKey("PartitionNumber")) {
        $Parts = $parts | where { if ($_.type -like "*gpt*") {
                $_.index -eq $PartitionNumber - 2
            } else {
                $_.index -eq $PartitionNumber - 1
            }}
    }

    foreach ($Part in $Parts) {

        $OutPut = New-Object PSObject
        $OutPut | Add-Member -MemberType NoteProperty -Name "SizeMax" -Value $($PartitionSize = $Part.Size - $Part.StartingOffset
            $DiskSize = ($Disks | where { $_.Index -eq $part.diskIndex }).size
            if (($parts | where {$_.Diskindex -eq $part.diskindex | measure -Maximum index -ErrorAction SilentlyContinue}).maximum -eq $Part.Index) {
                $DiskSize
            } else {
                $PartitionSize
            })
        $OutPut | Add-Member -MemberType NoteProperty -Name "SizeMin" -Value $(0)
        $OutPut
    }
}
function Get-Volume {
    [cmdletbinding()]
    param(
        [parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias("Number")]
        [int32]
        $DiskNumber,

        [parameter(ValueFromPipelineByPropertyName = $true)]
        [int32]
        $PartitionNumber
    )
    process {
        Get-DiskPartVolume | % {
            if ($PSBoundParameters.ContainsKey("DiskNumber") -and $_.DiskNumber -ne $DiskNumber) {
                continue
            }

            if ($PSBoundParameters.ContainsKey("PartitionNumber") -and $_.PartitionNumber -ne $PartitionNumber) {
                continue
            }

            if ($PSBoundParameters.ContainsKey("DriveLetter") -and $_.DriveLetter -ne $DriveLetter) {
                continue
            }

            $_
        }
    }
}
function Initialize-Disk {
    [cmdletbinding()]
    param(
        [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int32]
        $DiskNumber,

        [parameter(Mandatory = $true)]
        [ValidateSet("GPT", "MBR")]
        [string]
        $PartitionStyle
    )

    Try {
        $Output = "select disk $DiskNumber`nclean`nconvert $PartitionStyle`n" | diskpart
    } Catch {
        Write-Error $_
    }
}
function New-Partition {
    [cmdletbinding()]
    param(
        [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int32]
        $DiskNumber,

        [parameter()]
        [char]
        $DriveLetter,

        [Parameter()]
        [switch]
        $AssignDriveLetter,

        [Parameter()]
        [int32]
        $Size,

        [Parameter()]
        [switch]
        $UseMaximumSize
    )

    if ($Size) {
        $dpscript = "select disk $DiskNumber`ncreate partition primary size=$Size`nList Partition`n"
    } else {
        $dpscript = "select disk $DiskNumber`ncreate partition primary`nList Partition`n"
    }

    Try {
        $Output = $dpscript | diskpart
        $Parts = ($Output.split("`n") | where { $_ -match "Partition (\d+)"}).substring(12, 5).trimend()
        $PartNumber = $Parts[$Parts.length - 1]
    } Catch {
        Write-Error $_
    }
    Get-Partition -DiskNumber $DiskNumber -PartitionNumber $PartNumber
}
function Resize-Partition {
    [cmdletbinding()]
    param(
        [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]
        $DiskNumber,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]
        $PartitionNumber,

        [parameter()]
        [uint64]
        $Size
    )
    [int]$Size = $Size / 1073741824
    $dpscript = "select disk $DiskNumber`nselect partition $PartitionNumber`nextend"
    #if($Size){ $dpscript += "Size=$Size" }
    $dpscript += "`n"
    Try {
        $Output = $dpscript | diskpart
    } Catch {
        Write-Error $_
    }
}
function Set-Disk {
    [cmdletbinding()]
    param(
        [parameter()]
        [nullable[bool]]
        $IsOffline,

        [parameter()]
        [nullable[bool]]
        $IsReadOnly,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        $Number
    )

    $dpscript = "select disk $Number`n"
    if ($IsOffline -eq $true) {
        $dpscript += "offline disk`n"
    }
    if ($IsOffline -eq $false) {
        $dpscript += "online disk`n"
    }
    if ($IsReadOnly -eq $true) {
        $dpscript += "attributes disk set readonly`n"
    }
    if ($IsReadOnly -eq $false) {
        $dpscript += "attributes disk clear readonly`n"
    }
    Try {
        Write-Debug $dpscript
        $Output = $dpscript | diskpart
    } Catch {
        Write-Error $_
    }
}
function Set-Partition {
    [cmdletbinding()]
    param(
        [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int32]
        $DiskNumber,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int32]
        $PartitionNumber,

        [parameter(Mandatory = $true)]
        [char]
        $NewDriveLetter
    )

    Try {
        $Output = "select disk $DiskNumber`nselect partition $PartitionNumber`nassign letter=$NewDriveLetter`n" | diskpart
    } Catch {
        Write-Error $_
    }
}
function Set-Volume {
    [cmdletbinding()]
    param(
        [parameter()]
        [string]
        $NewFileSystemLabel,

        [parameter()]
        [char]
        $DriveLetter,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int32]
        $DiskNumber,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int32]
        $PartitionNumber
    )

    $dpscript = "select disk $DiskNumber`nselect partition $PartitionNumber`n"

    if (!$DriveLetter) {
        $DriveLetter = (get-partition -DiskNumber $PartObject.DiskNumber -PartitionNumber $PartObject.PartitionNumber).DriveLetter
    }
    if ([string]::IsNullOrWhiteSpace($DriveLetter)) {
        $Remove = $true
        $DriveLetter = "B"
    }
    if ($DriveLetter) {
        $dpscript += "assign letter=$DriveLetter`n"
    }
    Try {
        $Output = $dpscript | diskpart
    } Catch {
        Write-Error $_
    }
    if ($DriveLetter) {
        $drive = Get-WmiObject win32_volume -Filter "DriveLetter ='$DriveLetter`:'"
        $drive.Label = $NewFileSystemLabel
        $drive.put() | out-null
    }
    if ($Remove) {
        $Output = $Output.replace("assign", "remove")
        Try {
            $Output = $dpscript | diskpart
        } Catch {
            Write-Error $_
        }
    }
}
function Update-HostStorageCache {
    [cmdletbinding()]
    param()

    process {
        "rescan" | diskpart | Out-Null
    }
}
Export-ModuleMember -Function Format-Volume,
Get-Disk,
Get-Partition,
Get-PartitionSupportedSize,
Get-Volume,
Initialize-Disk,
New-Partition,
Resize-Partition,
Set-Disk,
Set-Partition,
Set-Volume,
Update-HostStorageCache