public/New-OSDeployUSB.ps1
|
function New-OSDeployUSB { <# .SYNOPSIS Creates a new OSDeploy USB bootable drive from an OSDeployCore BootImage build. .DESCRIPTION Prepares a USB drive for use as an OSDeploy bootable device. The function: - Prompts for selection of a completed BootImage from %ProgramData%\OSDeployCore\boot-media - Prompts for the specific bootmedia folder (bootmedia or bootmedia_ca2023) - Selects a suitable USB disk (7 GB - 2 TB) - Clears, partitions (MBR), and formats the disk: - FAT32 4 GB active boot partition (labeled $BootLabel) - NTFS remaining-space data partition (labeled $DataLabel) - Copies the selected BootMedia to the FAT32 partition .PARAMETER BootLabel Volume label for the FAT32 boot partition. Maximum 11 characters (FAT32 limit). Default is 'USB-WinPE'. .PARAMETER DataLabel Volume label for the NTFS data partition. Maximum 32 characters. Default is 'USB-DATA'. .EXAMPLE New-OSDeployUSB Creates a new OSDeploy USB using default labels 'USB-WinPE' and 'USB-DATA'. .EXAMPLE New-OSDeployUSB -BootLabel 'OSDEPLOY' -DataLabel 'OSD-DATA' Creates a new OSDeploy USB using custom partition labels. .OUTPUTS Microsoft.Management.Infrastructure.CimInstance#root/Microsoft/Windows/Storage/MSFT_Disk Returns the prepared USB disk object. .NOTES Author: David Segura Company: Recast Software Version: 0.1.0 Date: April 2026 Prerequisites: - PowerShell 5.0 or higher - Windows 10 or higher - Run as Administrator - At least one completed BootImage build in %ProgramData%\OSDeployCore\boot-media - A USB drive of at least 7 GB .LINK https://github.com/OSDeploy/OSDeploy #> [CmdletBinding()] param ( # Label for the FAT32 boot partition. Default is 'USB-WinPE'. [ValidateLength(0, 11)] [string] $BootLabel = 'USB-WinPE', # Label for the NTFS data partition. Default is 'USB-DATA'. [ValidateLength(0, 32)] [string] $DataLabel = 'USB-DATA' ) #================================================= Write-OSDeployBanner $Error.Clear() Write-Verbose "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] Start" #================================================= # Requires Run as Administrator $IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $IsAdmin) { Write-Warning "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] This function must be Run as Administrator" return } #================================================= # Set Variables $ErrorActionPreference = 'Stop' $MinimumSizeGB = 7 $MaximumSizeGB = 2000 #================================================= # Block Block-StandardUser Block-WindowsVersionNe10 Block-PowerShellVersionLt5 Block-WindowsReleaseIdLt1703 #================================================= # Select a BootImage build $SelectBootImage = Select-OSDeployCoreBootImage if ($null -eq $SelectBootImage) { Write-Warning "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] No OSDeployCore BootImage build was found or selected" return } Write-Host -ForegroundColor DarkGray "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] Selected BootImage: $($SelectBootImage.Name)" Write-Host -ForegroundColor DarkGray "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] BootImage path: $($SelectBootImage.Path)" #================================================= # Select a bootmedia folder Write-Host -ForegroundColor DarkGray "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] Select a bootmedia folder to copy to the USB (Cancel to exit)" $BootMediaObject = Get-ChildItem $SelectBootImage.Path -Directory | Where-Object { ($_.Name -eq 'bootmedia') -or ($_.Name -eq 'bootmedia-ca2023') } | Sort-Object Name, FullName | Select-Object Name, FullName | Out-GridView -Title 'Select a bootmedia folder to copy to the USB (Cancel to exit)' -OutputMode Single if ($null -eq $BootMediaObject) { Write-Warning "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] No bootmedia folder was found or selected" return } #================================================= # Disable Autorun Set-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer' -Name NoDriveTypeAutorun -Type DWord -Value 0xFF -ErrorAction SilentlyContinue #================================================= # Select USB disk $SelectDisk = Invoke-SelectUSBDisk -MinimumSizeGB $MinimumSizeGB -MaximumSizeGB $MaximumSizeGB if (-not $SelectDisk) { Write-Warning "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] No USB drive found meeting the size requirements ($MinimumSizeGB GB - $MaximumSizeGB GB)" break } $GetUSBDisk = Get-OSDDisk -BusType USB -Number $SelectDisk.Number $GetUSBDisk | Select-Object -Property * -ExcludeProperty Cim*, PS*, Pass* #================================================= # Clear the disk if ($GetUSBDisk.NumberOfPartitions -ne 0) { $GetUSBDisk | Clear-Disk -RemoveData -RemoveOEM -Confirm:$true -ErrorAction Stop } $GetUSBDisk = Get-OSDDisk -BusType USB -Number $SelectDisk.Number | Where-Object { $_.NumberOfPartitions -eq 0 } if (-not $GetUSBDisk) { Write-Warning "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] Unable to verify the USB disk was cleared successfully" break } #================================================= # Initialize and partition (MBR) if ($GetUSBDisk.PartitionStyle -eq 'RAW') { $GetUSBDisk | Initialize-Disk -PartitionStyle MBR -ErrorAction Stop } if ($GetUSBDisk.PartitionStyle -eq 'GPT') { Set-Disk -Number $GetUSBDisk.Number -PartitionStyle MBR -ErrorAction Stop } if ($GetUSBDisk.SizeGB -le 2000) { $BootPartition = $GetUSBDisk | New-Partition -Size 4GB -IsActive -AssignDriveLetter | Format-Volume -FileSystem FAT32 -NewFileSystemLabel $BootLabel -ErrorAction Stop $DataPartition = $GetUSBDisk | New-Partition -UseMaximumSize -AssignDriveLetter | Format-Volume -FileSystem NTFS -NewFileSystemLabel $DataLabel -ErrorAction Stop } #================================================= # Copy BootMedia to the FAT32 partition $WinpeDestinationPath = "$($BootPartition.DriveLetter):\" if (-not $WinpeDestinationPath) { Write-Warning "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] Unable to determine the destination drive letter" break } if ((Test-Path $BootMediaObject.FullName) -and (Test-Path $WinpeDestinationPath)) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] Copying BootMedia to $WinpeDestinationPath" robocopy.exe "$($BootMediaObject.FullName)" "$WinpeDestinationPath" *.* /e /ndl /njh /njs /np /r:0 /w:0 /b /zb } #================================================= Write-Verbose "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] End" return (Get-OSDDisk -BusType USB -Number $SelectDisk.Number) #================================================= } |