Private/Initialize-DiskPartition.ps1

function Initialize-DiskPartition
{
    <#
    .Synopsis
    Initialize a disk and create partitions
    .DESCRIPTION
    This command will will create partition(s) on a give disk. Supported layours are: BIOS, UEFI, WindowsToGo, Data.
 
    To create a recovery partitions use -RecoveryTools and -RecoveryImage
 
    .EXAMPLE
    Initialize-VDiskPartition -DiskNumber 5 -dynamic -size 30GB -DiskLayout BIOS
    .EXAMPLE
    Initialize-VHDPartition -DiskNumber 4 -dynamic -size 40GB -DiskLayout UEFI -NoRecoveryTools
    .EXAMPLE
    Initialize-VHDPartition -DiskNumber 1 -dynamic -size 40GB -DiskLayout Data -DataFormat ReFS
    .NOTES
    This function is intended as a helper for Intilize-VHDDiskPartition
    #>

    [CmdletBinding(SupportsShouldProcess,
        PositionalBinding = $false,
        ConfirmImpact = 'Medium')]
    Param
    (
        # Disk number, disk must exist
        [Parameter(Position = 0, Mandatory,
            HelpMessage = 'Disk Number based on Get-Disk')]
        [ValidateNotNullorEmpty()]
        [ValidateScript( {
                if (Get-Disk -Number $_)
                {
                    $true
                }
                else
                {
                    Throw "Disk number $_ does not exist."
                }
            })]
        [int]$DiskNumber,

        # Specifies whether to build the image for BIOS (MBR), UEFI (GPT), Data (GPT), or WindowsToGo (MBR).
        # Generation 1 VMs require BIOS (MBR) images and have 2-3 partitions. Generation 2 VMs require
        # UEFI (GPT) images and have 3-4 partitions.
        # Windows To Go images will boot in UEFI or BIOS
        [Parameter(Mandatory)]
        [Alias('Layout')]
        [string]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('BIOS', 'UEFI', 'WindowsToGo', 'Data')]
        $DiskLayout,

        # Format drive as NTFS or ReFS (Only applies when DiskLayout = Data)
        [string]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('NTFS', 'ReFS')]
        $DataFormat = 'ReFS',

        # Alocation Unit Size to format the primary partition
        [int]
        [ValidateSet(4kb, 8kb, 16kb, 32kb, 64kb, 128kb, 256kb, 512kb, 1024kb, 2048kb)]
        $AllocationUnitSize,

        # Output the disk object
        [switch]$Passthru,

        # Skip Creation of the Recovery Environment Tools Partition.
        [switch]$NoRecoveryTools,

        # System (boot loader) Partition Size
        [ValidateScript({$_ -ge 100mb})]
        [int]$SystemSize = 260MB,

        # MS Reserved Partition Size
        [ValidateScript({$_ -ge 16mb})]
        [int]$ReservedSize = 128MB,

        # Recovery Tools Partition Size
        [ValidateScript({$_ -ge 200mb})]
        [int]$RecoverySize = 905MB,

        # Force the overwrite of existing files
        [switch]$force
    )
    Begin
    {


        if ($pscmdlet.ShouldProcess("[$($MyInvocation.MyCommand)] Create [$DiskLayout] partition structure on Disk [$DiskNumber]",
                "Replace existing Partitions on disk [$DiskNumber] ? ",
                'Overwrite WARNING!'))
        {
            if (-not (Get-Disk -Number $DiskNumber | Get-Partition -ErrorAction SilentlyContinue) -Or
                $force -Or
                ((Get-Disk -Number $DiskNumber | Get-Partition -ErrorAction SilentlyContinue) -and $pscmdlet.ShouldContinue("Target Disk [$DiskNumber] has existing partitions! Any existing data will be lost! (suppress with -force)", 'Warning')))
            {
                #region Validate input

                switch ($DiskLayout)
                {
                    'BIOS'
                    {
                        $PartitionStyle = 'MBR'
                        $System = $SystemSize
                        $MsReserved = 0
                        $Recovery = $RecoverySize
                    }
                    'UEFI'
                    {
                        $PartitionStyle = 'GPT'
                        $System = $SystemSize
                        $MsReserved = $ReservedSize
                        $Recovery = $RecoverySize
                    }
                    'Data'
                    {
                        $PartitionStyle = 'GPT'
                        $System = 0
                        $MsReserved = $ReservedSize
                        $Recovery = 0
                    }
                    'WindowsToGo'
                    {
                        $PartitionStyle = 'MBR'
                        $System = $SystemSize
                        $MsReserved = 0
                        $Recovery = 0
                    }
                }
                if ($NoRecoveryTools)
                {
                    $Recovery = 0
                }
                #endregion

                #region create partitions
                try
                {
                    Write-Verbose -Message "[$($MyInvocation.MyCommand)] [$disknumber] : Clearing disk"
                    Clear-disk -Number $disknumber -RemoveData -RemoveOEM -Confirm:$false -ErrorAction SilentlyContinue
                    Write-Verbose -Message "[$($MyInvocation.MyCommand)] [$disknumber] : Initializing disk [$disknumber] as [$PartitionStyle]"
                    $null = Initialize-Disk -Number $disknumber -PartitionStyle $PartitionStyle -ErrorAction SilentlyContinue

                    $InitialPartition = Get-Disk -Number $disknumber -ErrorAction Stop |
                    Get-Partition -ErrorAction SilentlyContinue
                    if ($InitialPartition)
                    {
                        Write-Verbose -Message "[$($MyInvocation.MyCommand)] [$disknumber] : Clearing disk to start all over"
                        $InitialPartition | Remove-Partition -Confirm:$false -ErrorAction SilentlyContinue
                    }

                    if ($System)
                    {
                        # Create the system partition. Create a data partition so we can format it, then change to ESP
                        $NewPartitionParam = @{
                            DiskNumber = $diskNumber
                            Size       = $System
                        }
                        switch ($PartitionStyle)
                        {
                            GPT { $NewPartitionParam.add('GptType', '{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}') }
                            MBR { $NewPartitionParam.add('IsActive', $true) }
                        }
                        Write-Verbose "[$($MyInvocation.MyCommand)] [$disknumber] : System : Creating partition of [$SysSize] bytes"
                        $systemPartition = New-Partition @NewPartitionParam

                        $FileSystem = 'FAT32'
                        if ($DiskLayout -eq 'Bios')
                        {
                            $FileSystem = 'NTFS'
                        }
                        Write-Verbose "[$($MyInvocation.MyCommand)] [$disknumber] : System : Formatting [$FileSystem]"
                        $null = Format-Volume -Partition $systemPartition -NewFileSystemLabel 'System' -FileSystem $FileSystem -Force -Confirm:$false

                        if ($DiskLayout -eq 'UEFI')
                        {
                            Write-Verbose "[$($MyInvocation.MyCommand)] [$disknumber] : System : Setting system partition as ESP"
                            $systemPartition | Set-Partition -GptType '{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}'
                        }
                    }

                    if ($MsReserved)
                    {
                        $NewPartitionParam = @{
                            DiskNumber = $disknumber
                            Size       = $MsReserved
                            GptType    = '{e3c9e316-0b5c-4db8-817d-f92df00215ae}'
                        }
                        Write-Verbose "[$($MyInvocation.MyCommand)] [$disknumber] : MSR : Creating partition of [$MsReserved] bytes"
                        $null = New-Partition @NewPartitionParam

                    }

                    #region Create the Primay partition
                    # Refresh $disk to update free space
                    $disk = Get-Disk -Number $disknumber | Get-Disk

                    $NewPartitionParam = @{
                        DiskNumber = $disknumber
                        Size       = $disk.LargestFreeExtent - $Recovery
                    }
                    if ($PartitionStyle -eq 'GPT') { $NewPartitionParam.add('GptType', '{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}') }
                    Write-Verbose "[$($MyInvocation.MyCommand)] [$disknumber] : Primary : Creating partition of [$($disk.LargestFreeExtent - $Recovery)] bytes"
                    Write-Verbose ($NewPartitionParam | out-string)
                    $windowsPartition = New-Partition @NewPartitionParam

                    $FileSystem = 'NTFS'
                    $Label = 'Windows'
                    if ($DiskLayout -eq 'Data')
                    {
                        $FileSystem = $DataFormat
                        $Label = 'Data'
                    }
                    Write-Verbose "[$($MyInvocation.MyCommand)] [$disknumber] : Primary : Formatting volume [$FileSystem]"
                    $FormatPartitionParam = @{
                        Partition = $windowsPartition
                        NewFileSystemLabel = $Label
                        FileSystem = $FileSystem
                    }
                    if ($AllocationUnitSize) { $FormatPartitionParam.add('AllocationUnitSize', $AllocationUnitSize) }
                    $null = Format-Volume @FormatPartitionParam -Force -Confirm:$false
                    #endregion Primay Partiton

                    if ($Recovery)
                    {
                        Write-Verbose "[$($MyInvocation.MyCommand)] [$disknumber] : Recovery Tools : Creating partition using remaing free space"
                        $NewPartitionParam = @{
                            DiskNumber = $disknumber
                            Size       = $Recovery
                        }
                        if ($PartitionStyle -eq 'GPT') { $NewPartitionParam.add('GptType', '{de94bba4-06d1-4d40-a16a-bfd50179d6ac}') }
                        $recoveryImagePartition = New-Partition @NewPartitionParam
                        Write-Verbose "[$($MyInvocation.MyCommand)] [$disknumber] : Recovery Tools : Formatting volume NTFS"
                        $null = Format-Volume -Partition $recoveryImagePartition -NewFileSystemLabel 'Recovery Tools' -FileSystem NTFS -Force -Confirm:$false
                        #run diskpart to set partition to hidden and prevent deletion
                        #the here string must be left justified
                        if ($PartitionStyle -eq 'GPT')
                        {
                            $null = @"
select disk $($diskNumber)
select partition $($recoveryImagePartition.partitionNumber)
gpt attributes=0x8000000000000001
exit
"@
 |
                            diskpart.exe
                        }
                        else
                        {
                          $null = @"
select disk $($diskNumber)
select partition $($recoveryImagePartition.partitionNumber)
set id=27
exit
"@
 |
                            diskpart.exe
                        }
                    }

                }
                catch
                {
                    Write-Error -Message "[$($MyInvocation.MyCommand)] [$disknumber] : Creating Partitions"
                    throw $_.Exception.Message
                }
                #endregion create partitions

                if ($Passthru)
                {
                    #write the new disk object to the pipeline
                    Get-Disk -Number $DiskNumber
                }
            }
            else
            {
                Throw "[$($MyInvocation.MyCommand)] Aborted by user"
            }
        }
    }
}