Private/New-DpVhd.ps1

function New-DpVhd {
<#
    .SYNOPSIS
    Creates a new VHD or VHDX file using diskpart.
 
    .DESCRIPTION
    Uses diskpart to create a new VHD or VHDX file at the specified path and size. If the file exists, -Force is required to overwrite it. Ensures the path is a file, not a folder.
 
    .PARAMETER Path
    The path to the new VHD or VHDX file. Must end in .vhd or .vhdx.
 
    .PARAMETER Size
    The size of the VHD(X) in MB, GB, or TB (e.g., 40GB).
 
    .PARAMETER Force
    If specified, overwrites the existing file at the path.
 
    .EXAMPLE
    New-DpVhd -Path 'C:\disks\disk1.vhdx' -Size 40GB -Force
 
    .NOTES
    Author: WindowsImageTools Team
    Requires: Administrator privileges
#>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [Parameter(Mandatory, Position = 0, HelpMessage = 'Path to the new VHD/VHDX file')]
        [ValidateNotNullOrEmpty()]
        [ValidatePattern("\.vhdx?$")]
        [string]$Path,

        [Parameter(Mandatory, Position = 1, HelpMessage = 'Size of the VHD/VHDX in bytes (e.g., 40GB, 128849018880)')]
        [ValidateNotNullOrEmpty()]
        [double]$Size,

        [switch]$Force,

        # If specified, creates a dynamic (expandable) VHD(X). Otherwise, creates a fixed VHD(X).
        [switch]$Dynamic
    )

    # Make path absolute
    $Path = Get-FullFilePath -Path $Path

    # Ensure path is not a folder
    if (Test-Path $Path -PathType Container) {
        throw "The specified path '$Path' is a directory. Please specify a file path ending in .vhd or .vhdx."
    }

    # If file exists, require -Force or confirmation
    if (Test-Path $Path -PathType Leaf) {
        $shouldDelete = $false
        if ($Force) {
            $shouldDelete = $PSCmdlet.ShouldProcess($Path, 'Remove existing file')
        } else {
            $shouldDelete = $PSCmdlet.ShouldProcess($Path, 'Remove existing file') -and $PSCmdlet.ShouldContinue("The file '$Path' already exists. Do you want to overwrite it?", 'Confirm Overwrite')
        }
        if ($shouldDelete) {
            Remove-Item -Path $Path -Force
        } else {
            throw "The file '$Path' already exists. Use -Force to overwrite or confirm the action."
        }
    }

    # Calculate maximum size in MB for diskpart
    $maxMB = [long]($Size / 1MB)

    $type = if ($Dynamic) { 'expandable' } else { 'fixed' }
    $diskpartScript = @"
create vdisk file="$Path" maximum=$maxMB type=$type
"@

    $scriptFile = [System.IO.Path]::GetTempFileName()
    Set-Content -Path $scriptFile -Value $diskpartScript -Encoding ASCII

    try {
        Write-Verbose "Running diskpart to create VHD: $Path"
        $output = diskpart /s $scriptFile 2>&1
        if ($LASTEXITCODE -ne 0 -or ($output -join "`n") -match 'error') {
            throw "diskpart failed: $($output -join "`n")"
        }
    } finally {
        Remove-Item -Path $scriptFile -ErrorAction SilentlyContinue
    }
}