cOnlineAllDisksWithPools.psm1

enum PartitionType
{
    GPT
    MBR
}

<#
   This resource ensures all disks are online, read/write, partitioned and formatted
   [DscResource()] indicates the class is a DSC resource
#>

[DscResource()]
class cOnlineAllDisksWithPools
{
    <#
        This property is the name of the system - its not used for anything
        other than they key.
    #>

    [DscProperty(Key)]
    [string]$Name

    [DscProperty()]
    [PartitionType]$PartitionType = [PartitionType]::GPT

    <#
        This is a JSON representation of an array of drives using a PSCustomObject
        Object format is:
            DriveLetter DriveLun AllocUnit
            ----------- --------- ---------
            D 0 64
            Q 1+2
            R 3
        Typically, a new DriveArray is created via:
          $VMDiskArray = "D:0@64,Q:1+2,R:3"
          #Format is <Letter>:<Lun>@Alloc
          #LunSize can be <lun>+<Lun> for disk pooling
 
          $DiskArray = @()
          $VMDiskArray.Split(",") | ForEach-Object {
              $driveLetter = $_.split(':')[0]
              $DriveLuns = $_.split(':')[1].Split('@')[0]
              $AllocUnit = $_.split(':')[1].Split('@')[1]
              $DiskObject = [PSCustomObject]@{
                  DriveLetter = $DriveLetter
                  DriveSize = $DriveSize
                  AllocUnit = $AllocUnit
              }
              $DiskArray += $DiskObject
          }
 
        This should be passed via $DiskArray | ConvertTo-Json
    #>

    [DscProperty()]
    [string] $DriveArrayInJSON

    [PSCustomObject[]] $DriveArray

    <# This toggles the volume lable between "Disk1" and "E_Drive" #>
    [DscProperty()]
    [Bool]$UseDriveLetterInVolumeName = $False

    <#
        This method is equivalent of the Get-TargetResource script function.
        The implementation should use the keys to find appropriate resources.
        This method returns an instance of this class with the updated key
         properties.
    #>

    [cOnlineAllDisksWithPools] Get()
    {
        $this.Name = $Env:COMPUTERNAME
        #This is the default PartitionType
        $this.PartitionType = [PartitionType]::GPT

        #Convert from Json
        try {
            $this.DriveArray = $This.DriveArrayInJSON | ConvertFrom-Json
        } catch {
            throw "Invalid DriveArrayInJSON"
        }

        return $this
    }

    <#
        This method is equivalent of the Test-TargetResource script function.
        It should return True or False, showing whether the resource
        is in a desired state.
    #>

    [bool] Test()
    {
        Write-Verbose "COnlineAllDisksWithPools:Test - Start"
        Write-Verbose "COnlineAllDisksWithPools:Test - UseDriveLetterInVolumeName: $($This.UseDriveLetterInVolumeName)"
        Write-Verbose "COnlineAllDisksWithPools:Test - DriveArrayInJSON: $($This.DriveArrayInJSON)"
        #Default to PASS
        $TestResult = $true
        try {
            #See if we have any offline disks
            $OfflineDisks = Get-Disk | Where-Object {$_.IsOffline -eq $true}
            if ($OfflineDisks -ne $null) {
                Write-Verbose "COnlineAllDisksWithPools:Test - Found an offline disk"
                $TestResult = $false
            } else {
                Write-Verbose "COnlineAllDisksWithPools:Test - No offline disks"
            }

            #See if we have any RAW partitions
            $RawDisks = Get-Disk | Where-Object { $_.partitionstyle -eq 'raw' }
            #Any raw disks means we need to run SET
            if  ($RawDisks -ne $null) {
                Write-Verbose "COnlineAllDisksWithPools:Test - Found a RAW disk"
                $TestResult = $false
            } else {
                Write-Verbose "COnlineAllDisksWithPools:Test - No RAW disks"
            }

            #Now see if any disks have no partitions
            Get-Disk | Sort-Object { $_.Number } | ForEach-Object {
                $Part = (Get-Partition -DiskNumber $_.Number -ErrorAction SilentlyContinue | Where-Object {$_.Type -ne "Reserved"})
                if ( $Part -eq $null ) {
                    Write-Verbose "COnlineAllDisksWithPools:Test - Found an unpartitioned disk"
                    $TestResult = $false
                }
            }
            if ($testResult) {
                Write-Verbose "COnlineAllDisksWithPools:Test - No unpartitioned disks"
            }

            #Finally, see if any volumes need to be formatted
            Get-Volume | Where-Object {$_.DriveType -eq "fixed"} | ForEach-Object {
                if ($_.Size -eq 0) {
                    Write-Verbose "COnlineAllDisksWithPools:Test - Found an unformatted disk"
                    $TestResult = $false
                }
            }
            if ($testResult) {
                Write-Verbose "COnlineAllDisksWithPools:Test - No unformatted disks"
            }

            #All tests run, return results
            return $TestResult

        } finally {
            Write-Verbose "COnlineAllDisksWithPools:Test - End"
        }
    }

    <#
        This method is equivalent of the Set-TargetResource script function.
        It sets the resource to the desired state.
    #>

    [void] Set()
    {
        Write-Verbose "COnlineAllDisksWithPools:Set - Start"
        Write-Verbose "COnlineAllDisksWithPools:Set - UseDriveLetterInVolumeName: $($This.UseDriveLetterInVolumeName)"
        Write-Verbose "COnlineAllDisksWithPools:Set - DriveArrayInJSON: $($This.DriveArrayInJSON)"
        $DiskPoolToken = '\+'

        try {
            #Convert from Json
            try {
                $this.DriveArray = $This.DriveArrayInJSON | ConvertFrom-Json
            } catch {
                throw "Invalid DriveArrayInJSON"
            }

            #Online all disks
            $OfflineDisks = Get-Disk | Where-Object {$_.IsOffline -eq $true}
            if ($OfflineDisks -ne $null ) {
                $OfflineDisks | ForEach-Object {
                    $DiskNumber = $_.DiskNumber
                    Write-Verbose "COnlineAllDisksWithPools:Set - Onlining disk $DiskNumber"
                    Set-Disk -InputObject $_ -IsOffline $false
                    Write-Verbose "COnlineAllDisksWithPools:Set - Setting disk $DiskNumber to Read/Write"
                    Set-Disk -InputObject $_ -IsReadOnly $false
                }
            } else {
                Write-Verbose "COnlineAllDisksWithPools:Set - All disks online"
            }

            #Loop through each disk
            $DiskCount = 1
            $this.DriveArray | ForEach-Object {
                $DriveLetter = $_.DriveLetter
                $DriveLuns = $_.DriveLun
                $AllocUnit = 4 * 1024 #need to set var before using as [ref]
                If ( [System.Int32]::TryParse($_.AllocUnit, [ref]$AllocUnit) ) {
                    $AllocUnit = [int]$_.AllocUnit * 1024
                } else {
                    Write-Verbose "COnlineAllDisksWithPools:Set - Invalid AllocUnit - '$($_.AllocUnit)' - Using default 4K AllocUnits"
                    $AllocUnit = 4 * 1024
                }

                If ($DriveLuns -match "$DiskPoolToken")  {
                    #This is a pool, get the array of disks
                    $LunArray = $DriveLuns -split '\+'
                    $PhysDisks = Get-PhysicalDisk | Where-Object { $LunArray -contains $_.DeviceID}
                    $StorageName = (Get-StorageSubSystem).FriendlyName
                    $PoolName = ("Pool_" + $DriveLetter)
                    #Set up our pool
                    Write-Verbose "COnlineAllDisksWithPools:Set - Creating new pool '$PoolName' on '$StorageName' using LUN's '$DriveLuns'."
                    New-StoragePool -FriendlyName $PoolName `
                                    -StorageSubSystemFriendlyName $StorageName `
                                    -PhysicalDisks $PhysDisks
                    #Set up our Virtual Disk
                    $DiskLabel = ("Disk" + $DiskCount)
                    Write-Verbose "COnlineAllDisksWithPools:Set - Creating new Virtual Disk '$DiskLabel' on pool '$PoolName'."
                    New-VirtualDisk -StoragePoolFriendlyName $PoolName `
                                    -FriendlyName $DiskLabel `
                                    -ResiliencySettingName "Simple" `
                                    -Interleave 65536 `
                                    -NumberOfColumns $LunArray.count `
                                    -ProvisioningType Fixed `
                                    -UseMaximumSize

                    #Initialize Disk
                    $NewDisk = Get-VirtualDisk -FriendlyName $DiskLabel | Get-Disk
                    if ( ($this.PartitionType -EQ [PartitionType]::MBR) -and ( ($NewDisk.Size/1GB) -GT 2048) ) {
                        #MBR doesn't support more than 2048 - force GPT
                        Write-Verbose "COnlineAllDisksWithPools:Set - Initalizing Disk '$DiskLabel' using 'GPT'."
                        Initialize-Disk -Number $NewDisk.Number -PartitionStyle GPT
                    } else {
                        Write-Verbose ("COnlineAllDisksWithPools:Set - Initalizing Disk '$DiskLabel' using '" + ($this.PartitionType.ToString()) + "'.")
                        Initialize-Disk -Number $NewDisk.Number -PartitionStyle ($this.PartitionType.ToString())
                    }

                    #Partition
                    Write-Verbose "COnlineAllDisksWithPools:Set - Partitioning Pool $PoolName as '$DriveLetter'"
                    New-Partition -DiskNumber $NewDisk.Number -DriveLetter $DriveLetter -UseMaximumSize

                    #Format
                    if ($This.UseDriveLetterInVolumeName) {
                        Write-Verbose "COnlineAllDisksWithPools:Set - Formatting drive $DriveLetter using allocation unit $($AllocUnit) using label $($DriveLetter + "_Drive")"
                        Format-Volume -DriveLetter $DriveLetter -FileSystem NTFS -NewFileSystemLabel ($DriveLetter + "_Drive") -Confirm:$false -AllocationUnitSize $AllocUnit
                    } else {
                        Write-Verbose "COnlineAllDisksWithPools:Set - Formatting drive $DriveLetter using default allocation unit using label '$DiskLabel'"
                        Format-Volume -DriveLetter $DriveLetter -FileSystem NTFS -NewFileSystemLabel $DiskLabel -Confirm:$false -AllocationUnitSize $AllocUnit
                    }

                }  else {
                    #See if this disk is RAW
                    $disk = Get-Disk -Number $($DriveLuns)
                    if ($disk.PartitionStyle -EQ 'RAW') {
                        #Initalize Disk
                        Write-Verbose "COnlineAllDisksWithPools:Set - Initializing RAW Disk $($DriveLuns) as $($this.PartitionType.ToString())"
                        if ( ($this.PartitionType -EQ [PartitionType]::MBR) -and ( ($_.Size / 1GB) -GT 2048) ) {
                            #MBR doesn't support more than 2048 - force GPT
                            Initialize-Disk -Number $DriveLuns -PartitionStyle GPT
                        } else {
                            Initialize-Disk -Number $DriveLuns -PartitionStyle ($this.PartitionType.ToString())
                        }
                    } else {
                        Write-Verbose "COnlineAllDisksWithPools:Set - Disk '$($DriveLuns)' already intialized as '$($disk.PartitionStyle)'"
                    }

                    #See if this disk is Partitioned
                    $Part = (Get-Partition -DiskNumber $DriveLuns -ErrorAction SilentlyContinue | Where-Object { $_.Type -ne "Reserved" })
                    if ( $Part -eq $null ) {
                        #Partition disks
                        Write-Verbose "COnlineAllDisksWithPools:Set - Partitioning Disk '$DriveLuns' as '$DriveLetter'"
                        New-Partition -DiskNumber $DriveLuns -DriveLetter $DriveLetter  -UseMaximumSize
                    } else {
                        Write-Verbose "COnlineAllDisksWithPools:Set - Disk '$($DriveLuns)' already partitioned as drive letter '$DriveLetter'"
                    }

                    #See if this disk is formated - does not check to see if drive letter matches DiskArray
                    $Vol = Get-Volume -DriveLetter $DriveLetter -ErrorAction SilentlyContinue
                    if ( $Vol.FileSystemType -eq 'Unknown' ) {
                        $DiskLabel = ("Disk" + $DiskCount)
                        if ($This.UseDriveLetterInVolumeName) {
                            Write-Verbose ("COnlineAllDisksWithPools:Set - Formatting drive '$DriveLetter' using allocation unit '$AllocUnit' with label " + ($DriveLetter + "_Drive"))
                            Format-Volume -DriveLetter $DriveLetter -FileSystem NTFS -NewFileSystemLabel ($DriveLetter + "_Drive") -Confirm:$false -AllocationUnitSize $AllocUnit
                        } else {
                            Write-Verbose ("COnlineAllDisksWithPools:Set - Formatting drive $DriveLetter using allocation unit '$AllocUnit' with label '$DiskLabel'")
                            Format-Volume -DriveLetter $DriveLetter -FileSystem NTFS -NewFileSystemLabel $DiskLabel -Confirm:$false -AllocationUnitSize $AllocUnit
                        }
                    } else {
                        Write-Verbose "COnlineAllDisksWithPools:Set - Disk '$($DriveLuns)' already formatted"
                    }

                }
                $DiskCount += 1
            }
        } catch {
            $ErrorMessage = $_.Exception.Message
            Write-Verbose "ERROR: $ErrorMessage"
        } finally {
            Write-Verbose "COnlineAllDisksWithPools:Set - End"
        }
    }


} # This module defines a class for a DSC "cOnlineAllDisksWithPools" provider.