public/Update-OSDeployBootUSB.ps1
|
function Update-OSDeployBootUSB { <# .SYNOPSIS Updates an existing OSDeploy USB drive with new BootMedia from an OSDeployCore BootImage build. .DESCRIPTION Refreshes the BootMedia files on one or more existing OSDeploy USB drives. The function: - Prompts for selection of a completed BootImage from %ProgramData%\OSDeployCore\boot - Prompts for the specific bootmedia folder (bootmedia or bootmedia_ca2023) - Locates all connected USB volumes whose label matches $BootLabel (default 'OSDEPLOY') - Copies the selected media to each matching USB volume using robocopy - Writes a BootMedia.json file to each updated volume No partitioning or formatting is performed. Use New-OSDeployBootUSB to create a new drive. .PARAMETER BootLabel Volume label used to identify USB boot partitions to update. Maximum 11 characters. Default is 'OSDEPLOY'. .PARAMETER DataLabel Volume label used to identify the USB data partition when copying ESD files. Maximum 32 characters. Default is 'OSDCloud'. .EXAMPLE Update-OSDeployBootUSB Updates all connected USB volumes labeled 'OSDEPLOY' with the selected BootImage build. .EXAMPLE Update-OSDeployBootUSB -BootLabel 'WinPE' Updates all connected USB volumes labeled 'WinPE'. .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 - One or more USB drives previously created with New-OSDeployBootUSB Dependencies: Module Functions: Get-OSDeployVolumeUSB, New-OSDeployBootUSB, Select-OSDeployCoreBootImage, Test-IsAdministrator, Update-OSDeployCoreESD, Write-OSDeployBanner Executables: robocopy.exe Windows Features: Windows ADK WinPE Addon .LINK https://github.com/OSDeploy/OSDeploy #> [CmdletBinding(SupportsShouldProcess)] param ( # Label used to identify USB boot partitions. Default is 'OSDEPLOY'. [ValidateLength(0, 11)] [string] $BootLabel = 'OSDEPLOY', # Label used to identify the USB data partition for ESD file copy. Default is 'OSDCloud'. [ValidateLength(0, 32)] [string] $DataLabel = 'OSDCloud' ) #================================================= Write-OSDeployBanner $Error.Clear() Write-Verbose "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] Start" #================================================= # Requires Run as Administrator if (-not (Test-IsAdministrator)) { Write-Warning "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] This function must be Run as Administrator" return } #================================================= # Set Variables $ErrorActionPreference = 'Stop' #================================================= # 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)] Selected BootImage: $($SelectBootImage.Name)" Write-Host -ForegroundColor DarkGray "[$(Get-Date -format G)] BootImage path: $($SelectBootImage.Path)" #================================================= # Select a bootmedia folder Write-Host -ForegroundColor DarkGray "[$(Get-Date -format G)] 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 #================================================= # Update matching USB volumes if (Test-Path -Path $BootMediaObject.FullName) { $WinpeVolumes = Get-OSDeployVolumeUSB -FileSystemLabel $BootLabel if ($WinpeVolumes) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format G)] Copying $($BootMediaObject.FullName) to $BootLabel partitions" foreach ($volume in $WinpeVolumes) { if (Test-Path -Path "$($volume.DriveLetter):\") { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format G)] Updating $($volume.DriveLetter):\" robocopy.exe "$($BootMediaObject.FullName)" "$($volume.DriveLetter):\" *.* /e /njh /ndl /r:0 /w:0 /xd '$RECYCLE.BIN' 'System Volume Information' /xj $SelectBootImage | ConvertTo-Json -Depth 5 | Out-File -FilePath "$($volume.DriveLetter):\BootMedia.json" -Force } } } else { Write-Warning "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] No USB partitions labeled '$BootLabel' were found" } } else { Write-Warning "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] BootMedia path not found: $($BootMediaObject.FullName)" } #================================================= # Copy OSDCloud content to USB data partition $OSDCloudSourcePath = Join-Path $script:OSDeployCorePath 'OSDCloud' if (-not (Test-Path -Path $OSDCloudSourcePath)) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format G)] OSDCloud source path not found, skipping data partition copy: $OSDCloudSourcePath" } else { $DataVolumes = Get-OSDeployVolumeUSB -FileSystemLabel $DataLabel if (-not $DataVolumes) { Write-Warning "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] No USB partitions labeled '$DataLabel' were found — skipping OSDCloud copy" } else { foreach ($DataVolume in $DataVolumes) { $DataDrive = $DataVolume.DriveLetter $OSDCloudDestPath = "$DataDrive`:\OSDCloud" Write-Host -ForegroundColor DarkGray "[$(Get-Date -format G)] Checking OSDCloud content on $DataDrive`:\" # Calculate total source size for free space check $SourceFiles = Get-ChildItem -Path $OSDCloudSourcePath -File -Recurse $RequiredBytes = ($SourceFiles | Measure-Object -Property Length -Sum).Sum $DataVolumeInfo = Get-Volume -DriveLetter $DataDrive -ErrorAction SilentlyContinue $FreeBytes = $DataVolumeInfo.SizeRemaining if ($null -eq $FreeBytes) { Write-Warning "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] Could not determine free space on $DataDrive`:\ — skipping OSDCloud copy" continue } if ($RequiredBytes -gt $FreeBytes) { $RequiredGB = [Math]::Round($RequiredBytes / 1GB, 2) $FreeGB = [Math]::Round($FreeBytes / 1GB, 2) Write-Warning "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] Insufficient space on $DataDrive`:\ — need $RequiredGB GB, $FreeGB GB free — skipping OSDCloud copy" continue } $copyCaption = "Copy OSDCloud – $DataDrive`:\" $copyMessage = "Source : $OSDCloudSourcePath`nDest : $OSDCloudDestPath`nFiles : $($SourceFiles.Count) file(s)`nSize : $([Math]::Round($RequiredBytes / 1GB, 2)) GB`nFree : $([Math]::Round($FreeBytes / 1GB, 2)) GB`n`nCopy OSDCloud content to the USB data partition?" if ($PSCmdlet.ShouldContinue($copyMessage, $copyCaption)) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format G)] Copying OSDCloud from $OSDCloudSourcePath to $OSDCloudDestPath" robocopy.exe "$OSDCloudSourcePath" "$OSDCloudDestPath" *.* /e /njh /ndl /r:0 /w:0 /xd '$RECYCLE.BIN' 'System Volume Information' /xj } else { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format G)] OSDCloud copy declined by user for $DataDrive`:\" } } } } #================================================= Write-Verbose "[$(Get-Date -format G)] [$($MyInvocation.MyCommand.Name)] End" #================================================= } |