Private/Initialize-VHDPartition.ps1

function Initialize-VHDPartition
{
    <#
    .Synopsis
    Create VHD(X) with partitions needed to be bootable
    .DESCRIPTION
    This command will create a VHD or VHDX file. Supported layours are: BIOS, UEFI, Data or WindowsToGo.
 
    To create a recovery partitions use -RecoveryTools and -RecoveryImage
 
    .EXAMPLE
    Initialize-VHDPartition d:\disks\disk001.vhdx -dynamic -size 30GB -DiskLayout BIOS
    .EXAMPLE
    Initialize-VHDPartition d:\disks\disk001.vhdx -dynamic -size 40GB -DiskLayout UEFI -RecoveryTools
    .NOTES
    General notes
    #>

    [CmdletBinding(SupportsShouldProcess,
        PositionalBinding = $false,
        ConfirmImpact = 'Medium')]
    Param
    (
        # Path to the new VHDX file (Must end in .vhd, or .vhdx)
        [Parameter(Position = 0, Mandatory,
            HelpMessage = 'Enter the path for the new VHD/VHDX file')]
        [ValidateNotNullorEmpty()]
        [ValidatePattern(".\.vhdx?$")]
        [ValidateScript( {
                if (Get-FullFilePath -Path $_ |
                    Split-Path |
                    Resolve-Path )
                {
                    $true
                }
                else
                {
                    Throw "Parent folder for $_ does not exist."
                }
            })]
        [string]$Path,

        # Size in Bytes (Default 80B)
        [uint64]$Size = 80GB,

        # System (boot loader) Partition Size (Default : 260MB)
        [int]$SystemSize,

        # MS Reserved Partition Size (Default : 128MB)
        [int]$ReservedSize,

        # Recovery Tools Partition Size (Default : 905MB)
        [int]$RecoverySize,

        # Create Dynamic disk
        [switch]$Dynamic,

        # 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 one partition. Generation 2 VMs require
        # UEFI (GPT) images and have 3-5 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 image object
        [switch]$Passthru,

        # Create the Recovery Environment Tools Partition. Only valid on UEFI layout
        [switch]$NoRecoveryTools,

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


        if ($pscmdlet.ShouldProcess("[$($MyInvocation.MyCommand)] Create partition structure for Bootable vhd(x) on [$Path]",
                "Replace existing file [$Path] ? ",
                'Overwrite WARNING!'))
        {
            if ((-not (Test-Path $Path)) -Or
                $force -Or
                ((Test-Path $Path) -and $pscmdlet.ShouldContinue("TargetFile [$Path] exists! Any existin data will be lost!", 'Warning')))
            {

                $ParametersToPass = @{ }
                foreach ($key in ('Whatif', 'Verbose', 'Debug'))
                {
                    if ($PSBoundParameters.ContainsKey($key))
                    {
                        $ParametersToPass[$key] = $PSBoundParameters[$key]
                    }
                }

                #region Validate input

                $VHDFormat = ([IO.FileInfo]$Path).Extension.split('.')[-1]

                if (($DiskLayout -eq 'UEFI') -and ($VHDFormat -eq 'VHD'))
                {
                    throw 'UEFI disks must be in VHDX format. Please change the path to end in VHDX'
                }

                # Enforce max VHD size.
                if ('VHD' -ilike $VHDFormat)
                {
                    if ($Size -gt 2040GB)
                    {
                        Write-Warning -Message 'For the VHD file format, the maximum file size is ~2040GB. Reseting size to 2040GB.'
                        $Size = 2040GB
                    }
                }

                $fileName = Split-Path -Leaf -Path $Path

                # make paths absolute
                $Path = $Path | Get-FullFilePath
                #endregion

                # if we get this far it's ok to delete existing files. Save the ACL for the new file
                $Acl = $null
                if (Test-Path -Path $Path)
                {
                    $Acl = Get-Acl -Path $Path
                    Remove-Item -Path $Path
                }
                Write-Verbose -Message "[$($MyInvocation.MyCommand)] [$fileName] : Creating"

                #region Create VHD
                Try
                {
                    $vhdParams = @{
                        VHDFormat = $VHDFormat
                        Path      = $Path
                        SizeBytes = $Size
                    }

                    If ($Dynamic)
                    {
                        Write-Verbose -Message "[$($MyInvocation.MyCommand)] [$fileName] : Params for [WIM2VHD.VirtualHardDisk]::CreateSparseDisk()"
                        Write-Verbose -Message ($vhdParams | Out-String)
                        $null = [WIM2VHD.VirtualHardDisk]::CreateSparseDisk(
                            $VHDFormat,
                            $Path,
                            $Size,
                            $true
                        )
                    }
                    else
                    {
                        Write-Verbose -Message "[$($MyInvocation.MyCommand)] [$fileName] : Params for [WIM2VHD.VirtualHardDisk]::CreateFixedDisk()"
                        Write-Verbose -Message ($vhdParams | Out-String)
                        Write-Warning -Message 'Creating a Fixed Disk May take a long time!'
                        $null = [WIM2VHD.VirtualHardDisk]::CreateFixedDisk(
                            $VHDFormat,
                            $Path,
                            $Size,
                            $true
                        )

                    }
                }
                catch
                {
                    Throw "Failed to create $Path. $($_.Exception.Message)"
                }

                #endregion

                if (Test-Path -Path $Path)
                {
                    if ($Acl)
                    {
                        Set-Acl -Path $Path -AclObject $Acl
                    }
                    #region Mount Image
                    try
                    {
                        Write-Verbose -Message "[$($MyInvocation.MyCommand)] [$fileName] : Mounting disk image"
                        $disk = Mount-DiskImage -ImagePath $Path -PassThru |
                        Get-DiskImage |
                        Get-Disk
                    }
                    catch
                    {
                        throw $_.Exception.Message
                    }
                    #endregion
                }
                else
                {
                    Throw "Failed to create vhd"
                }

                #region Create partitions
                try
                {
                    $InitializeDiskParam = @{
                        DiskNumber = $disk.Number
                        DiskLayout = $DiskLayout
                        force      = $force
                    }
                    if ($DataFormat) { $InitializeDiskParam.add('DataFormat', $DataFormat) }
                    if ($NoRecoveryTools) { $InitializeDiskParam.add('NoRecoveryTools', $NoRecoveryTools) }
                    if ($SystemSize) { $InitializeDiskParam.add('SystemSize', $SystemSize) }
                    if ($ReservedSize) { $InitializeDiskParam.add('ReservedSize', $ReservedSize) }
                    if ($RecoverySize) { $InitializeDiskParam.add('RecoverySize', $RecoverySize) }
                    if ($AllocationUnitSize) { $InitializeDiskParam.add('AllocationUnitSize', $AllocationUnitSize) }

                    $null = Initialize-DiskPartition @ParametersToPass @InitializeDiskParam
                    #endregion
                }

                catch
                {
                    Write-Error -Message "[$($MyInvocation.MyCommand)] [$fileName] : Creating Partitions"
                    throw $_.Exception.Message
                }
                #region Dismount
                finally
                {
                    Write-Verbose -Message "[$($MyInvocation.MyCommand)] [$fileName] : Dismounting disk image"
                    $null = Dismount-DiskImage -ImagePath $Path
                    [System.GC]::Collect()
                }
                #endregion
                if ($Passthru)
                {
                    #write the new disk object to the pipeline
                    Get-DiskImage -ImagePath $Path
                }
            }
        }
    }
}