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 "OnlineDisks:Test - Start"
        Write-Verbose "OnlineDisks:Test - Start - UseDriveLetterInVolumeName: $This.UseDriveLetterInVolumeName"
        #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 "OnlineDisks:Test - Found an offline disk"
                $TestResult = $false
            }

            #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 "OnlineDisks:Test - Found a RAW disk"
                $TestResult = $false
            }

            #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 "OnlineDisks:Test - Found an unpartitioned disk"
                    $TestResult = $false 
                }
            } 
            #Finally, see if any volumes need to be formatted
            Get-Volume | Where-Object {$_.DriveType -eq "fixed"} | ForEach-Object { 
                if ($_.Size -eq 0) {
                    Write-Verbose "OnlineDisks:Test - Found an unformatted disk"
                    $TestResult = $false
                }
            }
            #All tests run, return results
            return $TestResult 

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

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

    [void] Set()
    {
        Write-Verbose "OnlineDisks:Set - Start"
        $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 "OnlineDisks:Set - Onlining disk $DiskNumber"
                    Set-Disk -InputObject $_ -IsOffline $false 
                    Write-Verbose "OnlineDisks:Set - Setting disk $DiskNumber to Read/Write"
                    Set-Disk -InputObject $_ -IsReadOnly $false
                }
            } else {
                Write-Verbose "OnlineDisks:Set - All disks online"
            }

            #Loop through each disk
            $DiskCount = 1
            $this.DriveArray | ForEach-Object {
                $DriveLetter = $_.DriveLetter
                $DriveLuns = $_.DriveLun
                $AllocUnit = [int]$_.AllocUnit * 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 ("OnlineDisks: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 ("OnlineDisks: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-Disk -FriendlyName $DiskLabel
                    if ( ($this.PartitionType -EQ [PartitionType]::MBR) -and ( ($NewDisk.Size/1GB) -GT 2048) ) {
                        #MBR doesn't support more than 2048 - force GPT
                        Write-Verbose ("OnlineDisks:Set - Initalizing Disk '$DiskLabel' using 'GPT'.")
                        Initialize-Disk -Number $NewDisk.Number -PartitionStyle GPT 
                    } else {
                        Write-Verbose ("OnlineDisks:Set - Initalizing Disk '$DiskLabel' using '" + ($this.PartitionType.ToString()) + "'.")
                        Initialize-Disk -Number $NewDisk.Number -PartitionStyle ($this.PartitionType.ToString()) 
                    }
                    
                    #Partition
                    Write-Verbose ("OnlineDisks:Set - Partitioning Pool $PoolName as '" + $DriveLetter + "'")
                    New-Partition -DiskNumber $NewDisk.Number -DriveLetter $DriveLetter -UseMaximumSize
                    
                    #Format
                    if ($This.UseDriveLetterInVolumeName) {
                        Write-Verbose ("OnlineDisks:Set - Formatting drive $DriveLetter using default allocation unit using label " + ($DriveLetter + "_Drive"))
                        Format-Volume -DriveLetter $DriveLetter -FileSystem NTFS -NewFileSystemLabel ($DriveLetter + "_Drive") -Confirm:$false -AllocationUnitSize $AllocUnit
                    } else {
                        Write-Verbose ("OnlineDisks:Set - Formatting drive $DriveLetter using default allocation unit using label '$DiskLabel'")
                        Format-Volume -DriveLetter $DriveLetter -FileSystem NTFS -NewFileSystemLabel $DiskLabel -Confirm:$false -AllocationUnitSize $AllocUnit
                    }

                }  else {
                    #Initalize Disk
                    Write-Verbose ("OnlineDisks:Set - Initializing RAW Disk $DiskNumber 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()) 
                    }
                    #Partition disks
                    Write-Verbose ("OnlineDisks:Set - Partitioning Disk $DriveLuns as " + $DriveLetter)
                    New-Partition -DiskNumber $DriveLuns -DriveLetter $DriveLetter  -UseMaximumSize

                    #Format
                    $DiskLabel = ("Disk" + $DiskCount)
                    if ($This.UseDriveLetterInVolumeName) {
                        Write-Verbose ("OnlineDisks:Set - Formatting drive $DriveLetter using default allocation unit using label " + ($DriveLetter + "_Drive"))
                        Format-Volume -DriveLetter $DriveLetter -FileSystem NTFS -NewFileSystemLabel ($DriveLetter + "_Drive") -Confirm:$false -AllocationUnitSize $AllocUnit
                    } else {
                        Write-Verbose ("OnlineDisks:Set - Formatting drive $DriveLetter using default allocation unit using label '$DiskLabel'")
                        Format-Volume -DriveLetter $DriveLetter -FileSystem NTFS -NewFileSystemLabel $DiskLabel -Confirm:$false -AllocationUnitSize $AllocUnit
                    }
                }
                $DiskCount += 1
            }
        } catch {
            $ErrorMessage = $_.Exception.Message
            Write-Verbose "ERROR: $ErrorMessage"
        } finally {
            Write-Verbose "OnlineDisks:Set - End"
        }
    }


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