Skip to content

Instantly share code, notes, and snippets.

@tcely
Last active January 27, 2026 18:19
Show Gist options
  • Select an option

  • Save tcely/5036a2f4297cfa274319bfee4349200a to your computer and use it in GitHub Desktop.

Select an option

Save tcely/5036a2f4297cfa274319bfee4349200a to your computer and use it in GitHub Desktop.
Synchronize ISO storage for Ventoy

Ventoy VHD Multiboot Automation

This project provides a robust framework for managing a bootable Ventoy environment within a Virtual Hard Disk (VHD) on Windows.

📁 Project Structure & Paths

Category Path Purpose
Project Root C:\ventoy Base container for all assets.
VHD File C:\ventoy\ventoy.vhd 1GiB fixed-size bootable virtual disk.
Backup Dir C:\ventoy\boot Storage for BCD backups.
Logs Dir C:\ventoy\logs Output for log-ventoy-vhd-sync.ps1 and automation audits.
Scripts Dir C:\ventoy\scripts Storage for PS1 scripts and XML task templates.

⚠️ Internal VHD Requirement

The synchronization scripts expect the Ventoy Link tool to be present inside the VHD at: [VHD_ROOT]\ventoy\VentoyVlnk.exe

🛠️ Script Manifest (C:\ventoy\scripts)

  • setup-directories.ps1: Run first to initialize boot, scripts, and logs.
  • create-ventoy-vhd.ps1: Provisions the required VHD boot container.
  • unmount-ventoy-vhd.ps1: Run after Ventoy installation to allow safe Microsoft BCDEdit operations.
  • ventoy-vhd-sync.ps1: The core logic for generating and moving .vlnk.iso files (triggered by tasks).
  • log-ventoy-vhd-sync.ps1: Wraps the sync process to generate C:\ventoy\logs\sync_[Weekday].log for auditing.
  • ventoy-watcher.ps1: Optional real-time monitor via FileSystemWatcher.

Resources

Ventoy VHD Boot Configuration & Backup Management

This document covers environment setup, BCD registration, and dual-stage backup management.

0. Phase 1: Environment Setup & VHD Preparation

Run these commands in an Administrator Command Prompt. This initializes the folders, creates the VHD, and ensures a clean dismount before the initial backup.

:: 1. Initialize folders and create the 1GiB Fixed VHD
powershell -ExecutionPolicy Bypass -File "C:\ventoy\scripts\setup-directories.ps1"
powershell -ExecutionPolicy Bypass -File "C:\ventoy\scripts\create-ventoy-vhd.ps1"

:: 2. ACTION REQUIRED: Install Ventoy via Ventoy2Disk.exe now, then return here.
pause

:: 3. Dismount and verify success before initial BCD backup
powershell -ExecutionPolicy Bypass -File "C:\ventoy\scripts\unmount-ventoy-vhd.ps1" || (echo ERROR: Dismount failed. Check for open files and retry. && exit /b)

:: 4. Export the original, untouched BCD to the new boot folder
bcdedit /export "C:\ventoy\boot\bcd_original.bak"

1. Register Ventoy in the BCD

Run these in the same prompt. Replace {GUID} with the identifier generated by the first command.

:: 1. Create the boot entry
bcdedit /create /d "Ventoy Multiboot" /application bootsector

:: 2. Point to the VHD file
bcdedit /set {GUID} device vhd=[C:]\ventoy\ventoy.vhd
bcdedit /set {GUID} osdevice vhd=[C:]\ventoy\ventoy.vhd

:: 3. Set the EFI path and add to menu
bcdedit /set {GUID} path \EFI\BOOT\BOOTX64.EFI
bcdedit /displayorder {GUID} /addlast

2. Customize Timing and Labels

Refine the boot menu behavior to prioritize the Ventoy entry.

:: Set timeout to 90 seconds and make Ventoy the default
bcdedit /timeout 90
bcdedit /default {GUID}

:: Rename entries for clarity
bcdedit /set {GUID} description "Ventoy System (Default)"
bcdedit /set {current} description "Windows 10 Host"

3. Phase 2: Final Customized Backup

Capture the final state once the menu is verified.

:: Export the final customized BCD to the boot folder
bcdedit /export "C:\ventoy\boot\bcd_ventoy_final.bak"

4. Restoration: Choosing Your State

Use these commands to revert or restore your configuration as needed.

To revert to a clean, Windows-only boot:

bcdedit /import "C:\ventoy\boot\bcd_original.bak"

To restore your Ventoy setup:

bcdedit /import "C:\ventoy\boot\bcd_ventoy_final.bak"

Resources

# Requires Administrator privileges
$VhdPath = "C:\ventoy\ventoy.vhd"
# 1. Create a 1GiB fixed VHD
Write-Host "Creating 1GiB Fixed VHD at $VhdPath..." -ForegroundColor Cyan
New-VHD -Path $VhdPath -Fixed -SizeBytes 1GB | Out-Null
# 2. Mount it so it appears as a Physical Device in Disk Management
Write-Host "Mounting VHD as a raw device..." -ForegroundColor Cyan
Mount-DiskImage -ImagePath $VhdPath -StorageType VHD
Write-Host "VHD is now ready." -ForegroundColor Green
Write-Host "ACTION REQUIRED:" -ForegroundColor Yellow
Write-Host "1. Open Ventoy2Disk.exe"
Write-Host "2. Go to 'Option' -> 'Show All Devices'"
Write-Host "3. Select the 1GB Microsoft Virtual Disk and click 'Install'."
# Requires Administrator privileges
$LogDir = "C:\ventoy\logs"
$SyncScript = "C:\ventoy\scripts\ventoy-vhd-sync.ps1"
$Today = (Get-Date).DayOfWeek
$Tomorrow = (Get-Date).AddDays(1).DayOfWeek
$LogFile = Join-Path $LogDir "sync_$Today.log"
$NextLog = Join-Path $LogDir "sync_$Tomorrow.log"
# 1. Execute Sync and Append for today
& $SyncScript *>> $LogFile
# 2. Pre-emptively clear tomorrow's log only if it has content
if (Test-Path $NextLog) {
if ((Get-Item $NextLog).Length -gt 0) {
Clear-Content $NextLog
Write-Host "Tomorrow's log ($Tomorrow) has been reset." -ForegroundColor Gray
}
}
# Requires Administrator privileges
$BaseDir = "C:\ventoy"
$SubDirs = "boot", "logs", "scripts"
foreach ($Dir in $SubDirs) {
$Path = Join-Path $BaseDir $Dir
if (!(Test-Path $Path)) {
New-Item -ItemType Directory -Path $Path -Force
Write-Host "Created: $Path" -ForegroundColor Green
} else {
Write-Host "Exists: $Path" -ForegroundColor Gray
}
}
Write-Host "Directory structure ready." -ForegroundColor Green

Ventoy Automation Tasks

This document covers the registration and removal of the synchronization and optional real-time watcher tasks.

1. Task Registration

Choose one of the following methods to register the tasks in an Administrator Command Prompt.

Tip

VentoyPeriodicSync is the primary requirement. VentoyRealTimeMonitor is an optional enhancement for instant updates upon file changes.

Method A: Direct CLI Creation

Use these commands to create the tasks with standard parameters.

:: 1. Core: VentoyPeriodicSync (Hourly)
schtasks /create /tn "VentoyPeriodicSync" /rl HIGHEST /sc HOURLY /mo 1 /tr "powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File C:\ventoy\scripts\log-ventoy-vhd-sync.ps1"

:: 2. Optional: VentoyRealTimeMonitor (At Logon)
schtasks /create /tn "VentoyRealTimeMonitor" /rl HIGHEST /it /sc ONLOGON /tr "powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File C:\ventoy\scripts\ventoy-watcher.ps1"

Method B: XML Import (Recommended)

Use these commands if you are using the provided XML templates (includes a 30-minute execution limit safety for the sync task).

:: 1. Core: Import the periodic sync task
schtasks /create /xml "C:\ventoy\scripts\VentoySync.xml" /tn "VentoyPeriodicSync" /f

:: 2. Optional: Import the real-time watcher task
schtasks /create /xml "C:\ventoy\scripts\VentoyWatcher.xml" /tn "VentoyRealTimeMonitor" /f

2. Verification

  1. Check Task Scheduler (taskschd.msc) to ensure the status for registered tasks is "Ready".
  2. Add an ISO to VTOYISO:\ISOs.
    • If using the Real-Time Monitor, a corresponding .vlnk.iso should appear immediately inside the mounted VHD.
    • Otherwise, the file will appear during the next hourly cycle.

3. Removal

To unregister the automation processes:

:: Remove the hourly synchronization task
schtasks /delete /tn "VentoyPeriodicSync" /f

:: Remove the real-time watcher task (if installed)
schtasks /delete /tn "VentoyRealTimeMonitor" /f

Links

# Requires Administrator privileges
$VhdPath = "C:\ventoy\ventoy.vhd"
# 1. Dismount the VHD
Write-Host "Dismounting VHD at $VhdPath..." -ForegroundColor Cyan
Dismount-DiskImage -ImagePath $VhdPath
# 2. Final Verification
if (Test-Path $VhdPath) {
$Size = (Get-Item $VhdPath).Length / 1MB
Write-Host "SUCCESS: Ventoy VHD is ready ($Size MB)." -ForegroundColor Green
Write-Host "You can now proceed to 'BOOT.md' to register this in your BCD." -ForegroundColor Yellow
} else {
Write-Error "VHD file not found at $VhdPath."
}
# --- Configuration ---
$StorageLabel = "VTOYISO"
$IsoSubDir = "ISOs"
$VhdPath = "C:\ventoy\ventoy.vhd"
$InternalPrPath = "ventoy\VentoyVlnk.exe" # Path inside the VHD
# --- Drive Discovery ---
$StorageVol = Get-Volume -FileSystemLabel $StorageLabel -ErrorAction SilentlyContinue
if (-not $StorageVol) { exit 0 }
$StorageRoot = "$($StorageVol.DriveLetter):\"
$IsoStorageDir = Join-Path $StorageRoot $IsoSubDir
$TempVlnkDir = Join-Path $StorageRoot "_vlnk_temp"
# --- Mount VHD to Access Programs ---
$VhdImage = Mount-DiskImage -ImagePath $VhdPath -PassThru
Start-Sleep -Seconds 2
# Find the Ventoy data partition (typically labeled "Ventoy")
$VhdDrive = ($VhdImage | Get-Volume | Where-Object { $_.FileSystemLabel -eq "Ventoy" -or $_.Size -gt 100MB }).DriveLetter | Select-Object -First 1
$VhdPathRoot = "$($VhdDrive):\"
$VentoyVlnkPath = Join-Path $VhdPathRoot $InternalPrPath
if (!(Test-Path $VentoyVlnkPath)) {
Write-Error "VentoyVlnk.exe not found inside VHD at $VentoyVlnkPath"
Dismount-DiskImage -ImagePath $VhdPath
exit 1
}
# --- Preparation ---
if (!(Test-Path $TempVlnkDir)) { New-Item -ItemType Directory -Path $TempVlnkDir | Out-Null }
Get-ChildItem $TempVlnkDir | Remove-Item -Force
# --- Generate Links using tool INSIDE VHD ---
Write-Host "Generating links using internal VHD tool..."
Get-ChildItem -Path $IsoStorageDir -Filter "*.iso" | ForEach-Object {
$OutFile = Join-Path $TempVlnkDir "$($_.BaseName).vlnk.iso"
# CLI syntax: -c for create
Start-Process -FilePath $VentoyVlnkPath -ArgumentList "-c `"$($_.FullName)`" `"$OutFile`"" -Wait -WindowStyle Hidden
}
# --- VHD Sync ---
Write-Host "Syncing vlnk files to VHD root..."
Get-ChildItem -Path $VhdPathRoot -Filter "*.vlnk.iso" | Remove-Item -Force
Move-Item -Path "$TempVlnkDir\*.vlnk.iso" -Destination $VhdPathRoot -Force
# --- Cleanup ---
Dismount-DiskImage -ImagePath $VhdPath
if (Test-Path $TempVlnkDir) { Remove-Item $TempVlnkDir -Recurse -Force }
Write-Host "Sync Complete." -ForegroundColor Green
# --- Configuration ---
$StorageLabel = "VTOYISO"
$IsoSubDir = "ISOs"
$SyncScript = "C:\ventoy\scripts\log-ventoy-vhd-sync.ps1"
# Wait for drive to be available
do {
$StorageVol = Get-Volume -FileSystemLabel $StorageLabel -ErrorAction SilentlyContinue
if (-not $StorageVol) { Start-Sleep -Seconds 10 }
} while (-not $StorageVol)
$WatchPath = Join-Path "$($StorageVol.DriveLetter):\" $IsoSubDir
$Watcher = New-Object System.IO.FileSystemWatcher
$Watcher.Path = $WatchPath
$Watcher.Filter = "*.iso"
$Watcher.EnableRaisingEvents = $true
$Action = {
Write-Host "Change detected in $WatchPath. Syncing..."
Start-Process powershell.exe -ArgumentList "-ExecutionPolicy Bypass -File $SyncScript" -WindowStyle Hidden
}
# Register events for creation and deletion
Register-ObjectEvent $Watcher "Created" -Action $Action
Register-ObjectEvent $Watcher "Deleted" -Action $Action
Write-Host "Monitoring $WatchPath for changes..."
while($true) { Start-Sleep 5 }
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com">
<RegistrationInfo>
<Description>Periodically synchronizes ISO vlnks to the Ventoy VHD.</Description>
<URI>\VentoySync</URI>
</RegistrationInfo>
<Triggers>
<CalendarTrigger>
<Repetition>
<Interval>PT1H</Interval>
<StopAtDurationEnd>false</StopAtDurationEnd>
</Repetition>
<StartBoundary>2024-01-01T00:00:00</StartBoundary>
<Enabled>true</Enabled>
<ScheduleByDay>
<DaysInterval>1</DaysInterval>
</ScheduleByDay>
</CalendarTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<LogonType>InteractiveToken</LogonType>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>true</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT30M</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>powershell.exe</Command>
<Arguments>-ExecutionPolicy Bypass -WindowStyle Hidden -File "C:\ventoy\scripts\log-ventoy-vhd-sync.ps1"</Arguments>
</Exec>
</Actions>
</Task>
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com">
<RegistrationInfo>
<Description>Starts the real-time FileSystemWatcher for the Ventoy ISO directory.</Description>
<URI>\VentoyWatcher</URI>
</RegistrationInfo>
<Triggers>
<LogonTrigger>
<Enabled>true</Enabled>
</LogonTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<LogonType>InteractiveToken</LogonType>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>true</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>powershell.exe</Command>
<Arguments>-ExecutionPolicy Bypass -WindowStyle Hidden -File "C:\ventoy\scripts\ventoy-watcher.ps1"</Arguments>
</Exec>
</Actions>
</Task>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment